从简单的JPA到Spring Data JPA

来源:互联网 发布:办公网络解决方案 编辑:程序博客网 时间:2024/05/22 04:48

本文代码是基于 Hibernate EntityManager,读者几乎不用修改任何代码,便可以非常容易地切换到其他 JPA 框架,因为代码中使用到的都是 JPA 规范提供的接口 / 类,并没有使用到框架本身的私有特性。示例主要涉及七个文件,但是很清晰:业务层包含一个接口和一个实现;持久层包含一个接口、一个实现、一个实体类;另外加上一个 JPA 配置文件和一个测试类。相关类 / 接口代码如下:

pom.xml

<properties><!-- hibernate 版本号 --><hibernate.version>5.1.0.Final</hibernate.version></properties><dependencies><!-- hibernate --><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-core</artifactId><version>${hibernate.version}</version></dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-entitymanager</artifactId><version>${hibernate.version}</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.9</version></dependency></dependencies>
dao层代码如下:

public interface UserDao {UserInfo save(UserInfo user);}public class UserDaoImpl implements UserDao {@Overridepublic UserInfo save(UserInfo user) {EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("SimplePU");EntityManager entityManager = entityManagerFactory.createEntityManager();entityManager.getTransaction().begin();entityManager.persist(user);entityManager.getTransaction().commit();entityManagerFactory.close();return user;}}
service层代码如下:

public interface UserService {public UserInfo createNewAccountInfo(String user, String pwd,Integer init);}public class UserServiceImpl implements UserService {private UserDao userDao = new UserDaoImpl();@Overridepublic UserInfo createNewAccountInfo(String name, String pwd,Integer init) {UserInfo user = new UserInfo();user.setUsername(name);user.setPassword(pwd);user.setBalance(init);return userDao.save(user);}}
persistence.xml

<?xml version="1.0" encoding="UTF-8"?><persistence xmlns="http://java.sun.com/xml/ns/persistence"version="2.0"><persistence-unit name="SimplePU" transaction-type="RESOURCE_LOCAL"><provider>org.hibernate.ejb.HibernatePersistence</provider><class>com。hsb.hibernate.entity.UserInfo</class><class>com。hsb.hibernate.entity.AccountInfo</class><properties><property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" /><property name="hibernate.connection.url"value="jdbc:mysql://127.0.0.1:3306/hibernatepractice?createDatabaseIfNotExist=true" /><property name="hibernate.connection.username" value="root" /><property name="hibernate.connection.password" value="" /><property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" /><property name="hibernate.show_sql" value="true" /><property name="hibernate.format_sql" value="true" /><property name="hibernate.use_sql_comments" value="false" /><property name="hibernate.hbm2ddl.auto" value="update" /></properties></persistence-unit></persistence>
单元测试代码如下:

public class UserServiceImplTests {private UserServiceImpl userService = new UserServiceImpl();@Testpublic void test() {userService.createNewAccountInfo("yunshixin", "mima", 1);}}


Spring框架对JPA简单的支持

引入Spring后通过注解完成对象的依赖注入,还可以通过Spring的声明式事务简化持久化操作:

pom.xml

<properties><!-- hibernate 版本号 --><hibernate.version>5.1.0.Final</hibernate.version><!-- spring版本号 --><spring.version>4.2.5.RELEASE</spring.version></properties><dependencies><!-- spring核心包 --><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-orm</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-orm</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-jpa</artifactId><version>1.10.1.RELEASE</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.5.0</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.5.0</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-annotations</artifactId><version>2.5.0</version></dependency><!-- hibernate --><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-core</artifactId><version>${hibernate.version}</version></dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-entitymanager</artifactId><version>${hibernate.version}</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.9</version></dependency></dependencies>
dao层代码如下:

public interface UserDao {UserInfo save(UserInfo user);}@Repositorypublic class UserDaoImpl implements UserDao {@PersistenceContextprivate EntityManager entityManager;@Transactional@Overridepublic UserInfo save(UserInfo user) {entityManager.persist(user);return user;}}
service层代码如下:

public interface UserService {public UserInfo createNewAccountInfo(String user, String pwd,Integer init);}@Servicepublic class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;@Override@Transactionalpublic UserInfo createNewAccountInfo(String name, String pwd,Integer init) {UserInfo user = new UserInfo();user.setUsername(name);user.setPassword(pwd);user.setBalance(init);return userDao.save(user);}}
persistence.xml配置无变化,增加了applicationContext.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:aop="http://www.springframework.org/schema/aop" xmlns:p="http://www.springframework.org/schema/p"xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:jpa="http://www.springframework.org/schema/data/jpa"xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans-4.2.xsd   http://www.springframework.org/schema/context   http://www.springframework.org/schema/context/spring-context-4.2.xsd   http://www.springframework.org/schema/tx   http://www.springframework.org/schema/tx/spring-tx-4.2.xsd   http://www.springframework.org/schema/aop    http://www.springframework.org/schema/aop/spring-aop-4.2.xsd   http://www.springframework.org/schema/mvc    http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd   http://www.springframework.org/schema/data/jpa    http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"><!-- 开启IOC注解扫描 --><context:component-scan base-package="com.hsb.hibernate" /><!-- 启用 annotation事务 --><tx:annotation-driven transaction-manager="transactionManager" /><!-- 配置事务管理器 --><bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"><property name="entityManagerFactory" ref="entityManagerFactory" /></bean><bean id="entityManagerFactory"class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"></bean></beans>
单元测试如下:

@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("classpath*:**/applicationContext.xml")public class UserServiceImplTests {@Autowiredprivate UserService userService;@Testpublic void test() {userService.createNewAccountInfo("yunshixin", "mima", 1);}}

通过对比重构前后的代码,可以发现 Spring 对 JPA 的简化已经非常出色了,我们可以大致总结一下 Spring 框架对 JPA 提供的支持主要体现在如下几个方面:
    首先,它使得 JPA 配置变得更加灵活。JPA 规范要求,配置文件必须命名为 persistence.xml,并存在于类路径下的 META-INF 目录中。该文件通常包含了初始化 JPA 引擎所需的全部信息。Spring 提供的 LocalContainerEntityManagerFactoryBean 提供了非常灵活的配置,persistence.xml 中的信息都可以在此以属性注入的方式提供。
    其次,Spring 实现了部分在 EJB 容器环境下才具有的功能,比如对 @PersistenceContext、@PersistenceUnit 的容器注入支持。
    第三,也是最具意义的,Spring 将 EntityManager 的创建与销毁、事务管理等代码抽取出来,并由其统一管理,开发者不需要关心这些,如前面的代码所示,业务方法中只剩下操作领域对象的代码,事务管理和 EntityManager 创建、销毁的代码都不再需要开发者关心了。


Spring Data JPA的进一步支持

①让dao层集成Repository接口,该接口使用了泛型,需要为其提供两个类型:第一个为该接口处理的域对象类型,第二个为该域对象的主键类型。修改后的dao层代码 如下:

public interface UserDao extends Repository<UserInfo, Integer> {UserInfo save(UserInfo user);}
②删除dao层的实现类。Spring Data JPA会根据定义的方法名自动完成业务逻辑

③在spring的配置文件中启用扫描并自动创建代理,springContext.xml增加配置如下:

<pre class="displaycode" style="margin-top: 0px; border: 1px solid rgb(204, 204, 204); outline: 0px; font-size: 11px; vertical-align: baseline; width: 780px; font-family: 'Andale Mono', 'Lucida Console', Monaco, Liberation, fixed, monospace; overflow: auto; clear: right; margin-bottom: 6px !important; padding: 5px 10px 5px 3px !important; background: rgb(247, 247, 247) !important;"><-- 需要在 <beans> 标签中增加对 jpa 命名空间的引用 --> 

<jpa:repositories base-package="com.hsb.hibernate"entity-manager-factory-ref="entityManagerFactory"transaction-manager-ref="transactionManager" />
其他部分不用改变,仍使用以前的单元测试,往数据库中添加数据。成功。


总结使用SpringDataJPA进行dao层的开发需要的几个步骤如下:

一、声明一个继承了Repository接口的接口

二、在声明的接口中定义业务需要的一些方法

三、在 Spring 配置文件中增加配置,让 Spring 容器为声明的接口创建代理对象。添加声明配置后,Spring 初始化容器时将会扫描 base-package 指定的包目录及其子目录,为继承 Repository 或其子接口的接口创建代理对象,并将代理对象注册为 Spring Bean,业务层便可以通过 Spring 自动封装的特性来直接使用该对象。


除了直接继承Repository接口外,还有另一种方式可以完成同样的工作,两种方式如下:

public interface UserDao extends Repository<UserInfo, Long> { …… }  @RepositoryDefinition(domainClass = UserInfo.class, idClass = Long.class)  public interface UserDao { …… }


通过解析方法名创建查询
通过前面的例子,读者基本上对解析方法名创建查询的方式有了一个大致的了解,这也是 Spring Data JPA 吸引开发者的一个很重要的因素。框架在进行方法名解析时,会先把方法名多余的前缀截取掉,比如 find、findBy、read、readBy、get、getBy,然后对剩下部分进行解析。并且如果方法的最后一个参数是 Sort 或者 Pageable 类型,也会提取相关的信息,以便按规则进行排序或者分页查询。
在创建查询时,我们通过在方法名中使用属性名称来表达,比如 findByUserAddressZip ()。框架在解析该方法时,首先剔除 findBy,然后对剩下的属性进行解析,详细规则如下(此处假设该方法针对的域对象为 UserInfo 类型):
先判断 userAddressZip (根据 POJO 规范,首字母变为小写,下同)是否为UserInfo 的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,继续第二步;
从右往左截取第一个大写字母开头的字符串(此处为 Zip),然后检查剩下的字符串是否为 UserInfo 的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,则重复第二步,继续从右往左截取;最后假设 user 为 UserInfo 的一个属性;
接着处理剩下部分( AddressZip ),先判断 user 所对应的类型是否有 addressZip 属性,如果有,则表示该方法最终是根据 "AccountInfo.user.addressZip" 的取值进行查询;否则继续按照步骤 2 的规则从右往左截取,最终表示根据 "AccountInfo.user.address.zip" 的值进行查询。
可能会存在一种特殊情况,比如 AccountInfo 包含一个 user 的属性,也有一个 userAddress 属性,此时会存在混淆。读者可以明确在属性之间加上 "_" 以显式表达意图,比如 "findByUser_AddressZip()" 或者 "findByUserAddress_Zip()"。
在查询时,通常需要同时根据多个属性进行查询,且查询的条件也格式各样(大于某个值、在某个范围等等),Spring Data JPA 为此提供了一些表达条件查询的关键字,大致如下:
And --- 等价于 SQL 中的 and 关键字,比如 findByUsernameAndPassword(String user, Striang pwd);
Or --- 等价于 SQL 中的 or 关键字,比如 findByUsernameOrAddress(String user, String addr);
Between --- 等价于 SQL 中的 between 关键字,比如 findBySalaryBetween(int max, int min);
LessThan --- 等价于 SQL 中的 "<",比如 findBySalaryLessThan(int max);
GreaterThan --- 等价于 SQL 中的">",比如 findBySalaryGreaterThan(int min);
IsNull --- 等价于 SQL 中的 "is null",比如 findByUsernameIsNull();
IsNotNull --- 等价于 SQL 中的 "is not null",比如 findByUsernameIsNotNull();
NotNull --- 与 IsNotNull 等价;
Like --- 等价于 SQL 中的 "like",比如 findByUsernameLike(String user);
NotLike --- 等价于 SQL 中的 "not like",比如 findByUsernameNotLike(String user);
OrderBy --- 等价于 SQL 中的 "order by",比如 findByUsernameOrderBySalaryAsc(String user);
Not --- 等价于 SQL 中的 "! =",比如 findByUsernameNot(String user);
In --- 等价于 SQL 中的 "in",比如 findByUsernameIn(Collection<String> userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数;
NotIn --- 等价于 SQL 中的 "not in",比如 findByUsernameNotIn(Collection<String> userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数;




参考:http://www.ibm.com/developerworks/cn/opensource/os-cn-spring-jpa/

0 0
原创粉丝点击