Spring MVC + jpa框架搭建,及全面分析
一,hibernate与jpa的关系
首先明确一点jpa是什么?以前我就搞不清楚jpa和hibernate的关系。
1,JPA(Java Persistence API)是Sun官方提出的Java持久化规范。它为Java开发人员提供了一种对象/关系映射工具来管理Java应用中的关系数据。而Hibernate是它的一种实现。除了Hibernate,还有EclipseLink(曾经的toplink),OpenJPA等可供选择,所以使用Jpa的一个好处是,可以更换实现而不必改动太多代码。
2,Hibernate作为JPA的一种实现,jpa的注解已经是hibernate的核心,hibernate只提供了一些补充,而不是两套注解。hibernate对jpa的支持够足量,在使用hibernate注解建议使用jpa。
(上面两句话是砖家的回答,http://zhidao.baidu.com/link?url=Crp8dwDmn1BrSTabvPu409AmbJhfQ91mASpvWHvhXkfImPA4LZ1PZvp5iTqzQ3x3RzkT4CMfh6n8Yl8pAgb_WK)
由此可见,是hibernate实现了jpa。而hibernate实现jpa的结果是怎样呢?
我的理解是有两点:1充分体现orm思想。2jpa的注解非常方便。
总结下,JPA和Hibernate之间的关系。可以简单的理解为JPA是标准接口,Hibernate是实现。那么Hibernate是如何实现与 JPA 的这种关系的呢。
Hibernate主要是通过三个组件来实现的,及hibernate-annotation、hibernate-entitymanager和hibernate-core。
由此又可见,hibernate还可以不去实现jpa。那是怎样的情景呢?如果你单独学习hibernate便会体验到了。
在hibernate中,通常配置对象关系映射关系有两种,一种是基于xml的方式,另一种是基于annotation的注解方式。
前者不赘述,而后者基于annotation的注解方式与jpa联系紧密,因为它包含jpa的标准,并添加少量hibernate独有的。
而在jpa出现之前,hibernate配置对象关联映射只有基于xml的方式。
我是最不喜欢写那种任何实体-数据库映射信息都写在配置文件xml里面的那种项目。所以使用注解是我心目中先进有力的方式。
所以,我选择spring+jpa!
二,Spring MVC + jpa框架搭建思路分析
spring mvc也就是一个三层架构而已,最底下是:持久层。中间是:控制层。上层是:页面。
为啥要分三层呢?因为最上层不需要与最底层联通。按照软件开发术语叫解耦。
所以,搭建spring mvc就是先搞,上层和中间,再搞中间和下层。
三,上层和中间
配置文件spring-mvc.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation= "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd"> <!-- 这个标签注册了Spring MVC分发请求到控制器所必须的DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter实例 --> <mvc:annotation-driven/> <context:component-scan base-package="com.controller"/> <!-- 定义视图解析器viewResolver --> <bean id = "viewResolver" class = "org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name = "prefix" value = "/WEB-INF/jsp/"/> <property name = "suffix" value = ".jsp"/> </bean> </beans>
这个文件其实就是定义了对前端请求的处理,以及处理结果怎样给到前端。也就是处理,上层-中间层间的关系。
具体讲就是,spring先拦截了前端的请求,这个拦截配置在web.xml里面。不赘述。
前端的请求要找到对应的controller类。这时spring要通过注解。<context:component-scan base-package="com.controller"/>这一句,表明要自动扫描,com.controller包。
package com.controller; import java.util.List; import javax.annotation.Resource; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import com.entity.User; import com.service.UserService; @Controller @RequestMapping("/path01/path02") public class HelloController { @Resource(name = "userServiceImpl") private UserService userService; @RequestMapping("/hello.form") public String execute(Model model) throws Exception{ List<User> users = userService.findAllUser(); model.addAttribute("users",users); return "hello"; } }
有了@Controller这个注解就会被扫描到。
<!-- 定义视图解析器viewResolver -->
<bean id = "viewResolver"
class = "org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name = "prefix" value = "/WEB-INF/jsp/"/>
<property name = "suffix" value = ".jsp"/>
</bean>
这句定义怎样将处理结果返回。返回到/WEB-INF/jsp/下面的jsp页面。
<%@page pageEncoding="utf-8" contentType="text/html;charset=utf-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!DOCTYPE html> <html> <head> <title>Spring Hello World!</title> </head> <body> hello Spring MVC,日向 2016-03 <c:forEach items="${users}" var="user"> id为${user.id}||用户名为${user.username} </c:forEach> </body> </html>
关键是,<mvc:annotation-driven/>这一句。
<mvc:annotation-driven /> 是一种简写形式,完全可以手动配置替代这种简写形式,简写形式可以让初学都快速应用默认配置方案。
<mvc:annotation-driven /> 会自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter 两个bean,是spring MVC为@Controllers分发请求所必须的。
(from:http://kingliu.iteye.com/blog/1972973)
所以<mvc:annotation-driven />的本质其实是DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter 两个bean。
网上可以借鉴的回答:
http://kingliu.iteye.com/blog/1972973
http://zhidao.baidu.com/link?url=CvJjOeWrEtf8UXaiowxsDk2rDYHPm7h7GjKMb25Fdu6fWGEsoB3II4gORHOL6U_M3V7JLVfYfQVmDZhjzyIj82O8Vopjghl7Nv_-h9noYoS
四,中间和下层
spring-common.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd"> <!-- 配置数据源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" > <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost/db_shop"></property> <property name="username" value="root"></property> <property name="password" value="1234"></property> </bean> <!-- LocalContainerEntityManagerFactoryBean spring配置jpa的三种方式之一,适用于所有环境的FactoryBean ,能全面控制EntityManagerFactory配置 --> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource"/> <!-- 设定为自动扫描,spring新特性,有了packagesToScan,我们不再需要自己动手去实现实体类的扫描了 --> <property name="packagesToScan" value="com.entity"/> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="showSql" value="true"/> <!-- generateDdl= true表示自动生成DDL--> <property name="generateDdl" value="true" /> <property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect"/> </bean> </property> </bean> <!-- 定义事务管理器 --> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <!-- 注解扫描 --> <context:component-scan base-package="com" /> <!--对@Transactional这个注解进行的驱动,这是基于注解的方式使用事务配置声明,这样在具体应用中可以指定对哪些方法使用事务。 --> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
dataSource作用:
public interface DataSource
该工厂用于提供到此 DataSource 对象表示的物理数据源的连接。作为 DriverManager 设施的替代项,DataSource 对象是获取连接的首选方法。实现 DataSource 接口的对象通常在基于 JavaTM Naming and Directory Interface (JNDI) API 的命名服务中注册。 DataSource 接口由驱动程序供应商实现。共有三种类型的实现: 基本实现 - 生成标准 Connection 对象
连接池实现 - 生成自动参与连接池的 Connection 对象。此实现与中间层连接池管理器一起使用。
分布式事务实现 - 生成一个 Connection 对象,该对象可用于分布式事务,并且几乎始终参与连接池。此实现与中间层事务管理器一起使用,并且几乎始终与连接池管理器一起使用。
DataSource 对象的属性在需要时可以修改。例如,如果将数据源移动到另一个服务器,则可更改与服务器相关的属性。其优点是,因为可以更改数据源的属性,所以任何访问该数据源的代码都无需更改。 通过 DataSource 对象访问的驱动程序不会向 DriverManager 注册。通过查找操作检索 DataSource 对象,然后使用该对象创建 Connection 对象。使用基本的实现,通过 DataSource 对象获取的连接与通过 DriverManager 设施获取的连接相同。
entityManagerFactory:
配置entityManagerFactory说明这是配置的jpa。因为我记得hibernate是配置sessionFactory的。
entityManagerFactory,顾名思义,就是managerFactory的工厂。我们使用的类是LocalContainerEntityManagerFactoryBean。这表明是本地容器托管的,也就决定了是使用注解@PersistenceContext来获取EntityManager对象的。
entityManagerFactory下面的一些具体配置就不详解了,代码中大致注释了。
transactionManager:
设置jpa事务管理器。事务就是对一系列的数据库操作进行统一的提交或回滚操作。这样可以防止在一些意外(例如说突然断电)的情况下出现乱数据,防止数据库数据出现问题。
五,其它一些代码
User:
package com.entity; import javax.persistence.Entity; import javax.persistence.Id; @Entity public class User { private int id; private String username; @Id public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } }
UserDao:
package com.dao; import java.util.List; import com.entity.User; public interface UserDao { public List<User> findAllUsers(); }
UserDaoImpl:
package com.daoimpl; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.FlushModeType; import javax.persistence.PersistenceContext; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import com.dao.UserDao; import com.entity.User; @Transactional @Repository("userDaoImpl") public class UserDaoImpl implements UserDao{ @PersistenceContext protected EntityManager entityManager; @Override public List<User> findAllUsers() { CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); CriteriaQuery<User> criteriaQuery = criteriaBuilder.createQuery(User.class); Root<User> root = criteriaQuery.from(User.class); criteriaQuery.select(root); Predicate restrictions = criteriaBuilder.conjunction(); criteriaQuery.where(restrictions); return entityManager.createQuery(criteriaQuery).setFlushMode(FlushModeType.COMMIT).getResultList(); } }
这段代码是实际的持久化操作的代码。通过@PersistenceContext注解获取,EntityManager,进行持久化操作。@Transactional表示将事务处理托付给spring。
这段代码实现的功能就是从数据库中查询所有的User。
UserService:
package com.service; import java.util.List; import com.entity.User; public interface UserService { public List<User> findAllUser(); }
UserServiceImpl:
package com.serviceimpl; import java.util.List; import javax.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.dao.UserDao; import com.entity.User; import com.service.UserService; @Transactional @Service("userServiceImpl") public class UserServiceImpl implements UserService{ @Resource(name = "userDaoImpl") private UserDao userDao; @Override public List<User> findAllUser() { return userDao.findAllUsers(); } }
web.xml:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <display-name></display-name> <!-- 加载所有的配置文件 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:/spring-*.xml</param-value> </context-param> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:/spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>*.form</url-pattern> </servlet-mapping> <!-- 配置Spring监听 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
六,运行
简简单单,毫无一点装饰的一个网页,但在服务器里进行了一系列的代码逻辑……
总体上,这个效果就是:用户输入这个地址,找到我们的服务器地址,localhost8080,我们的项目,RiXiangShop,然后通过spring解析路径,通过@Controller标签找到对应的controller,通过标签注入找到对应的service,再找到对应的dao,然后进行数据库持久化操作--查询所有用户,以List的数据结构返回,返回到service,返回到controller,response给前端jsp页面,jsp通过jstl将查询到的用户的id和名字打印到屏幕上。
所以说,这个框架是个雏形,你加入更多的逻辑,它就可以演化为一个电子商务网站,一个博客。
你加入更多更多逻辑,它也可以演化为阿尔法狗。
我希望将之演化为一个博客,再实践开发中搞更多技术。
因为这是第一个我完全独立自主搭建,并理解的框架。
我将用两个月时间开发。到六月,期待开发成功。
之后希望可以搞个自己的域名,建一个自己的个人网站。
然后,在维护开发中继续搞更多技术。