首页 > 其他分享 >Spring Data JPA 入门

Spring Data JPA 入门

时间:2023-05-28 22:55:23浏览次数:57  
标签:name JPA Spring private public Student desk Data id

注解说明

  1. @Entity(name = "")类注解,用来注解该类是一个实体类并用来和数据库中的表建立关联关系。其中name表示该表的名称
  2. @Table(name = "") 类注解,跟@Entity(name = "")作用一致
  3. @Id属性注解,该注解表明该属性字段是一个主键,该属性必须具备,不可缺少
  4. @GeneratedValue(strategy = ,generator = "")@Id主键注解一起使用,用来定义主键的呈现形式。其中strategy表示JPA通用的主键策略生成器,generator表示使用指定的主键生成器时,设置生成器的名称(即@GenericGenerator注解的name值)
    1. @GeneratedValue(strategy= GenerationType.IDENTITY) 该注解由数据库自动生成,主键自增型,在MySql数据库中使用最频繁,Oracle不支持
    2. @GeneratedValue(strategy= GenerationType.AUTO) 主键由程序控制,默认的主键生成策略,Oracle默认是序列化的方式,MySql默认是主键自增的方式
    3. @GeneratedValue(strategy= GenerationType.SEQUENCE) 根据底层数据库的序列来生成主键,条件是数据库支持序列,Oracle支持,MySql不支持
    4. @GeneratedValue(strategy= GenerationType.TABLE) 使用一个特定的数据库表格来保存主键,较少使用
  5. @GenericGenerator(name = "",strategy = "uuid") 自定义主键生成策略,其中name表示生成器的名称,strategy表示预定义的 Hibernate 策略或完全限定的类名
  6. @Column(name = "",nullable = ,columnDefinition = "") 类的属性注解,可以定义一个字段映射到数据库属性的具体特征(比如字段长度、非空、唯一、字段注释等)
  7. @Transient类的属性注解,该注解标注的字段不会被映射到数据库当中
  8. @JoinColum(name = "",referencedColumnName = "")保存表与表之间关系的字段,它要标注在实体属性上。一般修饰在主控方,用来定义一对一,一对多,多对多等关系。配合实体关系注解使用
    1. 关联的实体的主键一般是用来做外键的。但如果不想主键作为外键,则需要设置referencedColumnName属性
  9. @JoinTable(name = "", joinColumns = @JoinColumn(name = "",referencedColumnName = ""), inverseJoinColumns = @JoinColumn(name = "",referencedColumnName = ""))用于构建一对多,多对多时的连接表,默认会以主表加下划线加外键表为表名
    1. name中间表的表名称
    2. joinColumns = @JoinColumn(name = "",referencedColumnName = "")指明当前对象哪个列(referencedColumnName的值)和中间表哪个列(name的值)对应
    3. inverseJoinColumns = @JoinColumn(name = "",referencedColumnName = "")指明依赖对象哪个列(referencedColumnName的值)和中间表哪个列(name的值)对应

实体关系注解说明

  1. @OneToOne(cascade = {},fetch = {},mappedBy = "")实体间一对一的关系。实现的方式有外键关联中间表保存关联关系

    1. cascade当前类对象操作了之后,级联对象的操作
      1. REMOVE级联删除操作。删除当前实体时,与它有映射关系的实体也会跟着被删除
      2. MEGER级联更新(合并)操作。当前对象中的数据改变,会相应地更新级联对象中的数据
      3. DETACH级联脱管/游离操作。如果要删除一个实体,但是它有外键无法删除,就需要这个级联权限了。它会撤销所有相关的外键关联
      4. REFRESH级联刷新操作。更新数据前先刷新对象和级联对象,再更新
      5. PERSIST级联持久化(保存)操作。持久保存拥有方实体时,也会持久保存该实体的所有相关数据
      6. ALL当前类增删改查改变之后,关联类跟着增删改查,拥有以上所有级联操作权
    2. fetch关联对象的立即加载和延迟加载
      1. FetchType.LAZY懒加载
      2. FetchType.EAGER立即加载
    3. mappedBy表示该类放弃外键的维护,是关系被维护端。其中mappedBy的值是被拥有类中的属性名

    一对一的关系实例如下:一个学生拥有一张书桌的,一张书桌属于一个学生的

    @Getter
    @Setter
    @Entity
    @NoArgsConstructor
    @Table(name = "desk")
    public class Desk implements Serializable {
    
       @Id
       @GeneratedValue(strategy = GenerationType.IDENTITY)
       private Long id;
    
       @Column(name = "identifier", length = 20)
       private String identifier;
    
       /**
        * mappedBy = "desk" 表明该实体表放弃外键的维护。其中 mappedBy 的值是 Student 类中的属性
        */
       @OneToOne(mappedBy = "desk")
       private Student student;
    }
    
    @Getter
    @Setter
    @Entity
    @NoArgsConstructor
    @Table(name = "student")
    public class Student implements Serializable {
    
       @Id
       @GeneratedValue(strategy = GenerationType.IDENTITY)
       private Long id;
    
       @Column(name = "name", length = 100)
       private String name;
    
       /**
        * name = "desk_id" 设置外键在表中的字段名
        * nullable = false 说明该外键的值在表中不允许为 null
        */
       @OneToOne(fetch = FetchType.EAGER)
       @JoinColumn(nullable = false, name = "desk_id")
       private Desk desk;
    }
    
  2. @OneToMany@ManyToOne实体一对多和多对一的双向关系。一端(One)使用@OneToMany,多端(Many)使用@ManyToOne

    1. 在JPA规范中,一对多的双向关系由多端(Many)来维护,即多端(Many)负责关系的CRUD
    2. 一端(One)则为关系被维护端,不能维护关系,使用@OneToManymappedBy属性表明是关系的被维护端,值是被拥有类中的属性名
    3. 多端(Many)使用@ManyToOne表明是多端,使用@JoinColum设置在表中关联的字段(外键)

    一对多和多对一的双向关系实例如下:多个学生属于一间教室,一间教室属于多名学生

    @Getter
    @Setter
    @Entity
    @NoArgsConstructor
    @Entity
    @Table(name = "student")
    public class Student implements Serializable {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column(name = "name",length = 100)
        private String name;
    
        /**
         * name = "class_room_id" 设置外键在表中的字段名
         * nullable = false 说明该外键的值在表中不允许为 null
         */
        @ManyToOne
        @JoinColumn(nullable = false,name = "class_room_id",referencedColumnName = "class_no")
        private ClassRoom classRoom;
    }
    
    @Getter
    @Setter
    @Entity
    @NoArgsConstructor
    @Entity
    @Table(name = "class_room")
    public class ClassRoom implements Serializable {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column(name = "class_no")
        private String classNo;
    
        /**
         * mappedBy = "classRoom" 表明该实体表放弃外键的维护。其中 mappedBy 的值是 Student 类中的属性
         */
        @OneToMany(mappedBy = "classRoom")
        private Set<Student> students;
    }
    
  3. @ManyToMany实体间多对多的关系。由一个中间表来维护关系,表名默认为 主表名_从表名

    1. 多对多关系中一般不设置级联保存、级联删除、级联更新等操作
    2. 使用@JoinTable注解,中间表会按照注解指定的方式生成

    多对多的关系实例如下:一个教师有多名学生,一个学生有多个教师

    @Getter
    @Setter
    @Entity
    @NoArgsConstructor
    @Entity
    @Table(name = "student")
    public class Student implements Serializable {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column(name = "name",length = 100)
        private String name;
    
        /**
         * 在多对多的关系中,需要使用中间表来维护双方的关系,通过@JoinTable定义中间表
         *  name = "student_teacher" 中间表的表名称
         *  joinColumns = @JoinColumn(name = "student_id",referencedColumnName = "id") 指明当前对象哪个列(referencedColumnName的值)和中间表哪个列(name的值)对应
         *  inverseJoinColumns = @JoinColumn(name = "teacher_id",referencedColumnName = "id") 指明依赖对象哪个列(referencedColumnName的值)和中间表哪个列(name的值)对应
         */
        @ManyToMany
        @JoinTable(name = "student_teacher",
                joinColumns = @JoinColumn(name = "student_id",referencedColumnName = "id"),
                inverseJoinColumns = @JoinColumn(name = "teacher_id",referencedColumnName = "id"))
        private Set<Teacher> teachers;
    }
    
    @Getter
    @Setter
    @Entity
    @NoArgsConstructor
    @Table(name = "teacher")
    public class Teacher implements Serializable {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column(name = "name",length = 100)
        private String name;
    
        /**
         * mappedBy = "teacher" 表明该实体表放弃外键的维护。其中 mappedBy 的值是 Student 类中的属性
         */
        @ManyToMany(mappedBy = "teachers")
        private Set<Student> students;
    }
    

级联操作

级联在关系映射注解中指主动方对象执行操作(增删改)时,被关联对象(关系被维护段)是否同步执行统一操作
四个关系注解 @OneToMany@ManyToOne@OneToOne@ManyToMany 中都有一个属性cascade,通过该属性维护级联关系(默认值是default不存在级联操作):

  1. CascadeType.PERSIST级联是级联保存

    @Builder
    @Getter
    @Setter
    @AllArgsConstructor
    @Entity
    @Table(name = "student")
    public class Student implements Serializable {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        /**
         * @JoinColumn(nullable = false,name = "desk_id")
         *  name = "desk_id" 设置外键在表中的字段名
         *  nullable = false 说明该外键的值在表中不允许为 null
         *
         * @OneToOne(fetch = FetchType.LAZY,cascade = CascadeType.PERSIST)
         *  fetch = FetchType.LAZY 采用懒加载方式
         *  cascade = CascadeType.PERSIST 级联保存,当保存学生实体时,会同步保存课桌实体
         */
        @OneToOne(fetch = FetchType.LAZY,cascade = CascadeType.PERSIST)
        @JoinColumn(nullable = false,name = "desk_id")
        private Desk desk;
        
        @Tolerate
        public Student() { }
    }
    
    @Builder
    @Getter
    @Setter
    @Entity
    @Table(name = "desk")
    public class Desk implements Serializable {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column(name = "identifier",length = 20)
        private String identifier;
    
        /**
         * mappedBy = "desk" 表明该实体表放弃外键的维护。其中 mappedBy 的值是 Student 类中的属性
         */
        @OneToOne(mappedBy = "desk")
        private Student student;
    
        @Tolerate
        public Desk() { }
    }
    
    /***级联保存实例保存student对象同时保存desk对象*/
    @Test
    @Transactional
    public void saveStudent(){
      Student student = Student.builder().name("wen").age(23).birthday(LocalDate.now()).build();
      Desk desk = Desk.builder().identifier("456").build();
      student.setDesk(desk);
      studentRepository.save(student);
    }
    
    /***单纯保存desk不影响student*/
    @Test
    public void saveDesk(){
      Desk desk = Desk.builder().identifier("123").build();
      deskRepository.save(desk);
    }
    
  2. CascadeType.MERGE 级联更新

    @Builder
    @Getter
    @Setter
    @AllArgsConstructor
    @Entity
    @Table(name = "student")
    public class Student implements Serializable {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        /**
         * @JoinColumn(nullable = false,name = "desk_id")
         *  name = "desk_id" 设置外键在表中的字段名
         *  nullable = false 说明该外键的值在表中不允许为 null
         *
         * @OneToOne(fetch = FetchType.LAZY,cascade = CascadeType.MERGE)
         *  fetch = FetchType.LAZY 采用懒加载方式
         *  cascade = CascadeType.MERGE 级联更新,当保存更新的学生实体中又对Desk对象修改时,会同步保存课桌实体
         */
        @OneToOne(fetch = FetchType.LAZY,cascade = CascadeType.MERGE)
        @JoinColumn(nullable = false,name = "desk_id")
        private Desk desk;
        
        @Tolerate
        public Student() { }
    }
    
    @Builder
    @Getter
    @Setter
    @Entity
    @Table(name = "desk")
    public class Desk implements Serializable {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column(name = "identifier",length = 20)
        private String identifier;
    
        /**
         * mappedBy = "desk" 表明该实体表放弃外键的维护。其中 mappedBy 的值是 Student 类中的属性
         */
        @OneToOne(mappedBy = "desk")
        private Student student;
    
        @Tolerate
        public Desk() { }
    }
    
    /***级联更新实例student对象同时更新desk对象*/
    @Test
    @Transactional
    @Commit
    public void updateStudent() {
      Optional<Student> student = studentRepository.findById(1L);
      student.ifPresent(stu->{
          stu.setName("cheng");
          Desk desk = stu.getDesk();
          desk.setIdentifier("1001-11");
          studentRepository.save(stu);
      });
    }
    
  3. CascadeType.REMOVE 级联删除

    @Builder
    @Getter
    @Setter
    @AllArgsConstructor
    @Entity
    @Table(name = "student")
    public class Student implements Serializable {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        /**
         * @JoinColumn(nullable = false,name = "desk_id")
         *  name = "desk_id" 设置外键在表中的字段名
         *  nullable = false 说明该外键的值在表中不允许为 null
         *
         * @OneToOne(fetch = FetchType.LAZY,cascade = CascadeType.REMOVE)
         *  fetch = FetchType.LAZY 采用懒加载方式
         *  cascade = CascadeType.REMOVE 级联删除,当删除学生实体时,会同步删除课桌实体
         */
        @OneToOne(fetch = FetchType.LAZY,cascade = CascadeType.REMOVE)
        @JoinColumn(nullable = false,name = "desk_id")
        private Desk desk;
        
        @Tolerate
        public Student() { }
    }
    
    @Builder
    @Getter
    @Setter
    @Entity
    @Table(name = "desk")
    public class Desk implements Serializable {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column(name = "identifier",length = 20)
        private String identifier;
    
        /**
         * mappedBy = "desk" 表明该实体表放弃外键的维护。其中 mappedBy 的值是 Student 类中的属性
         */
        @OneToOne(mappedBy = "desk")
        private Student student;
    
        @Tolerate
        public Desk() { }
    }
    
    /***级联删除实例student对象同时删除desk对象*/
    @Test
    @Transactional
    @Commit
    public void deleteStudent() {
      studentRepository.deleteById(1L);
    }
    
  4. CascadeType.REFRESH 级联刷新

  5. CascadeType.DETACH 级联托管

  6. CascadeType.ALL 具有上述五个级联的功能

    注意点:@OneToOne@OneToMany中存在一个orphanRemoval属性,表示当关联关系被删除的时候,是否级联删除

    @Builder
    @Getter
    @Setter
    @AllArgsConstructor
    @Entity
    @Table(name = "student")
    public class Student implements Serializable {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column(name = "name",length = 100)
        private String name;
    
        /**
         * name = "class_room_id" 设置外键在表中的字段名
         * nullable = false 说明该外键的值在表中不允许为 null
         */
        @ManyToOne(cascade = CascadeType.ALL)
        @JoinColumn(nullable = false,name = "class_room_id",referencedColumnName = "class_no")
        private ClassRoom classRoom;
    
        @Tolerate
        public Student() {
    
        }
    }
    
    @Getter
    @Setter
    @Builder
    @Entity
    @Table(name = "class_room")
    public class ClassRoom implements Serializable {
    
       @Id
       @GeneratedValue(strategy = GenerationType.IDENTITY)
       private Long id;
    
       @Column(name = "class_no")
       private String classNo;
    
       /**
        * mappedBy = "classRoom" 表明该实体表放弃外键的维护。其中 mappedBy 的值是 Student 类中的属性
        * orphanRemoval = true 表示删除ClassRoom对象前先将Student对象删除后再删除ClassRoom对象
        */
       @OneToMany(mappedBy = "classRoom",orphanRemoval = true)
       private Set<Student> students;
    
       @Tolerate
       public ClassRoom() {
    
       }
    }
    
    /***级联删除实例ClassRoom对象前先删除外键引用的Student对象*/
    @Test
    @Transactional
    @Commit
    public void deleteClassRoom() {
       classRoomRepository.deleteById(8L);
    }
    

使用JPA关键字进行CRUD操作

使用JPA进行CRUD操作时,基本不用写简单的SQL语句,通过JPA提供的JpaRepository类即可完成简单的CRUD

Spring Data JPA提供了JpaRepository,该接口继承PagingAndSortingRepositoryQueryByExampleExecutor接口

  1. PagingAndSortingRepository接口继承CrudRepository,提供了排序和分页的方法。CrudRepository接口定义了简单的CRUD方法,而JpaRepository接口对该接口进行增强操作
  2. QueryByExampleExecutor接口提供了⼀种⽤户友好的查询技术,具有简单的接⼝,它允许动态查询创建,并且不需要编写包含字段名称的查询

简单CRUD入门操作

  1. 定义DAO层,不需要写@Repository注解
    public interface StudentRepository extends JpaRepository<Student,Long> { }
    
  2. 使用JPA提供的find和get关键字完成常规的查询操作,使用delete关键字完成删除,使用count关键字完成统计等,通过跟By关键字进行字段绑定
    public interface StudentRepository extends JpaRepository<Student,Long> {
        /**根据名称获取实体*/
        Student findByName(String name);
    
        /**根据age获取实体*/
        Student getByAge(Integer age);
    
        /**根据id和名称获取实体*/
        List<Student> findByIdAndName(Long id, String name);
    
        /**删除list中包含的实体并返回影响的行数*/
        Long deleteByIdIn(List<Long> ids);
    
        /**查询统计name包含关键字的个数*/
        Long countByNameContains(String key);
    }
    
  3. 如果要对某个对象进行修改操作,需要先查询出给实体后使用save方法进行更新,或者自定义SQL的方式进行更新操作

自定义SQL语句

自定义SQL在JPA中有两种呈现形式

  1. JPQL形式的SQL语句,from 后面是以类名呈现的,?1 代表是的是方法中的第一个参数
    public interface DeskRepository extends JpaRepository<Desk,Long> {
        @Query(value = "select desk from Desk desk where desk.id in ?1")
        List<Desk> findByIdIn(List<Long> ids);
    }
    
  2. 原生的SQL语句,需要使用nativeQuery = true指定使用原生SQL,:name是通过@Param注解去绑定的
    public interface DeskRepository extends JpaRepository<Desk,Long> {
        @Query(value = "select * from desk where identifier = :identifier",nativeQuery = true)
        Desk findByIdentifier(@Param("identifier") String identifier);
    }
    
  3. 自定义SQL更新数据。@Query注解中编写JPQL实现DELETE和UPDATE操作的时候必须加上@Modifying注解,以通知Spring Data 这是一个DELETE或UPDATE操作,同时接口的返回值表示影响的行数
    @Modifying
    @Query(value = "update student set age = :age where name = :name",nativeQuery = true)
    int updateStudentAgeByName(@Param("name") String name,@Param("age") Integer age);
    

备注:

  1. SQL 中的参数传递也有两种形式:
    1. 使用问号 ?,紧跟数字序列,数字序列从1开始
    2. 使用冒号 :,紧跟参数名,参数名是通过@Param注解来绑定
  2. 注意JPQL不支持INSERT操作
  3. UPDATE或者DELETE操作需要使用事务,此时需要 定义Service层,在Service层的方法上添加事务操作

动态创建查询

QueryByExampleExecutor接口提供了⼀种⽤户友好的查询技术,具有简单的接⼝,它允许动态查询创建,并且不需要编写包含字段名称的查询

QueryByExampleExecutor使用场景:使用一组静态或动态约束来查询数据存、频繁重构域对象,而不用担心破坏现有查询、简单的查询的使用场景

public interface QueryByExampleExecutor<T> {
    // 根据实例查找一个对象
    <S extends T> Optional<S> findOne(Example<S> example);
    // 根据实例查找一批对象
    <S extends T> Iterable<S> findAll(Example<S> example);
    // 根据实例查找一批对象,且排序
    <S extends T> Iterable<S> findAll(Example<S> example, Sort sort);
    // 根据实例查找一批对象,且排序和分页
    <S extends T> Page<S> findAll(Example<S> example, Pageable pageable);
    // 根据实例查找,返回符合条件的对象个数
    <S extends T> long count(Example<S> example);
    // 根据实例查找,返回符合条件的对象
    <S extends T> boolean exists(Example<S> example);
}

public interface Example<T> {

   static <T> Example<T> of(T probe) {
      return new TypedExample<>(probe, ExampleMatcher.matching());
   }

   static <T> Example<T> of(T probe, ExampleMatcher matcher) {
      return new TypedExample<>(probe, matcher);
   }

   T getProbe();

   ExampleMatcher getMatcher();

   @SuppressWarnings("unchecked")
   default Class<T> getProbeType() {
      return (Class<T>) ProxyUtils.getUserClass(getProbe().getClass());
   }
}

从源码中可以看出 Example 主要包含三部分内容。

  1. Probe:这是具有填充字段的域对象的实际实体类,即查询条件的封装类(又可以理解为:查询条件参数),必填。
  2. ExampleMatcher:ExampleMatcher 有关于如何匹配特定字段的匹配规则,它可以重复使用在多个示例,必填;如果不填,用默认的(又可以理解为参数的匹配规则)
  3. Example:Example 由 Probe 探针和 ExampleMatcher 组成,它用于创建查询,即组合查询参数和参数的匹配规则
//创建查询条件数据对象
Student student = new Student();
student.setName("i-");
student.setAge(22);

//创建匹配器,即如何使用查询条件
ExampleMatcher matcher = ExampleMatcher.matching()
       ////name采用“开始匹配”的方式查询
       .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.startsWith())
       //忽略属性age
       .withIgnorePaths("age");


//创建实例
Example<Student> example = Example.of(student, matcher);
List<Student> studentList = studentRepository.findAll(example);
for (Student stu : studentList) {
   System.out.println(stu);
}

QueryByExampleExecutor的特点及约束:

  1. 支持动态查询,输入字段为null则忽略这个条件
  2. 不支持过滤条件分组,即不支持过滤条件用 or(或)来连接,所有的过滤条件都是简单一层的用 and(并且)连接,如 name = ?1 or (email = ?2 and email = ?3)
  3. 支持字符串的开始、包含、结束、正则匹配和其他属性类型的精确匹配,如针对于“name”的过滤也只有这么一个存储过滤值的位置,没办法同时传入两个过滤值

ExampleMatcher说明:

  1. nullHandler:Null值处理方式,枚举类型,有两个可选值,INCLUDE(包括)、IGNORE(忽略)
    1. 标识作为条件的实体对象中,一个属性值(条件值)为 Null 时,是否参与过滤
    2. 当该选项值是 INCLUDE 时,表示仍参与过滤,会匹配数据库表中该字段值是 Null 的记录
    3. 若为 IGNORE 值,表示不参与过滤
  2. defaultStringMatcher:默认字符串匹配方式,枚举类型,有 6 个可选值。该配置对所有字符串属性过滤有效,除非该属性在 propertySpecifiers 中单独定义自己的匹配方式
    1. DEFAULT(默认,效果同 EXACT)
    2. EXACT(相等)
    3. STARTING(开始匹配)
    4. ENDING(结束匹配)
    5. CONTAINING(包含,模糊匹配)
    6. REGEX(正则表达式)
  3. defaultIgnoreCase:默认大小写忽略方式,布尔型,当值为false时,即不忽略,大小写不相等
    1. 该配置对所有字符串属性过滤有效,除非该属性在 propertySpecifiers 中单独定义自己的忽略大小写方式
  4. ignoredPaths:忽略属性列表,忽略的属性不参与查询过滤
  5. propertySpecifiers: 各属性自定义查询方式包含:属性名、字符串匹配方式、大小写忽略方式、属性转换器

QueryByExampleExecutor实际中需要考虑的因素
查询条件表示有两部分: 一是条件值,二是查询方式,条件值使用实体对象存储,页面传入值按值进行过滤,未传入值则忽略

  1. null值的处理,是否需要匹配数据库中字段值为null的记录
  2. 基本类型的处理,比如int的默认值是0,为了避免不传值时,使用默认值进行查询,避免使用基本类型,采用包装类型
  3. 忽略某些属性值,实体类中的所有属性是否都需要参与查询
  4. 不同的过滤方式,查询条件判断不一样
  5. 大小写匹配

排序实现

JPA提供了一个Sort类来定义排序,排序实现方式如下

  1. 使用原生SQL来写
    public interface DeskRepository extends JpaRepository<Desk,Long> {
        @Query(value = "select * from desk where identifier = :identifier order by id desc",nativeQuery = true)
        Desk findByIdentifier(@Param("identifier") String identifier);
    }
    
  2. 使用JPQL来写
    public interface DeskRepository extends JpaRepository<Desk,Long> {
        @Query(value = "select desk from Desk desk where desk.id in ?1 order by id asc")
        List<Desk> findByIdIn(List<Long> ids);
    }
    
  3. Sort作为参数传递给方法
    public interface DeskRepository extends JpaRepository<Desk,Long> {
        @Query(value = "select desk from Desk desk where desk.id in ?1")
        List<Desk> findByIdIn(List<Long> ids,Sort sort);
    }
    
    //单字段排序
    List<Desk> desks = deskRepository.findByIdIn(List.of(3L,4L,5L), Sort.by(Sort.Direction.DESC,"id"));
    System.out.println(desks);
    
    //多字段排序
    Sort.Order order1 = new Sort.Order(Sort.Direction.DESC, "id");
    Sort.Order order2 = new Sort.Order(Sort.Direction.DESC, "identifier");
    List<Sort.Order> orders = List.of(order1, order2);
    Sort sort = Sort.by(orders);
    List<Desk> deskList = deskRepository.findByIdIn(List.of(3L,4L,5L), sort);
    System.out.println(deskList);
    
  4. 基于特殊参数的排序
    public interface DeskRepository extends JpaRepository<Desk,Long> { 
         List<Desk> findAllByOrderByIdDesc();
    }
    

JPA分页实现

PagingAndSortingRepository接口继承自CrudRepository接口提供的分页和排序方法

@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
    //排序功能。它按照Sort制定的排序返回数据
	Iterable<T> findAll(Sort sort);
    //分页查询(含排序功能)
	Page<T> findAll(Pageable pageable);
}

Pageable接口用于构造分页查询,返回Page对象。Page从0开始分页。实例化Pageable方式如下

//方式一,不排序
Pageable pageable = PageRequest.of(page, size);

//方式二,自定义字段排序
Pageable pageable = PageRequest.of(page, size, sort);

//方式三,自定义多个字段相同的排序
Pageable pageable = PageRequest.of(page, size, direction,properties);

实现方式如下:

@Query(value = "select * from desk where id in (:ids)",nativeQuery = true)
Page<Desk> findAllDeskPageById(@Param("ids") List<Long> ids, Pageable pageable);

@Query(value = "from Desk where id in (:ids)")
Page<Desk> getDeskPageById(@Param("ids") List<Long> ids, Pageable pageable);

//当前页码(注意:第一页是从0开始)
int pageIndex = 0;
//分页大小
int pageSize = 10;
Sort sort = Sort.by(Sort.Order.desc("id"));
PageRequest pageRequest = PageRequest.of(pageIndex, pageSize, sort);

//查询所有
Page<Desk> desks = deskRepository.findAll(pageRequest);
//使用原生SQL语句
Page<Desk> desks = deskRepository.findAllDeskPageById(List.of(3L,4L),pageRequest);        
//使用JPQL语句
Page<Desk> desks = deskRepository.getDeskPageById(List.of(3L,4L),pageRequest);       


System.out.println(String.join("","当前页码数:", String.valueOf(desks.getNumber() + 1)," 页"));
System.out.println(String.join("","总页数:", String.valueOf(desks.getTotalPages())," 页"));
System.out.println(String.join("","总记录数:", String.valueOf(desks.getTotalElements())," 条"));
System.out.println(String.join("","每页的条数:", String.valueOf(desks.getSize())," 条"));
System.out.println(String.join("","数据列表:", String.valueOf(desks.getContent())));

JpaSpecificationExecutor 接口

可以用于动态生成Query来满足业务中的各种复杂场景

public interface DeskRepository extends JpaRepository<Desk,Long>, JpaSpecificationExecutor<Desk> { }
@Test
@Transactional
@Commit
public void StudentByName(){

  Desk desk = Desk.builder().identifier("5").id(4L).build();

  //排序 :第一个参数是排序的规则(DESC/ASC)  后面参数是排序的字符
   Sort sort = Sort.by(Sort.Order.desc("id"));
   PageRequest pageRequest = PageRequest.of(0, 2, sort);

  /***
   * root:代表了可以查询和操作的实体对象的根,可以通过它的 Path<Y> get(String attributeName); 这个方法拿到要操作的字段
   *      注意:只可以拿到对应的T的字段(Employee)
   * criteriaQuery:代表一个specific的顶层查询对象,包含查询的各个部分,比如select,from,where,group by ,order by 等
   *      简单理解 就是它提供 了查询ROOT的方法(where,select,having)
   * criteriaBuilder:用来构建CriteriaQuery的构建器对象(相当于条件或者说条件组合),构造好后以Predicate的形式返回  
   */
  Page<Desk> desks = deskRepository.findAll((root, criteriaQuery, criteriaBuilder) -> {

      List<Predicate> predicateList = new ArrayList<>();
      if (StringUtils.hasText(desk.getIdentifier())) {
          predicateList.add(criteriaBuilder.equal(root.get("identifier"),desk.getIdentifier()));
      }

      if (desk.getId() != null) {
          predicateList.add(criteriaBuilder.equal(root.get("id"),desk.getId()));
      }

      return criteriaQuery.where(predicateList.toArray(new Predicate[0])).groupBy(root.get("id")).getRestriction();
  },pageRequest);

  System.out.println(desks.getContent());
}

参考 https://zhuanlan.zhihu.com/p/110024146

标签:name,JPA,Spring,private,public,Student,desk,Data,id
From: https://www.cnblogs.com/52-IT-y/p/17439075.html

相关文章

  • SpringCloudAlibaba整合分布式事务Seata
    目录1整合分布式事务Seata1.1环境搭建1.1.1Nacos搭建1.1.2Seata搭建1.2项目搭建1.2.1项目示意1.2.2pom.xml1.2.2.1alibaba-demo模块1.2.2.2call模块1.2.2.3order模块1.2.2.4common模块1.2.3配置文件1.2.3.1order模块1.2.3.2call模块1.2.4OpenFeign调用1.2.5order......
  • 基于JAVA的springboot+vue摄影跟拍预定管理系统,附源码+数据库+论文+PPT
    1、项目介绍困扰管理层的许多问题当中,摄影跟拍预定管理一定是不敢忽视的一块。但是管理好摄影跟拍预定又面临很多麻烦需要解决,例如有几个方面:第一,往往用户人数都比较多,如何保证能够管理到每一用户;第二,如何在工作琐碎,记录繁多的情况下将摄影跟拍预定的当前情况反应给领导......
  • springboot整合mybatis实现简单的crud操作
    使用MyBatis框架操作数据,在SpringBoot框架集成MyBatis,项目整体结构前提:准备一张student表。SETNAMESutf8mb4;SETFOREIGN_KEY_CHECKS=0;--------------------------------Tablestructureforuser------------------------------DROPTABLEIFEXISTS`student`......
  • springboot配置Swagger3.0
    springboot配置Swagger3.01、pom加入依赖我们创建一个SpringBoot项目,引入swagger3依赖<dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>3.0.0</version></dependency&......
  • Spring Cloud Alibaba Sentinel实现熔断限流代码示例
    SpringCloudAlibabaSentinel介绍SpringCloudAlibabaSentinel是一个面向分布式服务架构的流量控制组件,是SpringCloudAlibaba的核心组件之一。主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来帮助您保障微服务的稳定性。代码示例以下是一个使用SpringC......
  • Java:SpringBoot整合Canal+RabbitMQ组合实现MySQL数据监听
    canal[kə’næl],译意为水道/管道/沟渠,主要用途是基于MySQL数据库增量日志解析,提供增量数据订阅和消费目录一、MySQL设置二、启动Canal服务端三、通过Canal客户端消费数据四、通过RabbitMQ消费数据1、启动RabbitMQ2、修改canal配置3、消费RabbitMQ中的数据文档资料github:https......
  • SpringBoot项目启动失败报错Annotation-specified bean name ‘xx‘ for bean class [
    Annotation-specifiedbeanname'datahubServiceImpl'forbeanclass[com.peony.common.service.impl.DatahubServiceImpl]conflictswithexisting,non-compatiblebeandefinitionofsamenameandclass[com.peony.common.service.DatahubServiceImpl] 1、......
  • java——微服务——spring cloud——Eureka——ribbon负载均衡——策略
                        ========================================================         全局设置:                         针对某个微服务单独设置:......
  • java——微服务——spring cloud——Eureka——ribbon负载均衡——饥饿加载
       ......
  • 使用Spring Boot 3和微服务构建一个电子商务网站
    使用SpringBoot3和微服务构建一个电子商务网站在本文中,我们将介绍如何使用SpringBoot3以及微服务架构来构建一个电子商务网站。我们将探讨以下主题:构建一个简单的SpringBoot应用程序来处理用户请求使用微服务架构并实现网络调用来管理订单和库存集成前端框架来呈现产品信息和......