Spring Data学习 04:Spring Data JPA
2019-05-01 本文已影响8人
我问你瓜保熟吗
Spring Data 学习 01 :JDBC 访问 MySQL
Spring Data 学习 02 :Spring JdbcTemplate 访问 MySQL
Spring Data 学习 03 :JPA
Spring Data 学习 04:Spring Data JPA
一、什么是Spring Data JPA:
- 是更大的Spring Data家族的一部分
- 对基于JPA的数据访问层的增强支持
- 更易于构建基于使用Spring数据访问技术栈的应用程序
二、Jpa、Hibernate、Spring Data Jpa三者之间的关系
JPA是ORM规范,它仅仅定义了一些接口。Hibernate、TopLink是实现了JPA规范的ORM框架,具体实现了JPA定义的接口。Spring Data JpaJPA 是对JPA规范的再次封装抽象(Repository层的实现)。Spring Data JPA的核心目标之一是减少代码并简化数据访问层
三、Repository
-
Repository是Spring Data的核心接口,不提供任何方法,包含JpaRepository ,PagingAndSortingRepository等子接口。
定义:
public interface Repository<T, ID extends Serializable>{
}
使用:User是实体类,int代表User的id的类型
添加注解和继承Repository效果一样
// @RepositoryDefinition(domainClass = User.class, idClass = int.class)
public interface UserRepository extends Repository<User, int>{
public User findByName(String name);
}
Repository同Serializable一样是一个空接口,没有包含方法声明的接口被称之为
标记接口
-
CrudRepository: 继承Repository ,提供基本的增删改查
public interface CrudRepository<T,ID extends Serializable>
extends Repository<T,ID> {
<S extends T> S save(S entity);
T findOne(ID primaryKey);
Iterable<T> findAll();
Long count();
void delete(T entity);
boolean exists(ID primaryKey);
}
-
PagingAndSortingRepository ,继承 CrudRepository ,实现 分页、排序:
public interface PagingAndSortingRepository<T,ID extends Serializable>
extends CrudRepository<T,ID> {
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
}
-
JpaRepository :继承PagingAndSortingRepository ,具有CrudRepository和PagingAndSortingRepository的所有函数,实现JPA的相关规范,例如刷新持久化上下文和批量删除记录
四、根据方法名创建自定义查询
- 实例
public interface PersonRepository extends JpaRepository<User,Long>{
List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
// 启用 distinct 标志(去重:放在前面后面都可以)
List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);
// 给独立的属性启用 ignore case(忽略大小写)
List<Person> findByLastnameIgnoreCase(String lastname);
// 给所有合适的属性启用 ignore case
List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);
// 启用 ORDER BY(排序)
List<Person> findByLastnameOrderByFirsnameAsc(String lastname);
List<Person> findByLastnameOrderByFirsnameDesc(String lastname);
}
-
Spring Data JPA 自定义查询接口约定命名规则:
五、实体类注解
数据库实体类是一个 POJO Bean 对象。
1、常用注解解释:
* @Entity 用来标示这个类是实体类
* @Table 实体类与数据表的映射,通过name确定在表名(默认类名为表名)比如类Users 和表 users;
* @Id 主键注解,表示该字段为主键
* @GeneratedValue 定义主键规则,默认AUTO
* @Column 类属性与表字段映射注解,其中可以设置在字段名,长度等信息
* @ManyToOne 多对一,可以设置数据加载方式等 默认加载方式是EAGER 就是使用left join
* @OneToMany 一对多 默认加载方式是 LAZY 懒加载
* @JoinColumn 与*对*配合使用,用来设置外键名等信息
* @Basic 实体类中会默认为每一个属性加上这个注解,表示与数据表存在关联,
* 没有使用Column注解的类属性会以属性名作为字段名,驼峰命名需要转为_
* @Temporal 对于Date属性的格式化注解,有 TIME,DATE,TIMESTAMP 几个选择
* @Transient 标注该属性不做与表的映射(原因:可能表中没有该属性对应的字段)有该注解,在执行sql语句时,就不会出现该属性,否则会有,若表中没有该字段则会报错。若存在不想与数据表映射的属性,则需要加上该注解,
2、主键上的注解
// 1、id生成策略自动增长
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private String id;
// 2、配置uuid,本来jpa是不支持uuid的,但借用hibernate的方法
@Id
@GeneratedValue(generator = "uuid") 可以实现。
@GenericGenerator(name = "uuid", strategy = "uuid")
private String id;
- UUID可以自动生成一个36字符组成的字符串,UUID是由10个阿拉伯数字加上26个字母组成,8-4-4-4-12的形式组成,例如:4c47cf4a-a55a-4fce-8cd9-024a790714b010。
- 产生UUID的方法:String uuid=UUID.randomUUID().toString();
六、依赖与配置
- gradle:build.gradle
// 添加 Spring Data JPA依赖
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// 添加 MySQL连接驱动依赖
runtimeOnly 'mysql:mysql-connector-java'
- maven:pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
- 配置:application.properties
#通用数据源配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/student?charset=utf8mb4&useSSL=false
spring.datasource.username=root
spring.datasource.password=root
# Hikari 数据源专用配置
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
# JPA 相关配置
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect // 在 SrpingBoot 2.0 版本中,Hibernate 创建数据表的时候,默认的数据库存储引擎选择的是 MyISAM ?这个参数是在建表的时候,将默认的存储引擎切换为 InnoDB.
spring.jpa.show-sql = true // 控制台显示sql
spring.jpa.hibernate.ddl-auto=create // 只能开发中使用:删除数据,重新创建表结构。
七、实战操作
1、建数据库student和表student
CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
2、建立数据库实体类
package com.example.demo.Entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int age;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
3、创建数据库持久层
package com.example.demo.Repository;
import com.example.demo.Entity.Student;
import org.springframework.data.jpa.repository.JpaRepository;
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 StudentRepository extends JpaRepository<Student, Long> {
// 利用注解自定义查询预警
@Query("select o from Student o where id = (select max(id) from Student o)") // Student 是对象
public Student getStudentByMaxId();
// 根据索引参数进行查询
@Query("select o from Student o where o.name=?1 and o.age=?2")
public List<Student>queryParams(String name, int age);
// 根据别名参数进行查询
@Query("select o from Student o where o.name=:name and o.age=:age")
public List<Student>queryParams2(@Param("name")String name,@Param("age") int age);
// 原生SQL语句查询
@Query(nativeQuery = true,value = "select count(1) from student") // student是表名
public long getCount();
// @query注解 修改、新增、删除等,要添加@Modifying注解,要在Service里面处理,并增加事务注解 @Transactional
@Modifying
@Query("update Student o set o.age = :age where o.id = :id")
public void update(@Param("id") Long id, @Param("age") int age);
}
4、创建Service
增删改操作需要事务支持,事务要放在Service层,一个service可能要调用多个dao调用,要保证多个调用在一个事务里。
package com.example.demo.Service;
import com.example.demo.Repository.StudentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
@Service
public class StudentService {
@Autowired
private StudentRepository studentRepository;
@Transactional
public void update(Long id, int age){
studentRepository.update(id,age);
}
}
5、对持久层 增删改查等操作 进行单元测试
package com.example.demo;
import com.example.demo.Entity.Student;
import com.example.demo.Repository.StudentRepository;
import com.example.demo.Service.StudentService;
import org.junit.Test;
import org.junit.runner.RunWith;
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.test.context.junit4.SpringRunner;
import java.util.ArrayList;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
@Autowired
private StudentRepository studentRepository;
@Autowired
private StudentService studentService;
// 自建操作测试
@Test
public void getByMaxId(){
Student student = studentRepository.getStudentByMaxId();
System.out.println("id:"+student.getId()+"name:"+student.getName()+"age:"+student.getAge());
}
@Test
public void queryParams(){
List<Student> students = studentRepository.queryParams("护士", 24);
for (Student student:students){
System.out.println("id:"+student.getId()+"name:"+student.getName()+"age:"+student.getAge());
}
}
@Test
public void queryParams2(){
List<Student> students = studentRepository.queryParams("护士", 24);
for (Student student:students){
System.out.println("id:"+student.getId()+"name:"+student.getName()+"age:"+student.getAge());
}
}
@Test
public void getCount(){
long count = studentRepository.getCount();
System.out.println(count);
}
@Test
public void update(){
studentService.update(1L,19);
}
// 自带增加、删除、查询、分页、排序 测试
@Test
public void add() {
// 增加一个
Student student = new Student();
student.setName("老师");
student.setAge(23);
studentRepository.save(student);
}
@Test
public void delete() {
// 增加一个
Student student = new Student();
student.setName("老师");
student.setAge(23);
studentRepository.deleteById(1L);
}
@Test
public void findall(){
// 查询所有
ArrayList<Student> all = new ArrayList<>();
for (Student student:studentRepository.findAll()){
System.out.println("id:"+student.getId()+"name:"+student.getName()+"age:"+student.getAge());
}
}
// 分页和排序
@Test
public void testPage(){
Sort sort= new Sort(Sort.Direction.DESC, "id");
PageRequest Request = PageRequest.of(0,5, sort);
Page<Student> allPage = studentRepository.findAll(Request);
System.out.println("查询总页数"+allPage.getTotalPages());
System.out.println("查询总记录数"+allPage.getTotalElements());
System.out.println("查询当前是第几页"+(allPage.getNumber()+1));
System.out.println("查询当前页面的内容集合"+allPage.getContent());
System.out.println("查询当前页面的记录数"+allPage.getNumberOfElements());
}
}