(转) Spring Data 系列之JPA

转自:https://my.oschina.net/ztlyde/blog/68513

引入:

Spring Data是SpringSource基金会下的一个用于简化数据库访问,并支持云服务的开源框架。其主要目标是使得数据库的访问变得方便快捷,并支持map-reduce框架和云计算数据服务。对于拥有海量数据的项目,可以用Spring Data来简化项目的开发。

然而针对不同的数据储存访问使用相对的类库来操作访问。Spring Data中已经为我们提供了很多业务中常用的一些接口和实现类来帮我们快速构建项目,比如分页、排序、DAO一些常用的操作。

今天主要是对Spring Data下的JPA模块进行讲解。

为什么说Spring Data能帮助我们快速构建项目呢,因为Spring Data已经在数据库访问层上帮我们实现了公用功能了,而我们只需写一个接口去继承Spring Data提供给我们接口,便可实现对数据库的访问及操作,类似于spring-orm的TemplateDAO。

----------------------------------------------邪恶的分割-------------------------------------------------------

核心接口:

1 public interface Repository<T, ID extends Serializable> {
2
3 }

这个接口只是一个空的接口,目的是为了统一所有Repository的类型,其接口类型使用了泛型,泛型参数中T代表实体类型,ID则是实体中id的类型

再来看一下Repository的直接子接口CrudRepository中的方法:

 1 public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
 2
 3     <S extends T> S save(S entity);
 4
 5     <S extends T> Iterable<S> save(Iterable<S> entities);
 6
 7     T findOne(ID id);
 8
 9     boolean exists(ID id);
10
11     Iterable<T> findAll();
12
13     Iterable<T> findAll(Iterable<ID> ids);
14
15     long count();
16
17     void delete(ID id);
18
19     void delete(T entity);
20
21     void delete(Iterable<? extends T> entities);
22
23     void deleteAll();
24 }

此接口中的方法大多是我们在访问数据库中常用的一些方法,如果我们要写自己的DAO类的时候,只需定义个接口来集成它便可使用了。

再来看看Spring Data未我们提供分页和排序的Repository的接口PagingAndSortingRepository:

1 public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
2     Iterable<T> findAll(Sort sort);
3     Page<T> findAll(Pageable pageable);
4 }

这些Repository都是spring-data-commons提供给我们的核心接口,spring-data-commons是Spring Data的核心包。这个接口中为我们提供了数据的分页方法,以及排序方法。[b]看吧,spring-data让我们省了很多心了,一切都按照这个规范进行构造,就连业务系统中常用到的一些操作都为我们考虑到了,而我们只需更用心的去关注业务逻辑层。[/b]spring-data将repository的颗粒度划得很细,其实我觉得spring的框架中将每个类的颗粒度都划得很细,这主要也是为了责任分离。

----------------------------------------------邪恶的分割线------------------------------------------------------

JPA实现:

针对spring-data-jpa又提供了一系列repository接口,其中有JpaRepository和JpaSpecificationExecutor,这2个接口又有什么区别呢,我们分别来看看这2个接口的源码。

JpaRepository.class

1 public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
2     List<T> findAll();
3     List<T> findAll(Sort sort);
4     <S extends T> List<S> save(Iterable<S> entities);
5     void flush();
6     T saveAndFlush(T entity);
7     void deleteInBatch(Iterable<T> entities);
8     void deleteAllInBatch();
9 }

这个类继承自PagingAndSortingRepository,看其中的方法,可以看出里面的方法都是一些简单的操作,并未涉及到复杂的逻辑。当你在处理一些简单的数据逻辑时,便可继承此接口,看一个小例子吧。本文JPA供应者选择的是Hibernate EntityManager,当然读者们也可以选择其他的JPA供应者,比如EclipseLink、OpenJPA,反正JPA是个标准,在无须修改的情况下便可移植。

先定义一用户实体类User.class:

 1 @Entity
 2 @Table( name = "spring_data_user" )
 3 @PrimaryKeyJoinColumn( name = "id" )
 4 public class User extends IdGenerator{
 5     private static final long serialVersionUID = 1L;
 6
 7     private String name;
 8     private String username;
 9     private String password;
10     private String sex;
11     private Date birth;
12     private String address;
13     private String zip;
14
15         //省略getter和setter
16 }

Id生成策略是采用的表生成策略,这里就不贴代码了,spring的配置文件我也就不贴出来了,反正就那些东西,网上一查,遍地都是。后续我会在将demo附上来。

实体类是有了,现在得写一个持久层,这样才能操作数据库啊,现在我们来看一下持久层。IUserDao.class:

1 @Repository("userDao")
2 public interface IUserDao extends JpaRepository<User, Long>{}

再在spring的配置文件中加上以下代码。

1 <beans xmlns="http://www.springframework.org/schema/beans"
2     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3     xmlns:jpa="http://www.springframework.org/schema/data/jpa"
4     xsi:schemaLocation="http://www.springframework.org/schema/beans
5     http://www.springframework.org/schema/data/jpa
6     http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
7
8     <jpa:repositories base-package="org.tea.springdata.**.dao" />
9 </beans>

加上这段后Spring就会将指定包中@Repository的类注册为bean,将bean托管给Spring。这样定义完了就OK了!哦,就这样就可以操作数据库了?

是的,前面我就已经说了,Spring data已经帮我们写好一个实现类了,而简单的操作我们只须这样继承JpaRepository就可以做CRUD操作了。再写个业务类来测试一把吧。由于我用的Cglib来动态代理,所以就不定义接口了,直接定义类UserService.class:

 1 @Service("userService")
 2 public class UserService {
 3
 4     @Autowired
 5     private IUserDao dao;
 6
 7     public void save(User user) {
 8         dao.save(user);
 9     }
10
11     public void delete(Long id) {
12         dao.delete(id);
13     }
14
15     public void update(User user) {
16         dao.saveAndFlush(user);
17     }
18
19     public List<User> findAll() {
20         return dao.findAll();
21     }
22 }

来写一单元测试。

 1 public class UserServiceTest {
 2
 3     private static ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
 4
 5     private static UserService userService = (UserService) context.getBean("userService");
 6
 7     public void saveUser() {
 8         StopWatch sw = new StopWatch(getClass().getSimpleName());
 9         sw.start("Add a user information.");
10         User u = new User();
11         u.setName("John");
12         u.setSex("Man");
13         u.setUsername("JohnZhang");
14         u.setPassword("123456");
15         u.setBirth(new Date());
16         userService.save(u);
17         sw.stop();
18         System.err.println(sw.prettyPrint());
19     }
20
21      public static void main(String[] args) {
22         UserServiceTest test = new UserServiceTest();
23         test.saveUser();
24     }
25 }

绿了,高兴了,测试通过!

额,都没用Junit怎么会绿呢,开个玩笑。
其余继承下来的操作方法,大家都可以自己测试一下,如没意外,应该都会测试通过。

这只是spring data jpa简单的使用,而往往在项目中这一点功能并不能满足我们的需求。这是当然的,在业务中查询是一件非常头疼的事,毕竟不可能只是对一张表的查询是吧? 其实在业务中往往会涉及到多张表的查询,以及查询时需要的各种条件。当然这不用担心,毕竟这是对JPA的支持,而我们在用JPA原生态API的时候往往可能会把一些个方法写得很凌乱,没得一个具体的规范来写自己的方法在后期维护上肯定会很困难。当然你自己也可以封装一些方法来使用,而当我们使用到Spring Data JPA时,它已经帮助我们完成了这个方法的规范了。

来一起看一下复杂查询时它为我们提供的接口。

JpaSpecificationExecutor.class

public interface JpaSpecificationExecutor<T> {

    T findOne(Specification<T> spec);

    List<T> findAll(Specification<T> spec);

    Page<T> findAll(Specification<T> spec, Pageable pageable);

    List<T> findAll(Specification<T> spec, Sort sort);

    long count(Specification<T> spec);
}

在这个接口里面出现次数最多的类就是Specification.class,而这个类主要也就是围绕Specification来打造的,Specification.class是Spring Data JPA提供的一个查询规范,而你只需围绕这个规范来设置你的查询条件便可,我们来看一下Specification.class这个接口中有些什么东西。

Specification.class

1 public interface Specification<T> {
2
3     Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
4 }

只有一个方法toPredicate,而其中的参数大家并不陌生,都是JPA规范中的,ROOT查询中的条件表达式、CriteriaQuery条件查询设计器、CriteriaBuilder条件查询构造器,而我们在使用复杂对象查询时,实现该方法用JPA去构造对象查询便可。

下面来看一个小例子:

1 @Repository("userDao")
2 public interface IUserDao extends JpaSpecificationExecutor<User>{
3 }

仍然只是一个空接口,这次继承的是JpaSpecificationExecutor了。

再写一测试用例:查询用户表中name包含Sam的记录,并分页按照birth排倒序

 1 public class UserDaoTest {
 2
 3     private static ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
 4
 5     private static IUserDao userDao = (IUserDao) context.getBean("userDao");
 6
 7     public void findBySpecAndPaginate() {
 8         Page<User> page = userDao.findAll(new Specification<User>() {
 9             @Override
10             public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
11                 root = query.from(User.class);
12                 Path<String> nameExp = root.get("name");
13                 return cb.like(nameExp, "%Sam%");
14             }
15
16         }, new PageRequest(1, 5, new Sort(Direction.DESC, new String[] { "birth" })));
17
18         StringBuilder stout = new StringBuilder(" 以下是姓名包含Sam人员信息 : ").append("\n");
19         stout.append("| 序号 | username | password | name | sex | birth |").append("\n");
20         int sortIndex = 1;
21         for (User u : page.getContent()) {
22             stout.append(" | ").append(sortIndex);
23             stout.append(" | ").append(u.getUsername());
24             stout.append(" | ").append(u.getPassword());
25             stout.append(" | ").append(u.getName());
26             stout.append(" | ").append(u.getSex());
27             stout.append(" | ").append(u.getBirth());
28             stout.append(" | \n");
29             sortIndex++;
30         }
31         System.err.println(stout);
32     }
33
34     public static void main(String[] args) {
35         UserDaoTest test = new UserDaoTest();
36         test.findBySpecAndPaginate();
37     }
38 }

当然,这只是一个测试,很简单的一个条件查询方法。你也可以设计复杂的查询来得到自己所需的结果,我这只是写一个很简单的方法来带大家入门。

写了两篇文章了,还没有讲Spring Data JPA为什么只需定义接口就可以使用,其实这也不难发现,查看源码,可以找到针对JpaRepository和JpaSpecificationExecutor有一个实现类,SimpleJpaRepository.class,这个类实现了刚才所提的两个接口。而Spring在给我们注入实现类的时候,就正是这个SimpleJpaRepository.class,具体的实现方式我就不在这意义赘述了,大家如果有兴趣可以去查看它的源码,和传统的JPA实现是一样的。

通过这篇文章我们学习到了,当要使用复杂的条件查询时,我们可以选择使用此接口来完善我们的需求,这篇文章就讲到这里,在下一篇文章中我主要是讲Spring Data JPA为我们提供的注解查询。

时间: 2024-10-01 08:07:13

(转) Spring Data 系列之JPA的相关文章

Spring Data系列之Jpa(一)

1.Spring Data的由来 Spring Data是SpringSource基金会创下的一个简化数据库访问.支持云服务的开源框架.其主要目的是让数据库访问变的方便快捷,可以用Spring Data框架简化项目的开发. 2.什么是JPA??? JPA全称Java Persistence API.JPA通过JDK 5.0注解或XML描述对象-------关系表的映射关系,并将运行期的实体对象持久化(对象持久化是指将内存中的对象保存到可永久保存的存储设备中(如磁盘)的一种技术.)到数据库中. 3

Spring Data 系列(三) Spring+JPA(spring-data-commons)

本章是Spring Data系列的第三篇.系列文章,重点不是讲解JPA语法,所以跑开了JPA的很多语法等,重点放在环境搭建,通过对比方式,快速体会Spring 对JPA的强大功能. 准备代码过程中,保持了每个例子的独立性,和简单性,准备的源码包,下载即可使用.如果,对JPA语法想深入研究的话,直接下载在此基础上进行测试. 前言 Spring Data 系列(一) 入门:简单介绍了原生态的SQL使用,以及JdbcTemplate的使用,在这里写SQL的活还需要自己准备. Spring Data 系

Spring Data 系列(二) Spring+JPA入门

通过[Spring Data 系列(一) 入门]的介绍,通过对比的方式认识到Spring提供的JdbcTemplate的强大功能.通过使用JdbcTemplate,操作数据库,不需要手动处理Connection,Statement等底层SQL对象.可是,Spring对数据库的操作还远远没有结束.本章主要介绍Spring 对JPA的支持. 主要内容 简单介绍JPA Spring Hibernate JPA整合 1.JPA简介 Java Persistence API (JPA),为开发人员提供了一

Spring Data 系列(一) 入门

认识一个框架的用途和作用,其实很简单.反其道而行之,如果不用某框架,我们的开发工作会怎么样?我们必须要做那些额外的工作. 为了保证系列的完整性,也专门写一篇文章,作为本系列的引子. 考虑到简单性和专一性,本文准备的例子,弱到几点. 主要功能 通过比对,了解spring JdbcTemplate的功能,作为Spring Data的入门. 搭建一个实验的例子,作为将来学习Spring Data的基础. 暂时不考虑事务(事务可以很简单通过AOP方式切入进来) 1.准备 JDK:1.7 spring:4

Spring Boot:整合Spring Data JPA

综合概述 JPA是Java Persistence API的简称,是一套Sun官方提出的Java持久化规范.其设计目标主要是为了简化现有的持久化开发工作和整合ORM技术,它为Java开发人员提供了一种ORM工具来管理Java应用中的关系数据. 简而言之,JPA提供了使用面向对象的方式操作数据库的功能.JPA充分吸收了现有Hibernate,TopLink,JDO等ORM框架的优势,具有易于使用.伸缩性强等优点. Spring Data JPA是Spring基于Spring Data框架对于JPA

spring boot系列(五)spring boot 配置spring data jpa (查询方法)

接着上面spring boot系列(四)spring boot 配置spring data jpa 保存修改方法继续做查询的测试: 1 创建UserInfo实体类,代码和https://www.cnblogs.com/kxm87/p/9273555.html中的一样. 2 创建数据库操作类相当于dao层,主要创建一个接口UserRepository,继承JpaRepository接口即可.本代码中主要都是自定义方法. 使用findXX 或者countXX(这两个不用编写sql,jpa会自动生成)

Spring Data Jpa系列教程--------实体解析和关联关系

Spring Data Jpa是基于HIbernate开发的,所以建立实体建的实体和映射关系需要好好好的去了解一下,本文有以下内容,实体管理器介绍,实体与数据库表的映射介绍,关联关系(一对多,多对多)介绍,SpringDataJpa应用分析 ------实体管理器   实体管理器EntityManager是实体与数据库的桥梁(和事务一起发挥作用),相当于Hibenrtae中的session,Mybatis中的sqlSession.使用方法放个小例子吧 @PersistenceContext pr

Spring Boot2 系列教程(二十三)理解 Spring Data Jpa

有很多读者留言希望松哥能好好聊聊 Spring Data Jpa! 其实这个话题松哥以前零零散散的介绍过,在我的书里也有介绍过,但是在公众号中还没和大伙聊过,因此本文就和大家来仔细聊聊 Spring Data 和 Jpa! 本文大纲: 1. 故事的主角 1.1 Jpa 1.1.1 JPA 是什么 Java Persistence API:用于对象持久化的 API Java EE 5.0 平台标准的 ORM 规范,使得应用程序以统一的方式访问持久层 1.1.2 JPA 和 Hibernate 的关

Spring Boot2 系列教程 (七) | 使用 Spring Data JPA 访问 Mysql

前言 如题,今天介绍 Spring Data JPA 的使用. 什么是 Spring Data JPA 在介绍 Spring Data JPA 之前,首先介绍 Hibernate . Hibernate 使用 O/R 映射 (Object-Relation Mapping) 技术实现数据访问, O/R 映射即将领域模型类与数据库的表进行映射,通过程序操作对象而实现表数据操作的能力,让数据访问操作无需关注数据库相关技术. Hibernate 主导了 EJB 3.0 的 JPA 规范, JPA 即