SpringData JPA学习使用笔记

本文详细介绍了Spring Data JPA,它能简化数据持久化开发,减少样板代码。文中阐述了其关键特点,如支持自动查询、JPA注解等。还介绍了javax常用注解,包括关联关系注解及异常处理,讲解了JPA原理实现,如级联操作和加载模式,最后说明了JPA的简单和复杂使用方法。

SpringData JPA

Spring Data JPA(Java Persistence API)是Spring生态系统中的一个子项目,用于简化JPA(Java Persistence API)的使用,使数据访问层的开发更加容易和高效。它提供了一组工具和库,以简化与数据库进行数据访问和持久化的操作。下面是Spring Data JPA的一些关键特点和概念:

  1. Repository接口: Spring Data JPA引入了Repository接口,允许你定义数据库操作的自定义方法,而无需手动编写SQL查询语句。只需继承Repository接口并使用方法命名约定,Spring Data JPA会自动为你生成查询方法。

  2. 自动查询方法: Spring Data JPA会根据方法名自动生成查询语句。例如,通过将方法命名为findByLastName(String lastName),你可以轻松地查找具有特定姓氏的对象。

  3. 支持JPA注解: Spring Data JPA支持使用JPA注解来定义实体和关联关系,这样你可以使用标准的JPA注解来描述数据模型。

  4. 分页和排序: Spring Data JPA提供了用于分页和排序查询结果的内置支持。你可以轻松地创建分页查询以及根据字段排序结果。

  5. Specification和Query DSL: Spring Data JPA支持使用Specification对象来动态构建查询条件,以及使用Query DSL(Domain Specific Language)来创建类型安全的查询。

  6. Auditing: Spring Data JPA允许你轻松地添加审计(auditing)功能,以记录数据库表的创建和修改时间,谁执行了这些操作等信息。

  7. 嵌套属性和关联查询: Spring Data JPA支持嵌套属性的查询,可以轻松访问实体的关联属性。

  8. 支持各种数据源: Spring Data JPA不仅支持关系型数据库,还支持NoSQL数据库(如MongoDB)等非关系型数据库。

  9. 多数据源支持: Spring Data JPA允许你在一个应用程序中使用多个数据源,并为每个数据源配置不同的Repository。

  10. 事务管理: Spring Data JPA与Spring框架无缝集成,可以利用Spring的事务管理来管理数据库操作。

总之,Spring Data JPA简化了数据持久化的开发,提供了一种高级的方式来与数据库交互,减少了重复的样板代码,使开发者能够更专注于业务逻辑而不是数据库操作。它是Spring生态系统的一部分,适用于各种Java应用程序,包括Web应用、微服务和传统的企业应用。

javax常用注解

在这里插入图片描述

@GeneratedValue

使用 GenerationType.IDENTITY 策略时,不需要显式地设置主键值,因为数据库会自动为每个插入的记录生成唯一的主键值。

请注意,GenerationType.IDENTITY 主键生成策略通常适用于支持自动递增的数据库,如 MySQL 中的自增列。但并非所有数据库都支持此策略,因此在使用特定数据库之前,请确保该数据库支持 GenerationType.IDENTITY 策略。

//四种生成方式
TABLE,
SEQUENCE,
IDENTITY,
AUTO;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

 	@Id
    @Column(name = "ID")
    @GeneratedValue(
            generator = "sequence",
            strategy = GenerationType.SEQUENCE
    )
    @SequenceGenerator(
            name = "sequence",
            sequenceName = "SEQ_PP_NODE",
            allocationSize = 1
    )
    private Long id;

@Convert()

//仅仅只作为java bean 中属性值和数据库存储值的映射
//比如业务对象存在一个Map或List属性,存入数据库时需保存为json字符串,返回前端时以对象来返回。

例如:

//某实体类的属性注解
@Column(name = "question_content", columnDefinition = "CLOB")
@Convert(converter = StringListConverter.class)
private List<String> questionContent;
//转换实现类
@Converter
public class StringListConverter implements AttributeConverter<List<String>, String> {

    private static final String SPLIT_CHAR = ";";

    @Override
    public String convertToDatabaseColumn(List<String> stringList) {
        return stringList != null ? String.join(SPLIT_CHAR, stringList) : "";
    }

    @Override
    public List<String> convertToEntityAttribute(String string) {
        return StringUtils.hasText(string) ? Arrays.asList(string.split(SPLIT_CHAR)) : new ArrayList<>();
    }
}

@JsonFormat

用于属性或者方法上(最好是属性上),可以方便的把Date类型直接转化为我们想要的模式。

@JsonFormat(shape =JsonFormat.Shape.STRING,pattern ="yyyy-MM-dd HH:mm:ss",timezone ="GMT+8")

@JsonProperty等解析

关联关系

@ManyToOne

@ManyToOne 注解用于在JPA实体中建立多对一的关系,其中一个实体(通常称为子实体)包含对另一种实体(通常称为父实体)的引用。以下是 @ManyToOne 注解的一些常见参数及其说明:

  1. targetEntity:指定关系的目标实体类。这是一个必需的参数。它定义了多对一关系的目标类型。例如:

    @ManyToOne(targetEntity = Author.class)
    private Author author;
    
  2. fetch:指定加载策略,即如何获取关联实体的数据。常见的选项包括:

    • FetchType.LAZY:表示懒加载,只有在访问关联实体时才加载数据。
    • FetchType.EAGER:表示急加载,关联实体会随父实体一起加载。

    例如:

    @ManyToOne(targetEntity = Author.class, fetch = FetchType.LAZY)
    private Author author;
    
  3. optional:指定关联是否可以为 null。如果设置为 false,则表示关联不能为空,这在数据库中通常对应于外键字段的非空约束。默认情况下,optional 被设置为 true,即关联可以为 null。

    例如:

    @ManyToOne(targetEntity = Author.class, fetch = FetchType.LAZY, optional = false)
    private Author author;
    
  4. cascade:指定级联操作,即在执行父实体的操作时是否级联执行子实体的操作。与 @OneToMany 类似,常见的选项包括 CascadeType.ALLCascadeType.PERSISTCascadeType.MERGECascadeType.REMOVE 等。

    例如:

    @ManyToOne(cascade = CascadeType.ALL) //一般在`@OneToMany`定义就好
    private Author author;
    

@JoinColumn

使用多对一的属性需要定义外键。

通过使用foreignKey属性并指定ConstraintMode.NO_CONSTRAINT,您可以告诉JPA在建立外键约束时不要应用 任何约束。这意味着将不会创建任何外键约束,从而避免由于约束错误导致的异常。

@JoinColumn(name = "AUTHOR_ID", foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT))
@ManyToOne(targetEntity = Author.class, fetch = FetchType.LAZY, optional = false)
private Author author;
@OneToMany

@OneToMany 注解用于在JPA实体中建立一对多的关系,其中一个实体(通常称为父实体)包含对多个另一种实体(通常称为子实体)的引用或集合。以下是 @OneToMany 注解的一些常见参数及其说明:

  1. mappedBy:指定另一端实体中用于映射关系的字段。这意味着多的一方(父实体)在其内部有一个字段,该字段包含对少的一方(子实体)的引用。通常,mappedBy 的值是在子实体中定义的字段名。这将确保双向关联正确映射。例如:

    @OneToMany(mappedBy = "author")
    private List<Book> books;
    
  2. cascade:指定级联操作,即在执行父实体的操作时是否级联执行子实体的操作。常见的选项包括:

    • CascadeType.ALL:表示所有操作都会级联执行,包括保存、更新、删除等。
    • CascadeType.PERSIST:表示只有保存操作会级联执行。
    • CascadeType.MERGE:表示只有更新操作会级联执行。
    • CascadeType.REMOVE:表示只有删除操作会级联执行。
    • 等等。你可以根据需求选择适当的级联操作。

    例如:

    @OneToMany(mappedBy = "author", cascade = CascadeType.ALL)
    private List<Book> books;
    
  3. fetch:指定加载策略,即如何获取关联实体的数据。常见的选项包括:

    • FetchType.LAZY:表示懒加载,只有在访问关联实体时才加载数据。
    • FetchType.EAGER:表示急加载,关联实体会随父实体一起加载。

    例如:

    @OneToMany(mappedBy = "author", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Book> books;
    
  4. **orphanRemoval **:是 JPA 中 @OneToMany@OneToOne 注解的一个属性,用于定义在从关联实体中移除对象时是否删除该对象。它通常与级联操作一起使用。

    orphanRemoval 被设置为 true 时,表示如果从父实体的集合属性中移除一个子实体对象,该子实体对象将被视为“孤儿”,JPA 将尝试将其从数据库中删除。这可以确保子实体不再被关联到父实体时会被删除。

    示例:

    @OneToMany(mappedBy = "author", cascade = CascadeType.ALL, fetch = FetchType.LAZY,  orphanRemoval = true)
    private List<Book> books;
    

    在这个示例中,ParentEntity 包含一个一对多关系的集合属性 children,并设置了 orphanRemovaltrue。如果从 children 集合中移除一个子实体对象,JPA 将尝试将该子实体对象从数据库中删除。

    注意以下几点:

    1. orphanRemoval 只对被管理的实体(即处于持久态的实体)起作用。当你从 children 集合中移除一个子实体对象并调用 EntityManagerflush() 方法时,JPA 会执行删除操作。
    2. 这个特性非常适合用于父子实体之间的强关联,通常用于删除子实体的场景。如果你只需要解除父子关系而不删除子实体,可以不使用 orphanRemoval
    3. 注意谨慎使用 orphanRemoval,因为它可能导致数据库中的数据被意外删除。只有在明确需要在解除关联时删除子实体的情况下才使用它。

这些参数允许你定义一对多关系的细节,包括目标实体、映射关系、级联操作和加载策略。你可以根据实际需求选择适当的参数值。根据这些参数,JPA会自动执行数据库操作,确保关联关系正确维护

//在节点类中关联任务信息
@ManyToOne(targetEntity = Task.class, fetch = FetchType.LAZY)
@JoinColumn(name = "TASK_ID", foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT))
private Task task;



//在任务信息类中关联节点信息
@JSONField(serialize = false)
@OneToMany(mappedBy = "task", fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
private List<TaskNode> nodes;
@ManyToMany

在多对多关系中,通常存在两个实体类(User 和 Role),它们之间相互关联,其中一个实体(User)可以拥有多个另一个实体(Role),反之亦然。为了在 JPA 中建立多对多关系,需要使用注解来映射数据库表和实体之间的关联。以下是你提供的代码片段以及它们的解释:

User 表

@JsonIgnore
@ManyToMany(targetEntity = Role.class)
@JoinTable(name = "user_role",
    joinColumns = @JoinColumn(name = "uid"),
    inverseJoinColumns = @JoinColumn(name = "rid"),
    foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT)
)
@Fetch(FetchMode.SUBSELECT)
private Set<Role> roles = new HashSet<>();
  • @ManyToMany: 这个注解表示 User 类与 Role 类之间是多对多的关联关系。
  • @JoinTable: 这个注解用于指定关联表的名称和关联字段的映射。在这里,关联表名为 “user_role”,joinColumns 用于指定 User 表中的外键字段,inverseJoinColumns 用于指定 Role 表中的外键字段。
  • foreignKey: 用于指定外键的约束方式。ConstraintMode.NO_CONSTRAINT 表示没有外键约束。
  • @Fetch(FetchMode.SUBSELECT): 这个注解用于指定在获取 User 对象时如何处理关联的 Role 集合。FetchMode.SUBSELECT 表示在获取 User 时,会使用子查询的方式一次性获取关联的 Role 集合。

Role 表

@JsonIgnoe
@ManyToMany(mappedBy = "roles", fetch = FetchType.LAZY)
@Fetch(FetchMode.SUBSELECT)
private Set<User> users;
  • @ManyToMany: 这个注解表示 Role 类与 User 类之间是多对多的关联关系。
  • mappedBy: 用于指定关联关系的维护方。在这里,“roles” 是 User 类中的属性名,它表示 User 是关联关系的维护方。
  • fetch: 这个属性指定了在获取 Role 对象时如何处理关联的 User 集合。FetchType.LAZY 表示延迟加载,只有当访问 User 集合时才会从数据库中获取数据。
  • @Fetch(FetchMode.SUBSELECT): 同样用于指定获取 Role 对象时如何处理关联的 User 集合,使用子查询的方式获取。
关联对象异常处理

在 JPA(Java Persistence API)中,如果您使用 @ManyToOne 来建立多对一的关系,并且在查询时懒加载(fetch = FetchType.LAZY),当您调用的关联对象已经被删除,那么在尝试加载这个关联对象时会引发异常。这是因为懒加载的对象在访问时才会被加载,而如果数据库中对应的数据已经不存在,加载时就会出现异常。

可以使用异常捕获

try {
    // 获取 config 对象
} catch (EntityNotFoundException e) {
    // 处理关联对象不存在的情况
}

@Embedded和@Embeddable

一个实体类要在多个不同的实体类中进行使用,而本身又不需要独立生成一个数据库表。

在 Spring Data JPA 中,@Embedded@Embeddable 是用来处理嵌入式对象(Embedded Objects)的注解。嵌入式对象是指一个对象作为另一个实体的一部分,它通常用于将一组相关属性分组到单个数据库表中。让我们详细了解它们的作用和使用:

1. @Embeddable 注解:

  • @Embeddable 注解通常用于标记一个 Java 类,该类表示将被嵌入到另一个实体类中的一部分属性。它告诉 JPA 框架该类的实例将嵌入到包含它的实体的表中。
  • @Embeddable 注解通常用于标记嵌入式对象类。这个类可以包含多个属性,这些属性将嵌入到包含它的实体的数据库表中。

示例:

@Embeddable
@Data
public class Address {
    private String street;
    private String city;
    private String zipCode;
}

2. @Embedded 注解:

  • @Embedded 注解通常用于在实体类中标记包含嵌入式对象的属性。它指示 JPA 框架应该将嵌入式对象的属性映射到包含嵌入式对象的实体类的数据库表。

示例:

@Entity
@Data
public class Person {
    @Id
    @GeneratedValue
    private Long id;
    private String name;

    @Embedded
    private Address address;
}

在这个示例中,Person 实体包含一个嵌入式对象 Address,通过 @Embedded 注解告诉 JPA 框架将 Address 的属性映射到 Person 表中。

使用场景:

  • 嵌入式对象通常用于将一组相关的属性组合成一个实体的一部分,以提高代码的组织性和可读性。这对于包括地址、联系信息、时间戳等常见的属性集合非常有用。
  • 嵌入式对象也允许将多个属性组合成一个更大的实体,同时在数据库中只使用一个表。
  • 注意,嵌入式对象通常不具有自己的标识符(ID),因为它们不会以独立的方式存储。它们总是作为它们所属的实体的一部分存储。

总之,@Embeddable@Embedded 是 Spring Data JPA 中用于处理嵌入式对象的注解。它们可以帮助你更好地组织和映射实体的属性,提高代码的可维护性。

@ElementCollection

你可以使用 @ElementCollection 注解来映射这种情况。首先,你需要创建一个 String 类型的实体,然后使用 @ElementCollection 注解将它映射为一个集合。接下来,你可以使用 JPQL 查询来搜索包含特定值的记录。

这是一个可能的实现方式:

@Entity
public class YourEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ElementCollection
    @CollectionTable(name = "HCC_TASK_OPERATOR", joinColumns = @JoinColumn(name = "TASK_ID"), foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
    @Column(name = "POST_ID")
    private List<String> staffList;
}

然后,你可以使用 JPQL 查询来查找包含特定 staff 的记录:

@Repository
public interface YourEntityRepository extends JpaRepository<YourEntity, Long> {
    List<YourEntity> findByStaffListContaining(String staff);
}

在这里,findByStaffListContaining 方法将返回包含指定 staff 值的记录列表。

确保你的数据库表结构与实体映射相匹配。如果你使用的是自动建表功能,它应该会根据实体映射创建表。如果不是,你需要手动创建表来匹配实体的结构。

@Transient

表示该属性并非一个到数据库表的字段的映射,ORM框架将忽略该属性。如果一个属性并非数据库表的字段映射,就务必将其标示为@Transient,否则,ORM框架默认其注解为@Basic。

@EntityListener基础字段

@EntityListeners(AuditingEntityListener.class)

在实体类增删改的时候监听,为创建人/创建时间等基础字段赋值

 	@CreatedBy
    @Column(name = "CREATE_BY")
    private String createBy;

    @CreatedDate
    @Column(name = "CREATE_TIME")
    private Date createTime;

    @LastModifiedBy
    @Column(name = "UPDATE_BY")
    private String updateBy;

    @LastModifiedDate
    @Column(name = "UPDATE_TIME")
    private Date updateTime;

//1.实体类添加注解
@EntityListeners(AuditingEntityListener.class)

//2.启动类需要添加注解
@EnableJpaAuditing

@Temporal

从数据库获取的时间进行格式化转换成java对应的时间类型DATE、TIME、TIMESTAMP

@Column(name = "START_TIME")
@Temporal(TemporalType.TIMESTAMP)
private Date startTime;

@Enumerated()

@Enumerated(value = EnumType.STRING)

定义的字段是枚举类型

@Column
@Enumerated(EnumType.STRING)
private Status status;


public enum Status {
    /**
     * 考试结束
     */
    OVER,
    /**
     * 批改中
     */
    CORRECTING}   

@Transactional()

  • 接口实现类或接口实现方法上,而不是接口类中。

  • 访问权限:只有在public才起作用

    @Transactional(rollbackFor = Exception.class)
    
    @Transactional 实现原理:
    1) 事务开始时,通过AOP机制,生成一个代理connection对象,
       并将其放入 DataSource 实例的某个与 DataSourceTransactionManager 相关的某处容器中。
       在接下来的整个事务中,客户代码都应该使用该 connection 连接数据库,
       执行所有数据库命令。
       [不使用该 connection 连接数据库执行的数据库命令,在本事务回滚的时候得不到回滚]
      (物理连接 connection 逻辑上新建一个会话session;
       DataSource 与 TransactionManager 配置相同的数据源)
     
    2) 事务结束时,回滚在第1步骤中得到的代理 connection 对象上执行的数据库命令,
       然后关闭该代理 connection 对象。
       
       所以在方法上加上@Transactional(rollbackFor = Exception.class)表示这个方法是一个事务,表示整个方法是一个事务,事务只会在方法执行结束后提交,而不会在方法内部的某个操作之后立即提交。
    
    

    在这里插入图片描述

嵌套事务

带有事务的方法调用其他事务的方法,此时执行的情况取决配置的事务的传播属性

  • PROPAGATION_REQUIRES_NEW :
    启动一个新的,不依赖于环境的“内部事务.这个事务将被完全committed或 rolled back而不依赖于外部事务,它拥有自己的隔离范围,自己的锁,等等.当内部事务开始执行时,外部事务将被挂起,内务事务结束时,外部事务将继续执行.
  • PROPAGATION_NESTED :
    如果外部事务commit,嵌套事务也会被commit ;如果外部事务roll back,嵌套事务也会被 roll back。
    开始一个"嵌套的”事务,它是已经存在事务的一个真正的子事务.嵌套事务开始执行时,它将取得一个savepoint.如果嵌套事务失败,将回滚到此savepoint。嵌套事务是外部事务的一部分,只有外部事务结束后它才会被提交。

序列化隐藏

@JsonIgnoreProperties() 是用于在序列化和反序列化过程中指定需要忽略的属性或属性列表,主要用在类上。

@JsonIgnoreProperties({ "prop1", "prop2" })
CLASSZ_ENTITY{
    @JsonIgnore
    private String prop3;
    
    @JSONField(serialize = false)
    private String prop4;
}

Jackson JSON库的@JsonIgnoreProperties@JsonIgnore 的使用场景略有不同。@JsonIgnore 用于特定属性,而 @JsonIgnoreProperties 用于整个类,指定一组属性应该被忽略。这在你需要一次性忽略多个属性时很有用。

例如,假设你有一个包含大量属性的类,但在某些情况下,你只希望序列化或反序列化其中的一部分属性。你可以使用 @JsonIgnoreProperties 来指定需要忽略的属性,而不必在每个属性上使用 @JsonIgnore 注解。

FastJSON库中的@JSONField(serialize = false) 用于指示 FastJSON 在序列化对象时不包含被注解的属性。这可以用于隐藏某些属性,以防止它们出现在生成的 JSON 中。

JPA 原理实现

cascade级联操作

cascade级联操作

fetch加载模式

fetch加载模式

懒加载(lazy)又叫做延时加载,就是当在真正需要数据的时候,才真正执行数据加载操作。至于为什么要用懒加载呢,就是当我们要访问的数据量过大时,明显用缓存不太合适,因为内存容量有限 ,为了减少并发量,减少系统资源的消耗,我们让数据在需要的时候才进行加载,这时我们就用到了懒加载。

JPA使用

简单使用

		<!-- 数据库驱动(注意版本问题)-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- 使用start整合配置druid数据源-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.17</version>
        </dependency>
        <!-- lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--spring-data-jpa-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

spring:
  mvc:
    hiddenmethod:
      filter:
        enabled: true
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/user?serverTimezone=UTC
    username: root
    password: qwer1234
  jpa:
    hibernate:
      ddl-auto: update  #自动更新 有表不创建,无表新建 create:有表则删除后重建 none:无表报错
    show-sql: true  #日志中显示sql语句
@Entity
@Table(name ="message")
@Data
public class Message implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    
    private String title;
    
    private String content;
    
    private Integer istop;
    
    private Integer count;
    
    private Date time;
    
    private Integer authorId;
}
public interface MessageRepository extends JpaRepository<Message,Integer> {

}
public interface MessageService {
    List<Message> getUsers();
}
@Service
public class MessageServiceImpl implements MessageService {
    @Resource
    private MessageRepository messageRepository;
    @Override
    public List<Message> getUsers() {
        return messageRepository.findAll();
    }
}

复杂使用

@Query

jpql查询
注意点:

  1. @Query写操作时需要配合@Modifying注解,并且使用事务注解避免报错
  2. like 需要手动添加百分号
  3. 使用SQL查询,nativeQuery = true
  4. 使用参数名称传参@Param(“propertyName”) String str 注入方式::str
//@Query使用  占位符有序
@Query("from Message where id = ?1")
Message findId(int id);

//@Query修改或删除时需要配合@Modifying注解,并且使用事务注解避免报错
@Query("update Message set title = ?2 where id = ?1")
@Modifying
@Transactional
void updateById(int id,String NewT);

//使用SQL查询,nativeQuery = true
@Query(value = "select * from message",nativeQuery = true)
List<Map<String,Object>> findBySql();
派生查询

按照方法命名规则查询

//已有接口方法 + By (+标识distinct|Top10) + 属性名称 + 查询方式(默认eq不用写)+ 连接符(and|or)+属性名称(+修饰符IgnoreCase)
List<Message> findByTitleAndId(String title,int id);
Sort 与 Pageable和Slice

分页

#使用pageable的实现类PageRequest
messageRepository.findAll(PageRequest.of(0, 5,Sort.by("id").descending()));

自定义条件分页

#controller
messageRepository.findByAuthorIdAndAndIdGreaterThanAndAndIdLessThan(1,10,30,
                                                             PageRequest.of(0,5,Sort.Direction.DESC,"id"));


#repository
Page findByAuthorIdAndAndIdGreaterThanAndAndIdLessThan(int uid, Pageable pageable);

Slice

当结果集非常大的时候,可以使用Slice,不考虑count,只考虑有没有next数据可用

排序

.findAll(Sort.by("id").descending().and(Sort.by("authorId").descending()));
Example

不固定查询

缺点:

  1. 不支持组合查询,比如:firstname = ?0 or (firstname = ?1 and lastname = ?2).
  2. 只支持字符串的starts/contains/ends/regex匹配,对于非字符串的属性,只支持精确匹配。换句话说,并不支持大于、小于、between等匹配。

使用:

Message message = new Message();
message.setTitle("单");
ExampleMatcher matcher = ExampleMatcher.matching()
        .withMatcher("title", match -> match.endsWith());
Example<Message> example = Example.of(message, matcher);
List<Message> all = messageRepository.findAll(example);
predicate
继承 QuerydslPredicateExecutor<T> 接口
Specification
继承 JpaSpecificationExecutor<T> 接口
   //构造查询条件

    /**
    *   root    :Root接口,代表查询的根对象,可以通过root获取实体中的属性
    *   query   :代表一个顶层查询对象,用来自定义查询
    *   cb      :用来构建查询,此对象里有很多条件方法
    **/



//第一个Specification定义了两个or的组合
Specification<SUser> s1 = (root, query, cb) -> {
    Predicate p1 = cb.equal(root.get("id"), 2);
    Predicate p2 = cb.equal(root.get("id"), 3);
    return cb.or(p1, p2);
};

//第二个Specification定义了两个or的组合
Specification<SUser> s2 = (root, query, cb) -> {
    Predicate p1 = cb.like(root.get("email"), "kk%");
    Predicate p2 = cb.like(root.get("username"), "foo%");
    return cb.or(p1, p2);
};
        
//通过Specifications将两个Specification连接起来,第一个条件加where,第二个是and
List<SUser> sUserList = sUserDao.findAll(Specifications.where(s1).and(s2));
//使用匿名内部类的方式,创建一个Specification的实现类,并实现toPredicate方法
Specification<Exam> spec = new Specification<Exam>() {
    public Predicate toPredicate(Root<Exam> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
        //cb:构建查询,添加查询方式   like:模糊匹配
        //root:从实体Exam对象中按照teacherId属性进行查询
        return cb.equal(root.get("teacherId"), exam.getTeacherId());
    }
};
return examRepository.findAll(spec);

select * from book where tenantId =1 and ( type is null or type!="")

    //使用lambda表达式

    Specification<Book> specification = (root, criteriaQuery, cb) -> {
    List<Predicate> list = Lists.newArrayList();
    list.add(cb.equal(root.get("tenantId"), "1"));
    //
    List<Predicate> or2 = Lists.newArrayList();
    or2.add(cb.isNull(root.get("type")));
    or2.add(cb.equal(root.get("type"),""));
    list.add(cb.or(or2.toArray(new Predicate[or2.size()])));
    //
    return cb.and(list.toArray(new Predicate[list.size()]));
};
Optional<List<Book>> all2 = this.findAll(specification);

@Async

异步执行,返回类型应使用异步

ListenableFuture:

可以监听异步执行的过程,执行完了,自动触发什么操作。除此之外,可以分别针对成功的情况,或者失败的情况做各种后续处理。

CompletableFuture:与ListenableFuture相似

@Async的使用

@Async
public void AsyncMethods(){
    new Thread(() -> {
        try {
            Thread.sleep(6000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ":异步任务");
    }).start();
}
public <T> T testAsync() {
    Message message = new Message();
    message.setTitle("单");
    ExampleMatcher matcher = ExampleMatcher.matching()
            .withMatcher("title", match -> match.endsWith());
    Example<Message> example = Example.of(message, matcher);
    List<Message> all = messageRepository.findAll(example);
    AsyncMethods();
    return (T) all;
}
QuerydslPredicateExecutor接口
@Repository
public interface ProcessOperationRepository extends JpaRepository<ProcessOperationRecord, Long>, QuerydslPredicateExecutor<ProcessOperationRecord> {
}
@GetMapping("query")
public Response<ProcessOperationRecord> query(@QuerydslPredicate(root = ProcessOperationRecord.class) Predicate predicate,
                                              @PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable) {
    Page<ProcessOperationRecord> recordPage = operationService.query(predicate, pageable);
    return ResultUtil.successRecords(recordPage.toList(), pageable, recordPage.getTotalElements());
}
QuerydslBinderCustomizer接口
public interfaceTaskRepository extends JpaRepository<Task, Long>,
        QuerydslPredicateExecutor<Task>, QuerydslBinderCustomizer<QTask JpaSpecificationExecutor<Task> {
  Task制查询规则
     *
     * @param bindings 查询绑定
     * @param root     Q实体
     */
    @Override
    default void customize(QuerydslBindings bindings, QTask root) {
        Task  bindings.bind(root.name).first(StringExpression::contains);
        // 时间区间查询
        bindings.bind(root.startTime)
                .all((path, value) -> {
                    List<? extends Date> dates = new ArrayList<>(value);
                    if (dates.size() == 1) {
                        return java.util.Optional.ofNullable(path.eq(dates.get(0)));
                    } else {
                        Date from = dates.get(0);
                        Date to = dates.get(1);
                        return java.util.Optional.of(path.between(from, to));
                    }
                });
        // 时间区间查询
        bindings.bind(root.endTime)
                .all((path, value) -> {
                    List<? extends Date> dates = new ArrayList<>(value);
                    if (dates.size() == 1) {
                        return java.util.Optional.ofNullable(path.eq(dates.get(0)));
                    } else {
                        Date from = dates.get(0);
                        Date to = dates.get(1);
                        return java.util.Optional.of(path.between(from, to));
                    }
                });
        // 状态列表查询
        bindings.bind(root.status)
                .all((path, value) -> {
                    List<AggregativeTask.Status> statusList = new ArrTask;
                    return java.util.Optional.of(path.in(statusList));
                });
    }
    
}    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值