使用 Spring Data JPA 简化 JPA 开发

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

package com.stone.jpa.persistence;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity 
@Table(name = "t_userinfo") 
public class UserInfo {

	private Long userId; 
	private String username;
	private String password;
	
	public void setUserId(Long userId) {
		this.userId = userId;
	}
	
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	public Long getUserId() {
		return this.userId;
	}
	
	public void setUsername(String username) {
		this.username = username;
	}
	
	public String getUsername() {
		return this.username;
	}
	
	public void setPassword(String password) {
		this.password = password;
	}
	
	public String getPassword() {
		return this.password;
	}
	
    @Override
	public String toString() {
		return "UserInfo{" +
				"userid=" + userId +
				", username='" + username + '\'' +
				'}';
	}
	
}

 

清单 2. 实体类 AccountInfo.java

package com.stone.jpa.persistence;

import java.io.Serializable;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;

@Entity 
@Table(name = "t_accountinfo") 
public class AccountInfo implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private Long accountId; 
	private Integer balance; 
	private UserInfo userInfo;
	
	public void setAccountId(Long accountId) {
		this.accountId = accountId;
	}
	
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	public Long getAccountId() {
		return this.accountId;
	}
	
	public void setBalance(Integer balance) {
		this.balance = balance;
	}
	
	public Integer getBalance() {
		return this.balance;
	}
	
	public void setUserInfo(UserInfo userInfo) {
		this.userInfo = userInfo;
	}
	
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "userinfo_id")
	public UserInfo getUserInfo() {
		return this.userInfo;
	}
    
    @Override
    public String toString() {
        return "AccountInfo{" +
                "accountId=" + accountId +
                ", userInfo=" + userInfo +
                ", balance=" + balance +
                '}';
    }
	
}

 

清单 3. DAO接口

package com.stone.jpa.persistence;

public interface UserDao {

	public AccountInfo save(AccountInfo accountInfo); 
}

 

清单 4. DAO实现

package com.stone.jpa.persistence;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;

@Repository
public class UserDaoImpl implements UserDao {

	@PersistenceContext 
	private EntityManager em; 
	
	@Override
	public AccountInfo save(AccountInfo accountInfo) {
		// TODO Auto-generated method stub
		em.persist(accountInfo);
		return accountInfo; 
	}

}

 

2、业务层
清单 1. 业务层接口

package com.stone.jpa.business;

import com.stone.jpa.persistence.AccountInfo;

public interface UserService {

	public AccountInfo createNewAccount(String username, String password, Integer initBalance); 
}

 

清单 2. 业务层实现

package com.stone.jpa.business;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.stone.jpa.persistence.AccountInfo;
import com.stone.jpa.persistence.UserDao;
import com.stone.jpa.persistence.UserInfo;

@Service
public class UserServiceImpl implements UserService {

	@Autowired 
	private UserDao userDao;
	
	@Transactional
	public AccountInfo createNewAccount(String username, String password, Integer initBalance) {
		// TODO Auto-generated method stub
		// 封装域对象
		AccountInfo accountInfo = new AccountInfo(); 
		UserInfo userInfo = new UserInfo(); 
		userInfo.setUsername(username); 
		userInfo.setPassword(password); 
		accountInfo.setBalance(initBalance); 
		accountInfo.setUserInfo(userInfo); 
		// 调用持久层,完成数据的保存
		return userDao.save(accountInfo); 
	}
	
}

 

3、配置文件
清单1  数据源配置

package com.stone.jpa.config;

import javax.persistence.EntityManagerFactory;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
//开启事务管理,proxyTargetClass= true代表开启类的事务管理
@EnableTransactionManagement(proxyTargetClass = true)
public class DataSourceConfig {
	
	@Bean
	public LocalEntityManagerFactoryBean entityManagerFactoryBean() {
		LocalEntityManagerFactoryBean embf = new LocalEntityManagerFactoryBean();
		embf.setPersistenceUnitName("SimplePU");
		return embf;
	}
	
    @Bean  
    public JpaTransactionManager transactionManager(EntityManagerFactory emf) {  
        JpaTransactionManager manager = new JpaTransactionManager();   
        manager.setEntityManagerFactory(emf);
        return manager;  
    } 
	
}

 


清单2  Spring自动组装配置

package com.stone.jpa.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration 
@ComponentScan(basePackages={"com.stone.jpa"})
public class SpringConfig {

}

 

清单3  JPA配置文件persistence.xml
路径: /src/META-INF/persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0"> 
  <!-- persistenceUnitName:用于指定持久化单元名字 -->
  <persistence-unit name="SimplePU" transaction-type="RESOURCE_LOCAL"> 
    <!-- persistenceProvider:用于指定持久化实现厂商类 -->
    <provider>org.hibernate.ejb.HibernatePersistence</provider> 
    <class>com.stone.jpa.persistence.UserInfo</class> 
    <class>com.stone.jpa.persistence.AccountInfo</class> 
    <properties> 
      <property name="hibernate.connection.driver_class"
         value="com.mysql.jdbc.Driver"/> 
      <property name="hibernate.connection.url"
         value="jdbc:mysql://localhost:3306/spring?characterEncoding=UTF-8"/> 
      <property name="hibernate.connection.username" value="root"/> 
      <property name="hibernate.connection.password" value="root"/> 
      <property name="hibernate.dialect"
         value="org.hibernate.dialect.MySQL5Dialect"/> 
      <property name="hibernate.show_sql" value="true"/> 
      <property name="hibernate.format_sql" value="false"/> 
      <property name="hibernate.use_sql_comments" value="false"/> 
      <property name="hibernate.hbm2ddl.auto" value="create"/> 
      <property name="javax.persistence.validation.mode" value="none"/>
    </properties> 
  </persistence-unit> 
</persistence>

该文件必须位于类路径下的META-INF目录下,也就是在src下新建一个META-INF目录,然后把persistence.xml放进去。

 

4、测试文件

清单1  SimpleSpringJpaDemo.java

package com.stone.jpa.business.test;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.stone.jpa.business.UserService;
import com.stone.jpa.config.SpringConfig;

public class SimpleSpringJpaDemo {
	
	public static void main(String[] args) {        	 
	// 通过Java配置来实例化Spring容器
        AnnotationConfigApplicationContext context 

             = new AnnotationConfigApplicationContext(SpringConfig.class);

        // 在Spring容器中获取Bean对象
        UserService userService = context.getBean(UserService.class);
        userService.createNewAccount("ZhangJianPing", "123456", 2500); 
        
        // 销毁该容器
        context.destroy();
	} 
}

运行SimpleSpringJpaDemo,结果如下:

使用 Spring Data JPA 简化 JPA 开发 - 叶主任 - 叶主任的博客

 

使用 Spring Data JPA 简化 JPA 开发 - 叶主任 - 叶主任的博客

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

更进一步:Spring Data JPA 让一切近乎完美
       通过前面的分析可以看出,Spring 对 JPA 的支持已经非常强大,开发者只需关心核心业务逻辑的实现代码,无需过多关注 EntityManager 的创建、事务处理等 JPA 相关的处理,这基本上也是作为一个开发框架而言所能做到的极限了。然而,Spring 开发小组并没有止步,他们再接再厉,于最近推出了 Spring Data JPA 框架,主要针对的就是 Spring 唯一没有简化到的业务逻辑代码,至此,开发者连仅剩的实现持久层业务逻辑的工作都省了,唯一要做的,就只是声明持久层的接口,其他都交给 Spring Data JPA 来帮你完成!
       至此,读者可能会存在一个疑问,框架怎么可能代替开发者实现业务逻辑呢?毕竟,每一个应用的持久层业务甚至领域对象都不尽相同,框架是怎么做到的呢?其实这背后的思想并不复杂,比如,当你看到 UserDao.findUserById() 这样一个方法声明,大致应该能判断出这是根据给定条件的 ID 查询出满足条件的 User 对象。Spring Data JPA 做的便是规范方法的名字,根据符合规范的名字来确定方法需要实现什么样的逻辑。
       接下来我们针对前面的例子进行改造,让 Spring Data JPA 来帮助我们完成业务逻辑。首先下载spring data jpa的两个jar包:
spring-data-commons:

<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-commons -->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-commons</artifactId>
    <version>1.13.12.RELEASE</version>
</dependency>

spring-data-jpa:

<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-jpa -->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jpa</artifactId>
    <version>1.11.12.RELEASE</version>
</dependency>

注意,spring-data-jpa 2.0以上版本要求jdk版本至少为1.8,请根据jdk版本下载。Commons 是 Spring Data 的公共基础包。
1、持久层接口UserDao修改
清单 UserDao.java

package com.stone.jpa.persistence;

import org.springframework.data.repository.Repository;

public interface UserDao extends Repository<AccountInfo, Long> {

	public AccountInfo save(AccountInfo accountInfo); 
}

并删除UserDao的实现UserDaoImpl 类,因为我们前面说过,框架会为我们完成业务逻辑。

 

2、修改数据源配置文件

清单 DataSourceConfig.java

package com.stone.jpa.config;

import javax.persistence.EntityManagerFactory;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableJpaRepositories(basePackages="com.stone.jpa.persistence", entityManagerFactoryRef="entityManagerFactoryBean")
//开启事务管理,proxyTargetClass= true代表开启类的事务管理
@EnableTransactionManagement(proxyTargetClass = true)
public class DataSourceConfig {
    
      @Bean
      public LocalEntityManagerFactoryBean entityManagerFactoryBean() {
            LocalEntityManagerFactoryBean embf = new LocalEntityManagerFactoryBean();
            embf.setPersistenceUnitName("SimplePU");
            return embf;
      }
    
      @Bean  
      public JpaTransactionManager transactionManager(EntityManagerFactory emf) {  
            JpaTransactionManager manager = new JpaTransactionManager();   
            manager.setEntityManagerFactory(emf);
            return manager;  
      } 
    
}

@EnableJpaRepositories 注解用于Srping JPA的代码配置,用于取代xml形式的配置文件<jpa:repositories>,其中basePackage属性用于配置扫描Repositories所在的package及子package,transactionManagerRef属性是事务管理工厂引用名称,对应到@Bean注解对应的方法。

        其他的都不需改动,运行SimpleSpringJpaDemo,数据已添加成功!

        如果要再增加新的持久层业务,比如希望查询出给 ID 的 AccountInfo 对象,该怎么办呢?很简单,在 UserDao 接口中增加一行代码即可:

package com.stone.jpa.persistence;

import org.springframework.data.repository.Repository;

public interface UserDao extends Repository<AccountInfo, Long> {

	public AccountInfo save(AccountInfo accountInfo); 
	public AccountInfo findByAccountId(Long accountId); 
}

 

service也相应改动:

清单1 UserService.java

package com.stone.jpa.business;

import com.stone.jpa.persistence.AccountInfo;

public interface UserService {

	public AccountInfo createNewAccount(String username, String password, Integer initBalance); 
	public AccountInfo findAccountInfoById(Long id);
}

 

清单2 UserServiceImpl.java

package com.stone.jpa.business;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.stone.jpa.persistence.AccountInfo;
import com.stone.jpa.persistence.UserDao;
import com.stone.jpa.persistence.UserInfo;

@Service
@Transactional
public class UserServiceImpl implements UserService {

	@Autowired 
	private UserDao userDao;
	
	public AccountInfo createNewAccount(String username, String password, Integer initBalance) {
		// TODO Auto-generated method stub
		// 封装域对象
		AccountInfo accountInfo = new AccountInfo(); 
		UserInfo userInfo = new UserInfo(); 
		userInfo.setUsername(username); 
		userInfo.setPassword(password); 
		accountInfo.setBalance(initBalance); 
		accountInfo.setUserInfo(userInfo); 
		// 调用持久层,完成数据的保存
		return userDao.save(accountInfo); 
	}

	@Override
	public AccountInfo findAccountInfoById(Long id) {
		// TODO Auto-generated method stub
		return userDao.findByAccountId(id);
	}
	
}

运行结果:

Hibernate: insert into t_userinfo (password, username) values (?, ?)
Hibernate: insert into t_accountinfo (balance, userinfo_id) values (?, ?)
Hibernate: select accountinf0_.accountId as accountId1_, accountinf0_.balance as balance1_, accountinf0_.userinfo_id as userinfo3_1_ from t_accountinfo accountinf0_ where accountinf0_.accountId=? limit ?
Hibernate: select userinfo0_.userId as userId0_0_, userinfo0_.password as password0_0_, userinfo0_.username as username0_0_ from t_userinfo userinfo0_ where userinfo0_.userId=?
AccountInfo{accountId=1, userInfo=UserInfo{userid=1, username='ZhangJianPing'}, balance=4500}

      下面总结一下使用 Spring Data JPA 进行持久层开发大致需要的三个步骤:
      (1)声明持久层的接口,该接口继承 Repository,Repository 是一个标记型接口,它不包含任何方法,当然如果有需要,Spring Data 也提供了若干 Repository 子接口,其中定义了一些常用的增删改查,以及分页相关的方法。
      (2)在接口中声明需要的业务方法。Spring Data 将根据给定的策略(具体策略稍后讲解)来为其生成实现代码。
      (3)在 Spring 配置文件中增加一行声明,让 Spring 为声明的接口创建代理对象。配置了 <jpa:repositories> 后,Spring 初始化容器时将会扫描 base-package 指定的包目录及其子目录,为继承 Repository 或其子接口的接口创建代理对象,并将代理对象注册为 Spring Bean,业务层便可以通过 Spring 自动封装的特性来直接使用该对象。

参考使用 Spring Data JPA 简化 JPA 开发

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值