Springboot 之使用 Spring data JPA

因为自己一直使用的是MyBatis,所以尝试了一下JPA。

关于spring data JPA

  • JPA 是Java 5 引入的一个标准。
  • Spring data JPA 是Spring对JPA标准的实现,实际上使用的是Hibernate。
  • Spring data JPA封装好了一些API让开发者按照标准编写一些类直接继承这些API并使用它们。
  • 开发者在调用这些API的时候,Spring会根据API名称和参数动态生成SQL交给ORM框架Hibernate去执行SQL并完成结果的封装

我个人觉得公司的项目可以考虑使用Spring data JPA,毕竟支持快速开发,还可以缩短开发周期。但是个人学习的时候,建议使用MyBatis这种半自动的ORM。毕竟你学会了Spring data JPA 只是学会了Spring data JPA。你学会使用MyBatis的时候,SQL也学会了。

Spring data JPA 的优缺点

  • 优点
    • 快速开发,减少了大量的SQL编写
    • 可以满足大部分单表操作和简单联查
  • 缺点
    • 由于是一些通用sql,遇到一些特定的业务需求就显得很笨,还不如直接手写sql

简单使用

新建一个springboot项目

我使用的是OpenJDK 11
选择spring data jpa, lombok, mysql-connector

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>

准备数据

新建两个表,一个是学生信息表,一个是学生成绩表,通过id逻辑关联

CREATE DATABASE /*!32312 IF NOT EXISTS*/ `test` /*!40100 DEFAULT CHARACTER SET utf8mb4 */; USE `test`; DROP TABLE IF EXISTS `t_student`; CREATE TABLE `t_student` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'primary key', `created_time` datetime DEFAULT NULL COMMENT 'created time', `updated_time` datetime DEFAULT NULL COMMENT 'updated time', `name` varchar(20) DEFAULT NULL COMMENT '姓名', `birthday` date DEFAULT NULL COMMENT '生日', `address` varchar(200) DEFAULT NULL COMMENT '家庭住址', `phone` varchar(20) DEFAULT NULL COMMENT '联系方式', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='学生信息表'; DROP TABLE IF EXISTS `t_student_score`; CREATE TABLE `t_student_score` ( `id` int(11) NOT NULL COMMENT 'primary key', `created_time` datetime DEFAULT NULL COMMENT 'created tiem', `updated_time` datetime DEFAULT NULL COMMENT 'updated tiem', `math` double DEFAULT '0' COMMENT '数学', `english` double DEFAULT '0' COMMENT '英语', `yuwen` double DEFAULT '0' COMMENT '语文', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COMMENT='学生成绩表'; INSERT INTO `t_student` (`id`,`created_time`,`updated_time`,`name`,`birthday`,`address`,`phone`) VALUES (1,'2020-11-03 11:43:06','2020-11-03 11:43:06','张三','2001-01-01','江西赣州','188888888'), (2,'2020-11-03 11:43:06','2020-11-03 11:43:06','李四','2000-05-01','广东韶关','177888888'), (3,'2020-11-03 11:43:06','2020-11-03 11:43:06','王老五','2002-03-01','江苏南京','199888888'), (4,'2020-11-03 11:43:06','2020-11-03 11:43:06','葛二蛋','2000-12-01','四川成都','166888888'); INSERT INTO `t_student_score` (`id`,`created_time`,`updated_time`,`math`,`english`,`yuwen`) VALUES (1,'2020-11-03 14:08:52','2020-11-03 14:08:52',93,24.5,85), (2,'2020-11-03 14:08:52','2020-11-03 14:08:52',85,67.5,82), (3,'2020-11-03 14:08:52','2020-11-03 14:08:52',22.5,93.5,70), (4,'2020-11-03 14:08:52','2020-11-03 14:08:52',88,76.5,65);

项目结构

我使用的是VS Code.
image.png

代码

配置文件 application.yml

spring: jpa: properties: hibernate: enable_lazy_load_no_trans: true # 解决 getOne 报错问题 datasource: url: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8 driver-class-name: com.mysql.cj.jdbc.Driver username: root password: password hikari: minimumIdle: 5 maximumPoolSize: 15 max-lifetime: 60000

Entity

  • Student
package com.yeyeck.springdatajpa.entity; import java.time.LocalDate; import java.time.LocalDateTime; 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; import lombok.Data; @Data @Table(name = "t_student") @Entity public class Student { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private LocalDateTime createdTime; private LocalDateTime updatedTime; private String name; private String address; private LocalDate birthday; private String phone; @OneToOne @JoinColumn(name="id", referencedColumnName = "id") private Score score; }
  • Score
package com.yeyeck.springdatajpa.entity; import java.time.LocalDateTime; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.Data; @Entity @Table(name = "t_student_score") @Data public class Score { @Id private Integer id; private Double math; private Double yuwen; private Double english; private LocalDateTime createdTime; private LocalDateTime updatedTime; }

Repository

这个就比较简单了,大部分方法是调用父接口的方法。Repository<T, ID>主要有四种依次为父子关系

  • Repository<T, ID>:纯粹的接口声明,没有一个方法
  • CrudRepository<T, ID>:简单的CRUD
  • PagingAndSortingRepository<T, ID>: 实现分页
  • JpaRepository<T, ID>:继承了两个接口PagingAndSortingRepository和QueryByExampleExecutor
  • QueryByExampleExecutor:实现通过定制Example进行查询,我看了一下Example,应该是一些比较繁琐的查询sql主要的承担者
package com.yeyeck.springdatajpa.repository; import java.time.LocalDate; import java.util.List; import com.yeyeck.springdatajpa.entity.Student; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface StudentRepository extends JpaRepository<Student, Integer> { List<Student> findByBirthdayBetween(LocalDate of, LocalDate of2); }

单元测试

package com.yeyeck.springdatajpa.repository; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; import com.yeyeck.springdatajpa.entity.Student; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.Sort; import lombok.extern.slf4j.Slf4j; @SpringBootTest @Slf4j public class StudentRepositoryTest { @Autowired StudentRepository studentRepository; @Test public void findAll() { List<Student> students = studentRepository.findAll(); log.info(students.toString()); } @Test public void findAllAndSort() { List<Student> students = studentRepository.findAll(Sort.by("birthday").descending()); log.info(students.toString()); } @Test public void findAllByIds() { List<Student> students = studentRepository.findAllById(List.of(1,3,4)); log.info(students.toString()); } @Test public void findById() { Student student = studentRepository.findById(1).get(); log.info(student.toString()); } @Test public void getOne() { Student student = studentRepository.getOne(1); log.info(student.toString()); } @Test public void save() { Student student = new Student(); student.setName("赵四"); student.setPhone("166666666"); student.setAddress("新疆乌鲁木齐"); student.setBirthday(LocalDate.of(2000, 3, 1)); student.setCreatedTime(LocalDateTime.now()); student.setUpdatedTime(LocalDateTime.now()); studentRepository.save(student); log.info(student.toString()); // Student(id=5, createdTime=2020-11-03T16:15:30.662052800, updatedTime=2020-11-03T16:15:30.662052800, name=赵四, address=新疆乌鲁木齐, birthday=2000-03-01, phone=166666666, score=null) } @Test public void findByBirthdayBetween() { List<Student> students = studentRepository.findByBirthdayBetween(LocalDate.of(2001, 1, 1), LocalDate.of(2002, 5, 1)); students.forEach(student -> log.info(student.toString())); } }
阅读(44)
评论(0)
updated@2020-11-04
评论区
目录