因为自己一直使用的是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.
代码
配置文件 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()));
}
}