从简单的JPA到Spring Data JPA

本文代码是基于 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 {
	@Override
	public 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();
	@Override
	public 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();
	@Test
	public 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);
}

@Repository
public class UserDaoImpl implements UserDao {
	@PersistenceContext
	private EntityManager entityManager;

	@Transactional
	@Override
	public UserInfo save(UserInfo user) {
		entityManager.persist(user);
		return user;
	}
}
service层代码如下:

public interface UserService {
	public UserInfo createNewAccountInfo(String user, String pwd,
			Integer init);
}

@Service
public class UserServiceImpl implements UserService {
	@Autowired
	private UserDao userDao;

	@Override
	@Transactional
	public 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 {
	@Autowired
	private UserService userService;

	@Test
	public 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值