本文主要介绍 Spring Boot 中如何使用 Sping Data JPA,相关的环境及软件信息如下:Spring Boot 2.6.10。
1、Sping Data JPA 简介
Spring Data JPA 是 Spring Data 家族的一部分,它对基于 JPA 的数据访问提供了增强支持,让 JPA 更加易用。 它使得使用 Spring 构建的应用程序访问数据库变得更加容易。
编写应用程序的数据访问层是一件很麻烦的事, 必须编写太多样板代码来执行简单的查询以及分页和审计。 Spring Data JPA 旨在通过减工作量来显着改进数据访问层的实现。 作为开发人员,编写数据访问层接口,包括自定义查询方法,Spring 将自动提供实现。
1.1、Spring Data JPA 的特点
-
Sophisticated support to build repositories based on Spring and JPA
-
Support for Querydsl predicates and thus type-safe JPA queries
-
Transparent auditing of domain class
-
Pagination support, dynamic query execution, ability to integrate custom data access code
-
Validation of
@Query
annotated queries at bootstrap time -
Support for XML based entity mapping
-
JavaConfig based repository configuration by introducing
@EnableJpaRepositories
.
1.2、Spring Data JPA 与 Hibernate 的关系
Spring Data JPA 是 Spring 提供的一套对 JPA 操作更加高级的封装,底层依赖 Hibernate 的 JPA 实现。
2、JpaRepository 和 JpaSpecificationExecutor 接口
Spring Data JPA 提供了 JpaRepository 和 JpaSpecificationExecutor 接口,通过它们可以快速定义 DAO 接口,Spring Data JPA 会自动实现该接口。
2.1、JpaRepository
JpaRepository 接口内置了很多方法:
2.1.1、使用 Query 注解
org.springframework.data.jpa.repository.Query 注解可以定义使用 JPQL 或 sql 操作数据。
/** * 通过 JPQL 查询 */ @Query("from Student where name=?1 and age=?2") Student query(String name, Integer age); /** * sql 语句查询数据 */ @Query(value = "select * from a_student where name=? and age=?", nativeQuery = true) Student queryBysql(String name, Integer age);
2.1.1、按照 Spring Data JPA 规范定义方法名称
Spring Data JPA 提供一些关键词,通过这些关键词和实体属性名称来组装方法名称。 主题关键词:Keyword | Description |
---|---|
|
General query method returning typically the repository type, a |
|
Exists projection, returning typically a |
|
Count projection returning a numeric result. |
|
Delete query method returning either no result ( |
|
Limit the query results to the first |
|
Use a distinct query to return only unique results. Consult the store-specific documentation whether that feature is supported. This keyword can occur in any place of the subject between |
Logical keyword | Keyword expressions |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
方法名 | JPQL |
findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
findByStartDateBetween | … where x.startDate between ?1 and ?2 |
findByAgeLessthan | … where x.age < ?1 |
findByAgeLessthanEqual | … where x.age ⇐ ?1 |
2.2、JpaSpecificationExecutor
JpaSpecificationExecutor 接口主要用来在 JpaRepository 接口无法满足要求时,可以使用 JPA 的标准 API 来操作数据。
3、Sping Data JPA 使用
这里演示下 Spring Data JPA 的基本使用,工程目录结构如下:
3.1、引入依赖
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.10</version> <relativePath /> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>MysqL</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.29</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> <scope>provided</scope> </dependency> </dependencies>
3.2、创建实体类
package com.abc.demojpa.entity; import lombok.Data; import lombok.NoArgsConstructor; import org.hibernate.annotations.CreationTimestamp; import org.hibernate.annotations.UpdateTimestamp; import javax.persistence.*; import java.time.LocalDateTime; @NoArgsConstructor @Data @Entity @Table(name = "a_student") public class Student { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @CreationTimestamp @Column(name = "create_time") private LocalDateTime createTime; @UpdateTimestamp @Column(name = "modify_time") private LocalDateTime modifyTime; private String name; private Integer age; @Column(name = "home_address") private String homeAddress; }
3.3、编写配置文件(application.yml)
spring: datasource: driver-class-name: com.MysqL.cj.jdbc.Driver url: jdbc:MysqL://10.49.196.10:3306/test?useUnicode=true&characterEncoding=UTF-8 username: root password: 123456 jpa: hibernate: ddl-auto: update
3.4、定义 DAO 接口
package com.abc.demojpa.dao; import com.abc.demojpa.entity.Student; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import java.util.List; public interface IStudentRepository extends JpaRepository<Student, Long>, JpaSpecificationExecutor<Student> { /** * 根据姓名和年龄查询 */ List<Student> findByNameAndAge(String name, Integer age); /** * 根据姓名和年龄查询前5条 */ List<Student> findTop5ByNameAndAge(String name, Integer age); /** * 根据姓名和年龄查询并按id排序 */ List<Student> findByNameAndAgeOrderByIdAsc(String name, Integer age); /** * 根据姓名和年龄查询第一条 */ Student findFirstByNameAndAge(String name, Integer age); /** * 通过 JPQL 查询 */ @Query("from Student where name=?1 and age=?2") Student query(String name, Integer age); /** * 通过 JPQL 查询 */ @Query("from Student where name=:name and age=:age") Student query2(@Param("name") String name, @Param("age") Integer age); /** * 通过 JPQL 更新数据 */ @Query("update Student set homeAddress=?1 where id=?2") @Modifying void updateHomeAddress(String homeAddress, Long id); /** * sql 语句查询数据 */ @Query(value = "select * from a_student where name=? and age=?", nativeQuery = true) Student queryBysql(String name, Integer age); }
3.5、编写启动类
package com.abc.demojpa; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @EnableJpaAuditing @SpringBootApplication public class DemoJpaApplication { public static void main(String[] args) { SpringApplication.run(DemoJpaApplication.class, args); } }
3.6、编写测试用例
3.6.1、增加数据
/** * 增加数据 * Spring Boot 事务测试时默认会回滚操作避免产生测试数据,如果不需要回滚可使用 @Commit 注解 */ @Test @Transactional @Commit public void add() { Student student = new Student(); student.setName("小明"); student.setAge(15); student.setHomeAddress("江苏"); Student student2 = new Student(); student2.setName("小红"); student2.setAge(18); student2.setHomeAddress("广东"); studentRepository.save(student); studentRepository.save(student2); }
3.6.2、修改数据
@Transactional @Commit @Test public void modify() { Student student = new Student(); student.setId(3L); student.setName("小明2"); student.setAge(15); student.setHomeAddress("江苏"); //设置了 id 表示更新数据 studentRepository.save(student); //调用自定义的更新方法 studentRepository.updateHomeAddress("上海", 12L); }
3.6.3、查询数据
@Test public void query() { //根据 id 查询 Optional<Student> optional = studentRepository.findById(12L); logger.info("student={}", optional.get()); //查询所有 List<Student> list = studentRepository.findAll(); logger.info("list={}", list); //分页及排序查询 List<Sort.Order> orders = new ArrayList<>(); orders.add(new Sort.Order(Sort.Direction.DESC, "id")); orders.add(new Sort.Order(Sort.Direction.ASC, "name")); Page<Student> page = studentRepository.findAll(PageRequest.of(0, 10, Sort.by(orders))); logger.info("page.getTotalElements={},page.getTotalPages()={},page.toList()={}", page.getTotalElements(), page.getTotalPages(), page.toList()); //通过 JPA 标准 API 查询,可以用来动态拼接查询条件 Specification<Student> specification = new Specification<Student>() { @Override public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) { List<Predicate> list = new ArrayList<>(); list.add(criteriaBuilder.equal(root.get("name"), "小红")); list.add(criteriaBuilder.ge(root.get("age"), 10)); return criteriaBuilder.and(list.toArray(new Predicate[]{})); } }; list = studentRepository.findAll(specification); logger.info("list={}", list); list = studentRepository.findByNameAndAge("小红", 18); logger.info("list={}", list); list = studentRepository.findTop5ByNameAndAge("小红", 18); logger.info("list={}", list); list = studentRepository.findByNameAndAgeOrderByIdAsc("小红", 18); logger.info("list={}", list); Student student = studentRepository.findFirstByNameAndAge("小红", 18); logger.info("student={}", student); student = studentRepository.query("小红", 18); logger.info("student={}", student); student = studentRepository.query2("小红", 18); logger.info("student={}", student); student = studentRepository.queryBysql("小红", 18); logger.info("student={}", student); }
3.6.4、删除数据
@Test public void remove() { //根据实体删除 Student student = new Student(); student.setId(3L); studentRepository.delete(student); List<Student> students = new ArrayList<>(); Student student2 = new Student(); student.setId(4L); students.add(student); students.add(student2); studentRepository.deleteall(students); //根据 id 删除 studentRepository.deleteById(5L); List<Long> ids = new ArrayList<Long>(){{ add(6L); add(7L); }}; studentRepository.deleteallByIdInBatch(ids); }
3.6.5、完整代码
package com.abc.demojpa.service; import com.abc.demojpa.dao.IStudentRepository; import com.abc.demojpa.entity.Student; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBoottest; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.test.annotation.Commit; import org.springframework.test.context.junit4.springrunner; import org.springframework.transaction.annotation.Transactional; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import java.util.ArrayList; import java.util.List; import java.util.Optional; @RunWith(springrunner.class) @SpringBoottest public class TestService { private static final Logger logger = LoggerFactory.getLogger(TestService.class); @Autowired private IStudentRepository studentRepository; /** * 增加数据 * Spring Boot 事务测试时默认会回滚操作避免产生测试数据,如果不需要回滚可使用 @Commit 注解 */ @Test @Transactional @Commit public void add() { Student student = new Student(); student.setName("小明"); student.setAge(15); student.setHomeAddress("江苏"); Student student2 = new Student(); student2.setName("小红"); student2.setAge(18); student2.setHomeAddress("广东"); studentRepository.save(student); studentRepository.save(student2); } /** * 修改数据 */ @Transactional @Commit @Test public void modify() { Student student = new Student(); student.setId(3L); student.setName("小明2"); student.setAge(15); student.setHomeAddress("江苏"); //设置了 id 表示更新数据 studentRepository.save(student); //调用自定义的更新方法 studentRepository.updateHomeAddress("上海", 12L); } /** * 查询数据 */ @Test public void query() { //根据 id 查询 Optional<Student> optional = studentRepository.findById(12L); logger.info("student={}", optional.get()); //查询所有 List<Student> list = studentRepository.findAll(); logger.info("list={}", list); //分页及排序查询 List<Sort.Order> orders = new ArrayList<>(); orders.add(new Sort.Order(Sort.Direction.DESC, "id")); orders.add(new Sort.Order(Sort.Direction.ASC, "name")); Page<Student> page = studentRepository.findAll(PageRequest.of(0, 10, Sort.by(orders))); logger.info("page.getTotalElements={},page.getTotalPages()={},page.toList()={}", page.getTotalElements(), page.getTotalPages(), page.toList()); //通过 JPA 标准 API 查询,可以用来动态拼接查询条件 Specification<Student> specification = new Specification<Student>() { @Override public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) { List<Predicate> list = new ArrayList<>(); list.add(criteriaBuilder.equal(root.get("name"), "小红")); list.add(criteriaBuilder.ge(root.get("age"), 10)); return criteriaBuilder.and(list.toArray(new Predicate[]{})); } }; list = studentRepository.findAll(specification); logger.info("list={}", list); list = studentRepository.findByNameAndAge("小红", 18); logger.info("list={}", list); list = studentRepository.findTop5ByNameAndAge("小红", 18); logger.info("list={}", list); list = studentRepository.findByNameAndAgeOrderByIdAsc("小红", 18); logger.info("list={}", list); Student student = studentRepository.findFirstByNameAndAge("小红", 18); logger.info("student={}", student); student = studentRepository.query("小红", 18); logger.info("student={}", student); student = studentRepository.query2("小红", 18); logger.info("student={}", student); student = studentRepository.queryBysql("小红", 18); logger.info("student={}", student); } /** * 删除数据 */ @Test public void remove() { //根据实体删除 Student student = new Student(); student.setId(3L); studentRepository.delete(student); List<Student> students = new ArrayList<>(); Student student2 = new Student(); student.setId(4L); students.add(student); students.add(student2); studentRepository.deleteall(students); //根据 id 删除 studentRepository.deleteById(5L); List<Long> ids = new ArrayList<Long>(){{ add(6L); add(7L); }}; studentRepository.deleteallByIdInBatch(ids); } }TestService.java
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。