目录
六、PagingAndSortingRepository 接口
一、Spring Data 介绍
Spring Data : Spring 的一个子项目。用于简化数据库访问,支持NoSQL 和 关系数据存储。其主要目标是使数据库的访问变得方便快捷。开发者唯一要做的,就是声明持久层的接口,其他都交给 Spring Data JPA 来帮你完成!
SpringData 项目所支持 NoSQL 存储:
-
MongoDB (文档数据库)
-
Neo4j(图形数据库)
-
Redis(键/值存储)
-
Hbase(列族数据库)
SpringData 项目所支持的关系数据存储技术:
-
JDBC
-
JPA
二、搭建 Spring Data JPA 环境
-
使用Maven构建项目,项目名为:springdatajpa
-
配置 pom.xml
<properties> <spring.version>5.2.6.RELEASE</spring.version> <hibernate.version>5.4.10.Final</hibernate.version> <mysql.version>8.0.21</mysql.version> <ehcache.version>3.8.1</ehcache.version> <jpa.version>1.0.1.Final</jpa.version> <slf4j.version>1.7.25</slf4j.version> <aspectj.version>1.9.5</aspectj.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <!-- Spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</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-core</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-aop</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-orm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>2.2.5.RELEASE</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>org.hibernate</groupId> <artifactId>hibernate-hikaricp</artifactId> <version>${hibernate.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-ehcache</artifactId> <version>${hibernate.version}</version> </dependency> <dependency> <groupId>org.hibernate.javax.persistence</groupId> <artifactId>hibernate-jpa-2.0-api</artifactId> <version>${jpa.version}</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>org.ehcache</groupId> <artifactId>ehcache</artifactId> <version>${ehcache.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>${aspectj.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>
-
创建 com.javakc.springdatajpa 包
-
创建 entity 包并在其目录下创建实体类
-
创建 dao 包并在其目录下创建接口继承 JpaRepository<T, ID> 接口
-
在 resources 目录下创建 jdbc.properties 配置文件
jdbc.driverClass=com.mysql.cj.jdbc.Driver jdbc.jdbcUrl=jdbc:mysql:///jpa?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8 jdbc.username=root jdbc.password=123456
-
在 resources 目录下创建 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:tx="http://www.springframework.org/schema/tx" 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.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/data/jpa https://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> <!-- 配置自动扫描包 --> <context:component-scan base-package="com.javakc.springdatajpa"></context:component-scan> <!-- 加载配置文件 --> <context:property-placeholder location="jdbc.properties"></context:property-placeholder> <!-- 配置 Hikari 数据源 --> <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"> <property name="driverClassName" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <!-- 配置 JPA 的 EntityManagerFactory --> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource"></property> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean> </property> <property name="packagesToScan" value="com.javakc.springdatajpa.entity"></property> <property name="jpaProperties"> <props> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> </props> </property> </bean> <!-- 配置 JPA 使用的事物管理器 --> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"></property> </bean> <!-- 配置支持基于注解的事物配置 --> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven> <!-- 配置 SpringDataJpa --> <jpa:repositories base-package="com.javakc.springdatajpa" entity-manager-factory-ref="entityManagerFactory"></jpa:repositories> </beans>
-
创建测试类
import com.javakc.springdatajpa.repository.JpaRepository; 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; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:spring-jpa.xml"}) public class JpaTest { @Autowired private StudentDao studentDao; @Test public void test() { } }
三、Repository 接口
Repository 接口是 Spring Data 的一个核心接口,它不提供任何方法,开发者在自己定义的接口中声明需要的方法,只要遵循 Spring Data的规范,就无需写实现类。
基础的 Repository 提供了最基本的数据访问功能,其几个子接口则扩展了一些功能。它们的继承关系如下:
-
Repository: 仅仅是一个标识,表明任何继承它的均为仓库接口类
-
CrudRepository: 继承 Repository,实现了 CRUD 相关的方法
-
PagingAndSortingRepository: 继承 CrudRepository,实现了分页排序相关的方法
-
JpaRepository: 继承 PagingAndSortingRepository,实现 JPA 规范相关的方法
-
JpaSpecificationExecutor: 不属于Repository体系,实现 JPA Criteria 查询条件相关的方法
四、Query 注解
-
使用 @Query 注解可以自定义 JPQL 语句来实现更灵活的查询,结构更为清晰,这是 Spring data 的特有实现
@Query("select count(s) from Student s") public int getCountStudent();
-
索引参数
@Query("select s from Student s where s.id = ?1 and s.studentName = ?2") public Student getStudentParam1(int id, String studentName);
-
命名参数
@Query("select s from Student s where s.id = :id and s.studentName = :studentName") public Student getStudentParam2(@Param("id") int id,@Param("studentName") String studentName);
-
模糊查询
@Query("select s from Student s where s.studentName like %?1%") public List<Student> findStudentParam3(String studentName);
@Query("select s from Student s where s.studentName like %:studentName%") public List<Student> findStudentParam4(@Param("studentName") String studentName);
-
原生SQL
@Query(value="select count(id) from jpa_student", nativeQuery=true) public int getCountStudent2();
五、CrudRepository接口
-
保存
@Test public void save() { Student student = new Student(); student.setStudentName("小明"); jpaDao.save(student); }
-
删除
@Test public void delete() { jpaDao.deleteById(1); }
-
获取单条数据
@Test public void get() { Optional<Student> o = studentDao.findById(1); Student student = o.get(); System.out.println(student); }
-
修改
@Test public void update() { Student student = new Student(); student.setId(1); student.setStudentName("小红"); jpaDao.save(student); }
六、PagingAndSortingRepository 接口
分页与排序
@Test public void testPageAndSort() { int pageNo = 1 -1; int pageSize = 3; Sort.Order order1 = new Sort.Order(Sort.Direction.ASC, "id"); Sort.Order order2 = new Sort.Order(Sort.Direction.ASC, "studentName"); Sort sort = Sort.by(order1, order2); Pageable pageable = PageRequest.of(pageNo,pageSize,sort); Page<Student> page = jpaDao.findAll(pageable); System.out.println("总记录数: " + page.getTotalElements()); System.out.println("总页数: " + page.getTotalPages()); System.out.println("当前页数据: " + page.getContent()); System.out.println("当前页记录数: " + page.getNumberOfElements()); }
七、JpaSpecificationExecutor 接口
继承 JpaSpecificationExecutor<T> 接口
带条件的分页查询
@Test public void testPageAndCriteria() { Specification<Student> specification = new Specification<Student>() { /** * @param root 要查询的实体类 * @param criteriaQuery 得到 Root 对象, 告知 JPA Criteria 要查询哪个实体类 * @param criteriaBuilder 创建 Criteria 对象的工厂, 得到 Predicate 对象 * @return 表示一个查询条件 */ @Override public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { Path path = root.get("studentName"); Predicate predicate = criteriaBuilder.like(path, "%D%"); return predicate; } }; int pageNo = 1 -1; int pageSize = 3; Pageable pageable = PageRequest.of(pageNo, pageSize); Page<Student> page = jpaDao.findAll(specification, pageable); System.out.println("总记录数: " + page.getTotalElements()); System.out.println("总页数: " + page.getTotalPages()); System.out.println("当前页数据: " + page.getContent()); System.out.println("当前页记录数: " + page.getNumberOfElements()); }
带条件的分页查询 - 多表关联
@Test public void testPageAndCriteria2() { Specification<Student> specification = new Specification<Student>() { @Override public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { // ## 动态 SQL 表达式 Predicate predicate = criteriaBuilder.conjunction(); // ## 动态 SQL 表达式集合 List<Expression<Boolean>> expressionList = predicate.getExpressions(); Join<Object, Object> join = root.join("classRoom", JoinType.LEFT); expressionList.add(criteriaBuilder.equal(join.get("classRoomName"), "javakc")); return predicate; } }; int pageNo = 1 -1; int pageSize = 3; Pageable pageable = PageRequest.of(pageNo, pageSize); Page<Student> page = jpaDao.findAll(specification, pageable); System.out.println("总记录数: " + page.getTotalElements()); System.out.println("总页数: " + page.getTotalPages()); System.out.println("当前页数据: " + page.getContent()); System.out.println("当前页记录数: " + page.getNumberOfElements()); }