SpringDataJPA
概述
虽然Spring开发框架可以整合JPA可以很方便的解决开发中得一些问题,但也存在一些问题。项目围绕业务层,而业务层围绕数据层,数据层在操作是需要使用JDBC,但是数据层需要定义DAO接口就必须实现其子类,还要手动使用EntityManager进行数据的操作,但是这些工作几乎都是相似的且重复的的,于是SpringDataJPA横空出世来解决这个问题。
项目源码地址:https://gitee.com/tirklee/lea-springdata-jpa
SpringDataJPA使用步骤
- 声明持久层的接口。该接口继承了Repository(或Repository的子接口,其中定义了一些常用的增、删、改、查,以及分页相关的方法)。
- 在接口中声明需要的业务方法,SpringDataJPA将根据给定的策略生成实现代码。
- 在配置文件中定义jpa:repositories配置项。这样Spring初始化容器时将扫描base-package指定的包目录及其子目录,为继承Repository或其子接口的接口创建代理对象,并将代理对象注册为Spring所管理的Bean对象,业务层便可以通过Spring自动封装的特性来直接使用该对象。
在整个SpringDataJPA中,核心的关键在于org.springframework.data.repository.Re
pository接口以及其子接口,这些接口的名称以及具体作用如下。
- Repository:一个标识,不包含任何方法,主要是为了方便Spring自动扫描识别。
- CrudRepository:继承于Repository,实现一组CRUD相关的方法。
- PagingAndSortingRepository:继承CrudRepository,实现一组分页排序相关的方法。
- JpaRepository:继承PagingAndSortingRepository,实现一组JPA规范相关的方法。
开发环境的搭建
- 根据数据库脚本创建相应的库已经数据表。
DROP DATABASE if EXISTS lea_spring_jpa;
create database lea_spring_jpa character set UTF8;
use lea_spring_jpa;
create TABLE dept(
deptno BIGINT AUTO_INCREMENT,
dname VARCHAR(50),
constraint deptno primary key (deptno)
)ENGINE=InnoDB;
- 新建项目lea-springdata-jpa 在POM.xml文件中加入项目依赖。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xiyue.leaspring</groupId>
<artifactId>lea-springdata-jpa</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>lea-springdata-jpa</name>
<description>lea-springdata-jpa</description>
<properties>
<spring-data-jpa.version>2.0.4.RELEASE</spring-data-jpa.version>
<c3p0.version>0.9.1.2</c3p0.version>
<spring.version>5.0.3.RELEASE</spring.version>
<mysql.version>8.0.23</mysql.version>
<junti.version>4.12</junti.version>
<compiler.version>3.6.1</compiler.version>
<jdk.version>1.8</jdk.version>
<project.build.sourceEnoding>UTF-8</project.build.sourceEnoding>
<hibernate.version>5.2.13.Final</hibernate.version>
</properties>
<dependencies>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>${c3p0.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</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>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junti.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>${spring-data-jpa.version}</version>
</dependency>
</dependencies>
</project>
- 在项目lea-springdata-jpa配置相应的配置文件
- src/main/resources/dev/config/database.properties
database.driverClass=com.mysql.cj.jdbc.Driver
database.url=jdbc:mysql://192.168.1.6:3306/lea_spring_jpa
database.user=root
database.password=123456
- src/main/resources/META-INF/persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="LEA_SPRING_JPA"><!-- 定义持久化单元 -->
<!-- <shared-cache-mode/>元素可以使用的配置项如下。
ALL:所有的实体类都被缓存。
NONE:所有的实体类都不被缓存。
ENABLE_SELECTIVE:标识@Cacheable(true) 注解的实体类将被缓存。
DISABLE_SELECTIVE:缓存除标识@Cacheable(false) 以外的所有实体类。
UNSPECIFIED:默认值,JPA 产品默认值将被使用。
-->
<!-- <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode> -->
<properties>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>
- src/main/resources/spring/spring-jpa.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:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.8.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<context:component-scan base-package="com.xiyue.leaspring">
<context:exclude-filter type="annotation" expression="com.xiyue.leaspring.dao"/>
</context:component-scan>
<context:property-placeholder location="classpath:dev/config/database.properties"/>
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${database.driverClass}"/>
<property name="jdbcUrl" value="${database.url}"/>
<property name="user" value="${database.user}"/>
<property name="password" value="${database.password}"/>
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/><!-- 数据源 -->
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml"/><!-- JPA核心配置文件 -->
<property name="persistenceUnitName" value="LEA_SPRING_JPA"/><!-- 持久化单元名称 -->
<property name="packagesToScan" value="com.xiyue.leaspring"/><!-- PO类扫描包 -->
<property name="persistenceProvider"><!-- 持久化提供类,本次为hibernate -->
<bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
</property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
</property>
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
</property>
</bean>
<!-- 定义事务管理的配置,必须配置PlatformTransactionManager接口子类 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 定义SpringDataJPA的数据层接口所在包,该包中的接口一定义是Repository子接口 -->
<jpa:repositories base-package="com.xiyue.leaspring.dao"/>
</beans>
4.新建实体类
package com.xiyue.leaspring.po;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@SuppressWarnings("serial")
@Entity
@Table(name="dept")
public class Dept implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long deptno;
private String dname;
public Long getDeptno() {
return deptno;
}
public void setDeptno(Long deptno) {
this.deptno = deptno;
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
@Override
public String toString() {
return "Dept [deptno=" + deptno + ", dname=" + dname + "]";
}
}
Repository基本使用
SpringDataJPA开发中,所有的数据层接口都要求继承于Repository接口。Repository的主要作用是可以直接使用注解或方法名称定义规则的形式来实现数据层功能。
- 在【lea-springdata-jpa】中新建DAO层接口IDeptDAO
ackage com.xiyue.leaspring.dao;
import java.util.List;
import org.springframework.data.repository.Repository;
import com.xiyue.leaspring.po.Dept;
public interface IDeptDAO extends Repository<Dept,Long>{
/**
* 增加新的部门数据,方法名称定义为save,以自动实现merge()功能
* @param vo 部门持久化对象
* @return 增加成功,返回持久化对象
*/
public Dept save(Dept vo);
/**
* 查询全部部门数据
* @return 部门持久化对象集合
*/
public List<Dept> findAll();
}
IDeptDAO数据层接口与原始定义上有一些差别,主要是在数据保存上使用了save方法名称。同时由于该接口继承了Repository父接口,所以此接口将为SpringDataJPA功能接口,该接口的子类可以动态创建。
提示:关于IDeptDAO接口中的方法名称。
定义IDeptDAO接口时,如果使用的方法不是save和findAll,DAO层将无法正常工作,因为SpringDataJPA规定了数据层方法命名。
- 在【lea-springdata-jpa】中 新建业务接口与其实现类。
package com.xiyue.leaspring.service;
import java.util.List;
import com.xiyue.leaspring.po.Dept;
public interface IDeptService {
/**
* 新增部门数据调用IDeptDao.doCreate方法处理
* @param vo 持久化类对象,没有设置主键
* @return 增加成功返回true,否则返回false
*/
public Dept add(Dept vo);
/**
* 查询dept表中得全部数据
* @return Dept持久化对象集合
*/
public List<Dept> getlist();
}
package com.xiyue.leaspring.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.xiyue.leaspring.dao.IDeptDAO;
import com.xiyue.leaspring.po.Dept;
import com.xiyue.leaspring.service.IDeptService;
@Service
public class DeptServiceImpl implements IDeptService {
@Autowired//注入IDeptDaos接口实例
private IDeptDAO deptDao;
public Dept add(Dept vo) {
// TODO Auto-generated method stub
return this.deptDao.save(vo);//增加实例
}
public List<Dept> getlist() {
// TODO Auto-generated method stub
return this.deptDao.findAll();//数据查询
}
}
可以使用注解代替继承的Repository父接口。本程序所定义的IDeptDAO接口必须强制性继承Repository父接口,才可以实现SpringDataJPA操作。Spring设计中考虑到不同开发者的需求,也可以利用注解来实现。
package com.xiyue.leaspring.dao;
import java.util.List;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.RepositoryDefinition;
import com.xiyue.leaspring.po.Dept;
@RepositoryDefinition(domainClass = Dept.class,idClass = Long.class)
public interface IDeptDAO{
/**
* 增加新的部门数据,方法名称定义为save,以自动实现merge()功能
* @param vo 部门持久化对象
* @return 增加成功,返回持久化对象
*/
public Dept save(Dept vo);
/**
* 查询全部部门数据
* @return 部门持久化对象集合
*/
@Query("SELECT d FROM Dept AS d")
public List<Dept> findAll();
}
这里DAO接口上使用的@RepositoryDefinition注解功能,与直接继承Repository父接口的效果相同。
Repository实现CRUD
SpringDataJPA中如果不依靠方法名称绑定操作,则可以通过@Query绑定操作类型。
/**
* 查询全部部门数据
* @return 部门持久化对象集合
*/
@Query("SELECT d FROM DEPT AS d")
public List<Dept> findAll();
1.根据ID进行查询
- 修改IDeptDao接口,@Query绑定参数方法
/**
* 根据ID进行查询,在JPQL获取参数时使用SpEL表达式获取第一个参数
* @param id 查询的部分编号
* @return 部门持久化对象
*/
@Query("SELECT d FROM Dept AS d WHERE d.deptno=?#{[0]}")//使用SpEL语法获得参数
public Dept findById(Long id);
- 在IDeptService接口定义一个get方法,用于调用findById方法,同时实现子类。
@Override
public Dept get(Long id) {
// TODO Auto-generated method stub
return this.deptDao.findById(id);
}
- 编写测试方法进行测试
@Test
public void testGet() {
Dept dept = this.deptService.get(1L);
System.out.println(dept);
}
Hibernate:
select
dept0_.deptno as deptno1_0_,
dept0_.dname as dname2_0_
from
dept dept0_
where
dept0_.deptno=?
Dept [deptno=1, dname=财务部]
- 根据指定的主键范围查询全部部门信息(IN操作)。
- 在IDeptDAO接口中增加新的数据查询方法。
/**
* 根据指定范围ID进行查询
* @param ids 全部部门ID
* @return 部门持久化对象
*/
@Query("SELECT d FROM Dept AS d WHERE d.deptno IN :pids")//使用pids获得参数
public List<Dept> findByIds(@Param(value="pids")Set<Long> ids);
- 在IDeptService方法中追加新的业务方法gets方法,并在DeptServiceImpl子类中实现此方法。
@Override
public List<Dept> gets(Set<Long> ids) {
// TODO Auto-generated method stub
return this.deptDao.findByIds(ids);
}
- 编写测试方法
@Test
public void testGets() {
Set<Long> allIds = new HashSet<Long>();
allIds.addAll(Arrays.asList(1L,3L,4L));
List<Dept> allDepts = this.deptService.gets(allIds);
allDepts.forEach((dept)->{
System.out.println(dept);
});
}
Hibernate:
select
dept0_.deptno as deptno1_0_,
dept0_.dname as dname2_0_
from
dept dept0_
where
dept0_.deptno in (
? , ? , ?
)
Dept [deptno=1, dname=财务部]
Dept [deptno=3, dname=市场部]
Dept [deptno=4, dname=研发部]
本程序在IDeptDAO定义新方法时,使用了@Param(value=“pids”)注解定义了@Query注解可以访问的参数名称,这种形式要比使用SpEL访问更加直观,同时该操作实现的IN处理只需要传递Set集合即可自动转换为查询参数。
- 在DAO方法中使用@Query注解时,也可以利用SpEL获取方法参数类中的属性。
- 在IDeptDAO接口中定义方法,可以根据部门ID与部门名称进行数据查询,此时将使用Dept作为参数类型。
/**
* 根据指定ID与部门名称进行查询
* @param dept 包含部门ID与部门名称dname
* @return Dept 部门持久化对象
*/
@Query("SELECT d FROM Dept AS d WHERE d.deptno=:#{#mydept.deptno} AND d.dname=:#{#mydept.dname}")//使用pids获得参数
public Dept findByIdAndName(@Param(value="mydept")Dept dept);
- 在IDeptService接口中定义一个getIdAndDname业务方法,并在子类进行实现。
@Override
public Dept getIdAndDname(Dept dept) {
// TODO Auto-generated method stub
return this.deptDao.findByIdAndName(dept);
}
- 编写测试方法
@Test
public void testGetIdAndName() {
Dept dpt = new Dept();
dpt.setDeptno(1L);
dpt.setDname("财务部");
System.out.println(this.deptService.getIdAndDname(dpt));
}
Hibernate:
select
dept0_.deptno as deptno1_0_,
dept0_.dname as dname2_0_
from
dept dept0_
where
dept0_.deptno=?
and dept0_.dname=?
Dept [deptno=1, dname=财务部]
本程序实现了多个参数的使用,并且为了方便将多个参数绑定在了Dept对象中,这样就可以在DAO层中利用SpEL访问对象的属性作为查询参数配置。
- 实现部门数据修改
而对于修改与删除的操作也可以利用@Query注解进行定义。
- 在IDeptDAO接口中定义数据修改方法。
/**
* 修改指定编号的部门信息
* @param dept 传递需要修改的部门数据
* @return 数据库更新影响的数据行数
*/
@Transactional //因为做的事更新操作此处开启事务否则可能会报错
@Modifying(clearAutomatically = true) //追加缓存的清楚与更新
@Query("UPDATE Dept AS d SET d.dname=:#{#mydept.dname} WHERE d.deptno=:#{#mydept.deptno}")//
public int doEdit(@Param(value="mydept")Dept dept);
- 在IDeptService接口定义edit方法,并且在子类实现此更新
@Override
public boolean edit(Dept dept) {
// TODO Auto-generated method stub
return this.deptDao.doEdit(dept)>0;
}
- 编写测试方法
@Test
public void testEdit() {
Dept dpt = new Dept();
dpt.setDeptno(3L);
dpt.setDname("小麦部");
System.out.println(this.deptService.edit(dpt));
}
Hibernate:
update
dept
set
dname=?
where
deptno=?
true
在进行更新数据时有可能需要进行相应缓存数据的清除,所以在定义doEdit方法时使用了@Modifying注解进行缓存更新,由于使用的是JPQL更新操作,所以不需要维护对象状态,可以直接更新。
- 实现数据删除。
- 在IDeptDAO接口定义数据删除方法。
/**
* 修改指定删除的部门信息
* @param deptno 部门编号
* @return 数据库更新影响的数据行数
*/
@Transactional
@Modifying(clearAutomatically = true) //追加缓存的清楚与更新
@Query("DELETE FROM Dept AS d WHERE d.deptno=:deptno")//
public int doRemove(@Param(value="deptno")Long dept);
- 在IDeptService业务层中定义remove方法,并且在子类实现此删除业务方法。
@Override
public boolean remove(Long id) {
// TODO Auto-generated method stub
return this.deptDao.doRemove(id)>0;
}
- 编写测试类
@Test
public void testRemove() {
System.out.println(this.deptService.remove(4L));
}
Hibernate:
delete
from
dept
where
deptno=?
true
本程序利用JPQL实现了数据删除处理操作,删除时由于可能会存在数据缓存问题,所以依然配置@Modifying注解,可以发现在Repository整体设计中都只需要开发者定义很简单的部分,即可方便地实现自定义数据层操作。
Repository方法映射
除了可以使用@Query操作外还可以采用方法映射形式利用容器自动生成查询语句定义。
SpringDataJPA框架在解析方法名时,会把方法名多余的前缀截取掉(如find、findBy、read、readBy、get、getBy等),然后对剩下部分进行解析。
- 根据部门名称查询
- IDeptDAO
/**
* 根据名称查询
* @param dname 部门名称
* @return
*/
public List<Dept> findByDname(String dname);
- IDeptService,IDeptService
@Override
public List<Dept> getByDname(String dname) {
// TODO Auto-generated method stub
return this.deptDao.findByDname(dname);
}
- 测试
@Test
public void testGetByDname() {
this.deptService.getByDname("市场部").forEach(System.out::println);
}
Hibernate:
select
dept0_.deptno as deptno1_0_,
dept0_.dname as dname2_0_
from
dept dept0_
where
dept0_.dname=?
本程序利用方法映射处理,从而省略了@Query注解定义查询语句。
- 查询指定范围ID的部门数据。
- IDeptDAO
/**
* 根据部门指定ids查询
* @param ids 查询部门的ids
* @return 返回结果
*/
public List<Dept> findByDeptnoIn(Set<Long> ids);
- IDeptService,IDeptService
@Override
public List<Dept> getByDeptnoIn(Set<Long> ids){
// TODO Auto-generated method stub
return this.deptDao.findByIds(ids);
}
- 测试
@Test
public void getByDeptnoIn() {
Set<Long> allIds = new HashSet<Long>();
allIds.addAll(Arrays.asList(1L,3L,4L));
List<Dept> allDepts = this.deptService.getByDeptnoIn(allIds);
allDepts.forEach(System.out::println);
}
Hibernate:
select
dept0_.deptno as deptno1_0_,
dept0_.dname as dname2_0_
from
dept dept0_
where
dept0_.deptno in (
? , ? , ?
)
Dept [deptno=1, dname=财务部]
Dept [deptno=3, dname=小麦部]
在使用IN操作符时,方法映射需要接收集合数据,所以本程序在数据层方法中定义了Set集合,保存所有要查询的部门编号,这样会自动将参数转换为IN使用的参数。
- 实现数据模糊查询与排序。
- IDeptDAO
/**
* 根据部门名称进行模糊查询(containing表示前后都追加%),而后将查询结果按照deptno降序排列
* @param keyWord 查询关键字
* @return 部门持久化对象集合
*/
public List<Dept> findByDnameContainingOrderByDeptnoDesc(String keyWord);
- IDeptService,IDeptService
@Override
public List<Dept> liestSearch(String keyword) {
// TODO Auto-generated method stub
return this.deptDao.findByDnameContainingOrderByDeptnoDesc(keyword);
}
- 测试
@Test
public void listSearch() {
this.deptService.liestSearch("销").forEach(System.out::println);
}
Hibernate:
select
dept0_.deptno as deptno1_0_,
dept0_.dname as dname2_0_
from
dept dept0_
where
dept0_.dname like ?
order by
dept0_.deptno desc
Dept [deptno=2, dname=销售部]
本程序实现了数据模糊查询。在方法映射中,模糊查询可以使用StartingWith(开头追加%)、EndingWith(结尾追加%)和Containing(前后都追加%)3种边界匹配方法。在数据查询后使用了ORDER BY排序定义,将部门编号由高到底降序排列。
CrudRepository数据接口
- 【lea-springdata-jpa】IDeptDAO_CrudRepository继承CrudRepository父接口
package com.xiyue.leaspring.dao;
import org.springframework.data.repository.CrudRepository;
import com.xiyue.leaspring.po.Dept;
public interface IDeptDAO_CrudRepository extends CrudRepository<Dept,Long>{
}
- 【lea-springdata-jpa】IDeptService_CrudRepository重新定义IDeptService业务接口
package com.xiyue.leaspring.service;
import java.util.List;
import com.xiyue.leaspring.po.Dept;
public interface IDeptService_CrudRepository {
public boolean add(Dept vo);//新增
public List<Dept> list();//获取全部
public Dept get(Long id);//根据ID查询
}
-【lea-springdata-jpa】 IDeptServiceImpl_CrudRepository ,IDeptService_CrudRepository重新定义IDeptService业务接口实现类
package com.xiyue.leaspring.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.xiyue.leaspring.dao.IDeptDAO_CrudRepository;
import com.xiyue.leaspring.po.Dept;
import com.xiyue.leaspring.service.IDeptService_CrudRepository;
@Service
public class IDeptServiceImpl_CrudRepository implements IDeptService_CrudRepository {
@Autowired
private IDeptDAO_CrudRepository iDeptDAO_CrudRepository;
@Override
public boolean add(Dept vo) {
// TODO Auto-generated method stub
return this.iDeptDAO_CrudRepository.save(vo) != null;
}
@Override
public List<Dept> list() {
// TODO Auto-generated method stub
return (List<Dept>) this.iDeptDAO_CrudRepository.findAll();
}
@Override
public Dept get(Long id) {
// TODO Auto-generated method stub
return this.iDeptDAO_CrudRepository.findById(id).get();
}
}
- 测试
package com.xiyue.leaspring.service;
import java.util.List;
import java.util.function.Consumer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.xiyue.leaspring.po.Dept;
@ContextConfiguration(locations = {"classpath:spring/spring-jpa.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class IDeptServiceTest2 {
@Autowired
private IDeptService_CrudRepository deptService;
@Test
public void testAdd() {
Dept vo = new Dept();
vo.setDname("天空部");
System.out.println(this.deptService.add(vo));
}
@Test
public void testGetlist() {
List<Dept> allDepts = this.deptService.list();
allDepts.forEach(new Consumer<Dept>() {
public void accept(Dept dept) {
System.out.println(dept);
}
});
}
@Test
public void testGet() {
Dept dept = this.deptService.get(1L);
System.out.println(dept);
}
}
本程序的测试结果与之前相同,区别在于用户自定义DAO层中的方法可以减少重复功能定义。
PagingAndSortingRepository数据接口
CrudRepository实现的只是基础的CRUD处理功能,其本身不支持数据的分页显示及排序处理,所以在SpringDataJPA中还提供了CrudRepository子接口PagingAndSortingRepository,以实现分页与排序。
PagingAndSortingRepository接口提供的findAll方法可以实现分页与排序查询,此方法需要接收一个Pageable接口参数。该类型可以保存分页信息,如当前页(currentPage,从0开始)、每页显示数据行数(lineSize)与排序定义。
- 继承PagingAndSortingRepository父接口。
package com.xiyue.leaspring.dao;
import org.springframework.data.repository.PagingAndSortingRepository;
import com.xiyue.leaspring.po.Dept;
public interface IDeptDAO_PagingAndSortingRepository extends PagingAndSortingRepository<Dept,Long> {
}
- 修改IDeptService业务层定义,增加新的方法。
package com.xiyue.leaspring.service;
import java.util.List;
import java.util.Map;
import com.xiyue.leaspring.po.Dept;
public interface IDeptService_PagingAndSortingRepository {
/**
* 实现数据分页处理
* @param curentPage 当前页
* @param lineSize 每页的数据行
* @return 返回结果包含以下情况:
* 1.key=allDepts value=所有部门信息
* 2.key=deptCount value=统计结果
* 3.key=deptPage value=总页数
*/
public Map<String,Object> listPageSort(int curentPage,int lineSize);
public List<Dept> list();
}
- 修改DeptServiceImpl子类,为list方法追加排序,同时覆写listPageSort分页查询。
package com.xiyue.leaspring.service.impl;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import com.xiyue.leaspring.dao.IDeptDAO_PagingAndSortingRepository;
import com.xiyue.leaspring.po.Dept;
import com.xiyue.leaspring.service.IDeptService_PagingAndSortingRepository;
@Service
public class IDeptServiceImpl_PagingAndSortingRepository implements IDeptService_PagingAndSortingRepository {
@Autowired
private IDeptDAO_PagingAndSortingRepository iDeptDAO_PagingAndSortingRepository;
@Override
public Map<String, Object> listPageSort(int curentPage, int lineSize) {
// TODO Auto-generated method stub
Sort sort = new Sort(Sort.Direction.DESC,"deptno");//设置部门编号deptno降序
//将分页与排序操作保存到PageRequest接口对象中,通过DAO层进行方法调用,页数从0开始
Pageable pageable = PageRequest.of(curentPage-1, lineSize,sort);
//Page会自动保存全部记录,总记录数,以及总页数
Page<Dept> pageDept = this.iDeptDAO_PagingAndSortingRepository.
findAll(pageable);//数据查询
Map<String,Object> map = new HashMap<String, Object>();//保存返回结果
map.put("allDepts", pageDept.getContent());//保存全部记录
map.put("deptCount", pageDept.getTotalElements());//保存总记录数
map.put("deptPage",pageDept.getTotalPages());//保存总页数
return map;
}
@Override
public List<Dept> list() {
// TODO Auto-generated method stub
Sort sort = new Sort(Sort.Direction.DESC,"deptno");//设置部门编号deptno降序
return (List<Dept>) this.iDeptDAO_PagingAndSortingRepository.findAll(sort);//排序查询
}
}
- 在测试类中编写代码,测试listPageSort.
package com.xiyue.leaspring.service;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ContextConfiguration(locations = {"classpath:spring/spring-jpa.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class IDeptServiceTest3 {
@Autowired
private IDeptService_PagingAndSortingRepository deptService;
@Test
public void testPageSortDept() {
System.out.println(this.deptService.listPageSort(1,2));
}
}
Hibernate:
select
dept0_.deptno as deptno1_0_,
dept0_.dname as dname2_0_
from
dept dept0_
order by
dept0_.deptno desc limit ?
Hibernate:
select
count(dept0_.deptno) as col_0_0_
from
dept dept0_
{allDepts=[Dept [deptno=5, dname=天空部], Dept [deptno=3, dname=小麦部]], deptPage=2, deptCount=4}
本程序实现了数据的分页查询处理。在进行分页查询(数据查询与统计查询)中,需要将所有分页的参数利用PageRequest类提供的方法进行创建,而返回的数据会通过Page接口进行包装。
JpaRepository数据接口
Repository数据接口支持SpringData相关处理,同时为了方便开发者进行项目编写还提供了CrudRepository、PagingAndSortingRepository子接口,但是考虑到JPA开发中的一些特性,SpringDataJPA又在PagingAndSortingRepository子接口下继续定义了一个JpaRepository子接口.
1.新建IDeptDAO_JpaRepository接口,并继承JpaRepository接口。
package com.xiyue.leaspring.dao;
import org.springframework.data.jpa.repository.JpaRepository;
import com.xiyue.leaspring.po.Dept;
public interface IDeptDAO_JpaRepository extends JpaRepository<Dept,Long> {//SpringData数据接口
}
2.新建IDeptService_JpaRepository接口以及其实现子类IDeptServiceImpl_JpaRepository并添加业务方法。
package com.xiyue.leaspring.service;
import java.util.List;
import java.util.Set;
import com.xiyue.leaspring.po.Dept;
public interface IDeptService_JpaRepository {
/**
* 根据指定范围内的Id进行数据查询
* @param ids 要查询的部门编号
* @return 全部部门信息
*/
public List<Dept> list(Set<Long> ids);
}
package com.xiyue.leaspring.service;
import java.util.List;
import java.util.Set;
import com.xiyue.leaspring.po.Dept;
public interface IDeptService_JpaRepository {
/**
* 根据指定范围内的Id进行数据查询
* @param ids 要查询的部门编号
* @return 全部部门信息
*/
public List<Dept> list(Set<Long> ids);
}
3.编写测试业务
package com.xiyue.leaspring.service;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ContextConfiguration(locations = {"classpath:spring/spring-jpa.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class IDeptServiceTest4 {
@Autowired
private IDeptService_JpaRepository deptService;
@Test
public void testList() {
Set<Long> allIds = new HashSet<Long>();
allIds.addAll(Arrays.asList(1L,2L,3L));
this.deptService.list(allIds).forEach(System.out::println);
}
}
Hibernate:
select
dept0_.deptno as deptno1_0_,
dept0_.dname as dname2_0_
from
dept dept0_
where
dept0_.deptno in (
? , ? , ?
)
Dept [deptno=1, dname=财务部]
Dept [deptno=2, dname=销售部]
Dept [deptno=3, dname=小麦部]
通过一系列分析可以发现,在实际项目开发中,使用JpaRepository实现的数据层开发操作是最接近于JPA开发框架使用环境的,同时也支持其他的Repository接口功能。利用此接口实现的JPA整合才是最方便的。