Administrator
发布于 2025-06-27 / 0 阅读
0
0

SpringDataJpa框架-202509120151

SpringDataJpa框架-202509120151

SpringDataJpa框架

一、什么是JPA

jpa 的全称是 Java Persistence API , 中文的字面意思就是 java 的持久层 API , jpa 就是定义了一系

列标准,让实体类和数据库中的表建立一个对应的关系,当我们在使用 java 操作实体类的时候能

达到操作数据库中表的效果(不用写sql ,就可以达到效果),jpa 的实现思想即是 ORM (Object Rel

ation Mapping),对象关系映射,用于在关系型数据库和业务实体对象之间作一个映射。

jpa 并不是一个框架,是一类框架的总称,持久层框架 Hibernate 是 jpa 的一个具体实现,本文要

谈的 spring data jpa 又是在 Hibernate 的基础之上的封装实现。

使用 jpa 是可以解决一些我们写 sql 语句的烦恼;

二、SpringDataJpa常用的配置

org.springframework.boot

spring-boot-starter-data-jpa

#项目端口的常用配置

server.port=8081


数据库连接的配置

spring.datasource.url=jdbc:mysql:///jpa?useSSL=false

spring.datasource.username=root

spring.datasource.password=zempty123

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver


#数据库连接池的配置,hikari 连接池的配置

spring.datasource.hikari.idle-timeout=30000

spring.datasource.hikari.connection-timeout=10000

spring.datasource.hikari.maximum-pool-size=15

spring.datasource.hikari.minimum-idle=5

spring.datasource.hikari.auto-commit=true



#通过 jpa 自动生成数据库中的表


spring.jpa.hibernate.ddl-auto=update

三、类隐射到数据库表的常用注解

1. @Entity

用来注解该类是一个实体类用来进行和数据库中的表建立关联关系,首次启动项目的时候,默认

会在数据中生成一个同实体类相同名字的表(table),也可以通过注解中的 name 属性来修改表

(table)名称, 如@Entity(name=“stu”) , 这样数据库中表的名称则是 stu 。 该注解十分重要,

如果没有该注解首次启动项目的时候你会发现数据库没有生成对应的表。

2.Id

@Id 类的属性注解,该注解表明该属性字段是一个主键,该属性必须具备,不可缺少。

3.@GeneratedValue

该注解通常和 @Id 主键注解一起使用,用来定义主键的呈现形式,

该注解通常有多种使用策略:

@GeneratedValue(strategy= GenerationType.IDENTITY) 该注解由数据库自动生成,主键自

增型,在 mysql 数据库中使用最频繁,oracle 不支持。

@GeneratedValue(strategy= GenerationType.AUTO) 主键由程序控制,默认的主键生成策

略,oracle 默认是序列化的方式,mysql 默认是主键自增的方式。

@GeneratedValue(strategy= GenerationType.SEQUENCE) 根据底层数据库的序列来生成主

键,条件是数据库支持序列,Oracle支持,Mysql不支持。

@GeneratedValue(strategy= GenerationType.TABLE) 使用一个特定的数据库表格来保存主

键,较少使用。

以上的主键生成策略当中,在数据库 mysql 当中 IDENTITY 和 AUTO 用的较多,二者当中 IDENTI

TY 用的多些

4.@Column

是一个类的属性注解,该注解可以定义一个字段映射到数据库属性的具体特征,比如字段长度,

映射到数据库时属性的具体名字等。

#该配置比较常用,当服务首次启动会在数据库中生成相应表,后续启动服务时如果实体类有增加

属性会在数据中添加相应字段,

#原来数据仍在,该配置除了 update ,\

#还有其他配置值,

#create :该值慎用,每次重启项目的时候都会删除表结构,重新生成,原来数据会丢失不见。

#create-drop :慎用,当项目关闭,数据库中的表会被删掉。

#validate : 验证数据库和实体类的属性是否匹配,不匹配将会报错。


spring.jpa.show-sql=true

#该配置当在执行数据库操作的时候会在控制台打印 sql 语句,方便我们检查排错等。


spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Inn

oDBDialect

5.@Transient

是一个属性注解,该注解标注的字段不会被应射到数据库当中。

实例代码:

@Setter//lombok 的注解

@Getter//lombok 的注解

@Accessors(chain = true) //lombok 的注解

@Entity(name = "stu")

//@Table

public class Student {

@Id

@GeneratedValue(strategy= GenerationType.TABLE)

private long id;


@Column(length = 100)

private String name;


@Transient

private String test;


private int age;


private LocalTime onDuty;


private LocalDate onPosition;


private LocalDateTime birthdayTime;

}

@Entity

@Data

@Table(name = "paper_certificate")

public class PaperCertificate implements Serializable {

private static final long serialVersionUID = 1L;

@Id

private String id;

/**

  • 证书路径
  • */

    @Column(name = "certificate_url")

    private String certificateUrl;

    /**

    6.Repository注解

    @Modifying //做update操作时需要添加

    @Query // 自定义Sql

    @Transactional //事务

    @Async //异步操作

  • 分类名字
  • */

    @Column(name = "certificate_name")

    private String certificateName;

    /**

  • 证书层级
  • */

    @Column(name = "certificate_level")

    private String certificateLevel;

    /**

  • 分类id
  • */

    @Column(name = "classification_id")

    private String classificationId;

    /**

  • 用户手机号
  • */

    @Column(name = "user_phone")

    private String userPhone;

    /**

  • 创建时间
  • */

    @Column(name = "create_time")

    private LocalDateTime createTime;

    /**

  • 更新时间
  • */

    @Column(name = "update_time")

    private LocalDateTime updateTime;

    public PaperCertificate() {

    }

    }

    四、使用SpringDataJPA进行增删改查

    1.定义Dao层

    定义一个 Student 的 dao 层,这样我们的增删改查就已经有了

    2.使用关键字自定义查询

    我们可以使用 jpa 提供的 find 和 get 关键字完成常规的查询操作,使用 delete 关键字完成删除,使

    用 count 关键字完成统计等

    jpa关键词查询是通过方法名称关键字的搭配,底层生成sql的方式来实现与数据库的交互,其关

    键词的搭配又很多方式,基本能覆盖表查询的所又情况,其余关键字查询可以自行百度;

    3.使用 sql 增删改查

    jpa同样支持写sql语句来操作数据,sql 有两种呈现形式:

    @Query(value = "SELECT * FROM USERS WHERE X = ?1", nativeQuery = true)

    User findByEmailAddress(String X);

    @Query("select u from User u where u.firstname = :firstname") //不加

    nativeQuery应使用HQL

    User findByLastnameOrFirstname(@Param("lastname") String lastname);

    •public interface StudentRepository  extends

    JpaRepository,

    JpaSpecificationExecutor {

    }•

    public interface StudentRepository extends

    JpaRepository {


    // 查询数据库中指定名字的学生

    List findByName(String name);


    // 根据名字和年龄查询学生

    List getByNameAndAge(String name, Integer age);


    //删除指定名字的学生,删除时需要在Service层调用是添加事务注解@Transactional

    Long deleteByName(String name);


    // 统计指定名字学生的数量

    Long countByName(String name);


    }

    JPQL 形式的 sql 语句,from 后面是以类名呈现的。

    原生的 sql 语句,需要使用 nativeQuery = true 指定使用原生 sql

    sql 中的参数传递也有两种形式:

    1. 使用问号 ?,紧跟数字序列,数字序列从1 开始,如 ?1 接收第一个方法参数的值。

    2. 使用冒号:,紧跟参数名,参数名是通过@Param 注解来确定。

    4.排序

    实例化Sort CUDEXE

    public interface ClassRoomRepository extends

    JpaRepository {


    //使用的 JPQL 的 sql 形式 from 后面是类名

    // ?1 代表是的是方法中的第一个参数

    @Query("select s from ClassRoom s where s.name =?1")

    List findClassRoom1(String name);


    //这是使用正常的 sql 语句去查询

    // :name 是通过 @Param 注解去确定的

    @Query(nativeQuery = true,value = "select * from class_room c

    where c.name =:name")

    List findClassRoom2(@Param("name")String name);

    }

    /**

  • 获取学生列表
  • @param subject
  • @return
  • */

    public List getStudents(String name) {

    // 第一种方法实例化出 Sort 类,根据姓名进行升序排列

    Sort sort = Sort.by(Sort.Direction.DESC, "name");

    // 第二种方法定义多个字段的排序

    Sort sort2 = Sort.by(Sort.Direction.DESC, "id", "name");

    // 通过实例化 Sort.Order 来排序多个字段

    List orders = new ArrayList<>();

    Sort.Order order1 = new Sort.Order(Sort.Direction.DESC, "id");

    Sort.Order order2 = new Sort.Order(Sort.Direction.DESC, "age");

    orders.add(order1);

    orders.add(order2);

    Sort sort3 = Sort.by(orders);

    5.JPA的分页查询

    return studentRepository.getStudents(name, sort);

    }

    public interface StudentRepository extends JpaRepository

    Long> {

    // 正常使用,只是多加了一个 sort 参数而已

    @Query("select t from Student t where t.name = ?1")

    List getStudents(String name, Sort sort);

    }

    public PageResponse excute(BoardPageQryCmd

    build) {

    Pageable pageable = PageRequest.of(build.getPageNum() - 1,

    build.getPageSize(),

    Sort.by(Sort.Direction.DESC, "regTime").descending());

    Specification spe = (root, query, cb) -> {

    List predicates = new ArrayList<>();

    if (StringUtils.isNotEmpty(build.getMerchantName())) {

    predicates.add(cb.equal(root.get("merchantName"),

    build.getMerchantName()));

    }

    if (StringUtils.isNotEmpty(build.getChargeName())) {

    predicates.add(cb.equal(root.get("chargeName"),

    build.getChargeName()));

    }

    if (RoleTagEnum.ZX_BRAND.getValue().equals(build.getRole())) {

    // 品牌只查询下级俱乐部


    predicates.add(cb.in(root.get("merchantCode")).value(build.getClubMerch

    antCodes()));

    }

    if (RoleTagEnum.ADMIN.getValue().equals(build.getRole())) {

    // 管理员选择查看品牌或者俱乐部

    predicates.add(cb.equal(root.get("roleTag"),

    build.getQueryRoleTag()));

    }

    predicates.add(cb.equal(root.get("deleted"),

    DeleteEnum.EXIST.getCode()));

    return cb.and(predicates.toArray(new Predicate[0]));

    };

    Page pageData =

    purchaseBoardInfoRepository.findAll(spe, pageable);

    PageRequest 一共有三个可以实例化的静态方法:

    public static PageRequest of(int page, int size)

    public static PageRequest of(int page, int size, Sort sort) 分页的同时还可以针对分页后的结

    果进行一个排序。

    public static PageRequest of(int page, int size, Direction direction, String… properties) 直接

    针对字段进行排序。

    6.jap使用Specification

    List purchaseBoardInfoDtos =

    PurchaseBoardInfoConverter.converterToDtos(pageData.getContent());

    // 返回分页结果,确保页码从 1 开始(如果需要)

    return PageResponse.of(purchaseBoardInfoDtos,

    pageData.getTotalElements(), build.getPageSize(), build.getPageNum());

    }

    public Page getPage(@PathVariable("subject") String subject) {

    // 第一种方法实例化 Pageable

    Pageable pageable1 = PageRequest.of(1, 2);


    //第二种实例化 Pageable

    Sort sort = Sort.by(Sort.Direction.ASC, "age");

    Pageable pageable2 = PageRequest.of(1, 2, sort);


    //第三种实例化 Pageable

    Pageable pageable3 = PageRequest.of(1, 2, Sort.Direction.DESC,

    "age");


    //可以传入不同的 Pageable,测试效果

    Page page = teacherRepositoty.getPage(subject, pageable3);

    System.out.println(page.getTotalElements());

    System.out.println(page.getTotalPages());

    System.out.println(page.hasNext());

    System.out.println(page.hasPrevious());

    System.out.println(page.getNumberOfElements());

    System.out.println(page.getSize());

    return page;

    }

    //正常使用,只是多加了一个 Pageable 参数而已

    @Query("select t from Teacher t where t.subject = :subject")

    Page getPage(@Param("subject") String subject, Pageable

    pageable);

    6.1 接口继承

    6.2 实例化 Specification

    Specification 是一个函数式接口,里面有一个抽象的方法:

    参数说明:

    Predicate 是用来建立 where 后的查寻条件的相当于上述sql语句的 age > 20。

    Root 使用来定位具体的查询字段,比如 root.get(“age”) ,定位 age字段,

    CriteriaBuilder是用来构建一个字段的范围,相当于 > ,= ,<,and …. 等等

    CriteriaQuery 可以用来构建整个 sql 语句,可以指定sql 语句中的 select 后的查询字段,也

    可以拼接 where , groupby 和 having 等复杂语句。

    public interface TeacherRepositoty extends

    JpaRepository , JpaSpecificationExecutor {

    }


    //JpaSpecificationExecutor提供了如下的几个方法供我们使用 方法参数:

    Specification

    public interface JpaSpecificationExecutor {


    Optional findOne(@Nullable Specification var1);


    List findAll(@Nullable Specification var1);


    Page findAll(@Nullable Specification var1, Pageable var2);


    List findAll(@Nullable Specification var1, Sort var2);


    long count(@Nullable Specification var1);


    boolean exists(Specification spec);


    long delete(Specification spec);


    R findBy(Specification spec,

    Function, R> queryFunction);


    }

    Predicate toPredicate(Root var1, CriteriaQuery var2,

    CriteriaBuilder var3);

    6.3核心方法

    查询所有数据 findAll()

    修改 添加数据 S save(S entity)

    分页查询 Page<> findAll(Example<> example, Pageable pageable)

    根据id查询 findOne()

    根据实体类属性查询: findByProperty (type Property); 例如:findByAge(int age)

    删除 void delete(T entity)

    计数 查询 long count() 或者 根据某个属性的值查询总数 countByAge(int age)

    是否存在 boolean existsById(ID primaryKey)

    6.4 查询关键字

    //多条件查询并排序

    public List queryBanner(Integer location) {

    return findAll(

    //实例化 Specification 类

    (root, query, criteriaBuilder) -> {

    // 构建查询条件

    Predicate predicate =

    criteriaBuilder.equal(root.get(“数据库字段”),与数据库字段比较的参数 );

    // 使用 and 连接上一个条件

    predicate = criteriaBuilder.and(

    predicate,

    criteriaBuilder.equal(root.get(数据库字段),

    与数据库字段比较的参数));

    return predicate;

    },

    //传入Sort排序参数进行排序

    Sort.by(Sort.Order.desc("数据库字段")));

    }

    -and

    And 例如:findByUsernameAndPassword(String user, Striang pwd);

    -or

    Or 例如:findByUsernameOrAddress(String user, String addr);

    -between

    Between 例如:SalaryBetween(int max, int min);

    -"<"

    6.5内置方法

    LessThan 例如: findBySalaryLessThan(int max);

    -">"

    GreaterThan 例如: findBySalaryGreaterThan(int min);

    -is null

    IsNull 例如: findByUsernameIsNull();

    -is not null

    IsNotNull NotNull 与 IsNotNull 等价 例如: findByUsernameIsNotNull();

    -like

    Like 例如: findByUsernameLike(String user);

    -not like

    NotLike 例如: findByUsernameNotLike(String user);

    -order by

    OrderBy 例如: findByUsernameOrderByNameAsc(String user);直接通过name正序

    排序

    -"!="

    Not 例如: findByUsernameNot(String user);

    -in

    In 例如: findByUsernameIn(Collection userList) ,方法的参数可以是

    Collection 类型,也可以是数组或者不定长参数;

    -not in

    NotIn 例如: findByUsernameNotIn(Collection userList) ,方法的参数

    可以是 Collection 类型,也可以是数组或者不定长参数;

    -Top/Limit

    查询方法结果的数量可以通过关键字来限制,first 或者 top都可以使用。top/first加数字

    可以指定要返回最大结果的大小 默认为1

    分页+排序实现:

    DemoBean demoBean = new DemoBean();

    demoBean.setAppId(appId); //查询条件

    //创建查询参数

    Example example = Example.of(demoBean);

    //获取排序对象

    Sort sort = new Sort(Sort.Direction.DESC, "id");

    //创建分页对象

    PageRequest pageRequest = new PageRequest(index, num, sort);

    Example_实例查询

    创建一个ExampleMatcher对象,最后再用Example的of方法构造相应的Example对象并传递给相

    关查询方法。我们看看Spring的例子。

    ExampleMatcher用于创建一个查询对象,上面的代码就创建了一个查询对象。withIgnorePaths

    方法用来排除某个属性的查询。withIncludeNullValues方法让空值也参与查询,就是我们设置了

    对象的姓,而名为空值.

    综合使用:

    //分页查询

    return demoRepository.findAll(example, pageRequest).getContent();

    Person person = new Person();

    person.setFirstname("Dave"); //Firstname = 'Dave'



    ExampleMatcher matcher = ExampleMatcher.matching()

    .withMatcher("name",

    GenericPropertyMatchers.startsWith()) //姓名采用“开始匹配”的方式查询

    .withIgnorePaths("int"); //忽略属性:是否关注。

    因为是基本类型,需要忽略掉


    Example example = Example.of(person, matcher); //Example根据域

    对象和配置创建一个新的ExampleMatcher

    //创建查询条件数据对象

    Customer customer = new Customer();

    customer.setName("zhang");

    customer.setAddress("河南省");

    customer.setRemark("BB");

    //创建匹配器,即如何使用查询条件

    ExampleMatcher matcher = ExampleMatcher.matching() //构建对象

    .withStringMatcher(StringMatcher.CONTAINING) //改变默认字符串匹配

    方式:模糊查询

    .withIgnoreCase(true) //改变默认大小写忽略方式:忽略大小写

    .withMatcher("address", GenericPropertyMatchers.startsWith())

    //地址采用“开始匹配”的方式查询

    .withIgnorePaths("focus"); //忽略属性:是否关注。因为是基本类型,需要

    忽略掉

    //创建实例

    Example ex = Example.of(customer, matcher);

    //查询

    1、概念定义:

    上面例子中,是这样创建“实例”的:Example ex = Example.of(customer, matcher);

    我们看到,Example对象,由customer和matcher共同创建。

    A、实体对象:在持久化框架中与Table对应的域对象,一个对象代表数据库表中的一条记录,

    如上例中Customer对象。在构建查询条件时,一个实体对象代表的是查询条件中的“数值”部分。

    如:要查询名字是“Dave”的客户,实体对象只能存储条件值“Dave”。

    B、匹配器:ExampleMatcher对象,它是匹配“实体对象”的,表示了如何使用“实体对象”中的

    “值”进行查询,它代表的是“查询方式”,解释了如何去查的问题。如:要查询FirstName是“Dave”

    的客户,即名以“Dave"开头的客户,该对象就表示了“以什么开头的”这个查询方式,如上例中:with

    Matcher("name", GenericPropertyMatchers.startsWith())

    C、实例:即Example对象,代表的是完整的查询条件。由实体对象(查询条件值)和匹配器

    (查询方式)共同创建。

    再来理解“实例查询”,顾名思义,就是通过一个例子来查询。要查询的是Customer对象,查询

    条件也是一个Customer对象,通过一个现有的客户对象作为例子,查询和这个例子相匹配的对

    象。

    2、特点及约束(局限性):

    A、支持动态查询。即支持查询条件个数不固定的情况,如:客户列表中有多个过滤条件,用户

    使用时在“地址”查询框中输入了值,就需要按地址进行过滤,如果没有输入值,就忽略这个过滤

    条件。对应的实现是,在构建查询条件Customer对象时,将address属性值置具体的条件值或置

    为null。

    B、不支持过滤条件分组。即不支持过滤条件用 or(或) 来连接,所有的过滤查件,都是简单一层

    的用 and(并且) 连接。

    C、仅支持字符串的开始/包含/结束/正则表达式匹配 和 其他属性类型的精确匹配。查询时,对

    一个要进行匹配的属性(如:姓名 name),只能传入一个过滤条件值,如以Customer为例,要

    查询姓“刘”的客户,“刘”这个条件值就存储在表示条件对象的Customer对象的name属性中,针对

    于“姓名”的过滤也只有这么一个存储过滤值的位置,没办法同时传入两个过滤值。正是由于这个

    限制,有些查询是没办法支持的,例如要查询某个时间段内添加的客户,对应的属性是 addTim

    e,需要传入“开始时间”和“结束时间”两个条件值,而这种查询方式没有存两个值的位置,所以就

    没办法完成这样的查询。

    3、ExampleMatcher的使用 :

    一些问题:

    (1)Null值的处理。当某个条件值为Null,是应当忽略这个过滤条件呢,还是应当去匹配数据库表

    中该字段值是Null的记录?

    (2)基本类型的处理。如客户Customer对象中的年龄age是int型的,当页面不传入条件值时,

    List ls = dao.findAll(ex);

    它默认是0,是有值的,那是否参与查询呢?

    (3)忽略某些属性值。一个实体对象,有许多个属性,是否每个属性都参与过滤?是否可以忽

    略某些属性?

    (4)不同的过滤方式。同样是作为String值,可能“姓名”希望精确匹配,“地址”希望模糊匹配,

    如何做到?

    (5)大小写匹配。字符串匹配时,有时可能希望忽略大小写,有时则不忽略,如何做到?

    一些方法:

    1、关于基本数据类型。

    实体对象中,避免使用基本数据类型,采用包装器类型。如果已经采用了基本类型,

    而这个属性查询时不需要进行过滤,则把它添加到忽略列表(ignoredPaths)中。

    2、Null值处理方式。

    默认值是 IGNORE(忽略),即当条件值为null时,则忽略此过滤条件,一般业务也是采用这种方

    式就可满足。当需要查询数据库表中属性为null的记录时,可将值设为INCLUDE,这时,对于不

    需要参与查询的属性,都必须添加到忽略列表(ignoredPaths)中,否则会出现查不到数据的情

    况。

    3、默认配置、特殊配置。

    默认创建匹配器时,字符串采用的是精确匹配、不忽略大小写,可以通过操作方法改变这种默认

    匹配,以满足大多数查询条件的需要,如将“字符串匹配方式”改为CONTAINING(包含,模糊匹

    配),这是比较常用的情况。对于个别属性需要特定的查询方式,可以通过配置“属性特定查询方

    式”来满足要求。

    4、非字符串属性

    如约束中所谈,非字符串属性均采用精确匹配,即等于。

    5、忽略大小写的问题。

    忽略大小的生效与否,是依赖于数据库的。例如 MySql 数据库中,默认创建表结构时,字段是已

    经忽略大小写的,所以这个配置与否,都是忽略的。如果业务需要严格区分大小写,可以改变数

    据库表结构属性来实现,具体可百度。

    五、Spring data jpa + QueryDSL 进行复杂查询

    1. QueryDSL仅仅是一个通用的查询框架,专注于通过Java API构建类型安全的SQL查询。

    2. Querydsl可以通过一组通用的查询API为用户构建出适合不同类型ORM框架或者是SQL的查

    询语句,也就是说QueryDSL是基于各种ORM框架以及SQL之上的一个通用的查询框架。

    3. 借助QueryDSL可以在任何支持的ORM框架或者SQL平台上以一种通用的API方式来构建查

    询。目前QueryDSL支持的平台包括JPA,JDO,SQL,Java Collections,RDF,Lucene,Hibernate Se

    arch。

    1.实体类

    城市类:

    2 单表动态分页查询

    Spring Data JPA中提供了QueryDslPredicateExecutor接口,用于支持QueryDSL的查询操作

    3 多表动态查询

    QueryDSL对多表查询提供了一个很好地封装,看下面代码:

    @Entity

    @Table(name = "t_city", schema = "test", catalog = "")

    public class TCity {

    //省略JPA注解标识

    private int id;

    private String name;

    private String state;

    private String country;

    private String map;

    }

    旅馆类

    @Entity

    @Table(name = "t_hotel", schema = "test", catalog = "")

    public class THotel {

    //省略JPA注解标识

    private int id;

    private String name;

    private String address;

    private Integer city;//保存着城市的id主键

    }

    public interface tCityRepository extends JpaRepository

    Integer>, QueryDslPredicateExecutor {}

    //查找出Id小于3,并且名称带有`shanghai`的记录.

    //动态条件

    QTCity qtCity = QTCity.tCity;

    //SDL实体类//该Predicate为querydsl下的类,支持嵌套组装复杂查询条件

    Predicate predicate =

    qtCity.id.longValue().lt(3).and(qtCity.name.like("shanghai"))

    ;//分页排序Sort sort = new Sort(new

    Sort.Order(Sort.Direction.ASC,"id"));

    PageRequest pageRequest = new PageRequest(0,10,sort);

    //查找结果Page tCityPage =

    tCityRepository.findAll(predicate,pageRequest);

    代码规范

    1.实体类

    /**

  • 关联查询示例,查询出城市和对应的旅店
  • @param predicate 查询条件
  • @return 查询实体
  • */

    @Override

    public List findCityAndHotel(Predicate predicate) {

    JPAQueryFactory queryFactory = new JPAQueryFactory(em);

    JPAQuery jpaQuery =

    queryFactory.select(QTCity.tCity,QTHotel.tHotel)

    .from(QTCity.tCity)

    .leftJoin(QTHotel.tHotel)


    .on(QTHotel.tHotel.city.longValue().eq(QTCity.tCity.id.longValue()));

    //添加查询条件

    jpaQuery.where(predicate);

    //拿到结果

    return jpaQuery.fetch();

    }

    import jakarta.persistence.Column;

    import jakarta.persistence.Entity;

    import jakarta.persistence.Id;

    import jakarta.persistence.Table;

    import lombok.Data;

    import java.io.Serializable;

    import java.time.LocalDateTime;

    /**

  • @author gcj
  • @since 2024-11-26
  • */

    @Entity

    @Data

    @Table(name = "paper_certificate")

    public class PaperCertificate implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id

    private String id;

    /**

  • 证书路径
  • */

    @Column(name = "certificate_url")

    private String certificateUrl;

    /**

  • 分类名字
  • */

    @Column(name = "certificate_name")

    private String certificateName;

    /**

  • 证书层级
  • */

    @Column(name = "certificate_level")

    private String certificateLevel;

    /**

  • 分类id
  • */

    @Column(name = "classification_id")

    private String classificationId;

    /**

  • 用户手机号
  • */

    @Column(name = "user_phone")

    private String userPhone;

    /**

  • 创建时间
  • */

    @Column(name = "create_time")

    private LocalDateTime createTime;

    /**

  • 更新时间
  • */

    @Column(name = "update_time")

    private LocalDateTime updateTime;

    public PaperCertificate() {

    }

    }

    需要生成dto和vo的实体类,生成之后还需要写转化器

    public class PaperCertificateConverter {

    public static PaperCertificateDto converterToDto(PaperCertificate

    paperCertificate) {

    if (paperCertificate == null) {

    return null;

    }

    PaperCertificateDto paperCertificateDto = new

    PaperCertificateDto();

    paperCertificateDto.setId(paperCertificate.getId());


    paperCertificateDto.setCertificateUrl(paperCertificate.getCertificateUr

    l());


    paperCertificateDto.setCertificateName(paperCertificate.getCertificateN

    ame());


    paperCertificateDto.setCertificateLevel(paperCertificate.getCertificate

    Level());


    paperCertificateDto.setClassificationId(paperCertificate.getClassificat

    ionId());


    paperCertificateDto.setUserPhone(paperCertificate.getUserPhone());


    paperCertificateDto.setCreateTime(paperCertificate.getCreateTime());


    paperCertificateDto.setUpdateTime(paperCertificate.getUpdateTime());

    return paperCertificateDto;

    }

    public static PaperCertificate converterToPo(PaperCertificateDto

    paperCertificateDto) {

    if (paperCertificateDto == null) {

    return null;

    }

    PaperCertificate paperCertificate = new PaperCertificate();

    paperCertificate.setId(paperCertificateDto.getId());


    paperCertificate.setCertificateUrl(paperCertificateDto.getCertificateUr

    l());


    paperCertificate.setCertificateName(paperCertificateDto.getCertificateN

    ame());


    paperCertificate.setCertificateLevel(paperCertificateDto.getCertificate

    Level());


    paperCertificate.setClassificationId(paperCertificateDto.getClassificat

    ionId());


    paperCertificate.setUserPhone(paperCertificateDto.getUserPhone());


    paperCertificate.setCreateTime(paperCertificateDto.getCreateTime());


    paperCertificate.setUpdateTime(paperCertificateDto.getUpdateTime());

    return paperCertificate;

    }

    public static PaperCertificateVo converterToVo(PaperCertificateDto

    paperCertificateDto, Integer certificateStatus) {

    if (paperCertificateDto == null) {

    return null;

    }

    PaperCertificateVo paperCertificateVo = new

    PaperCertificateVo();

    paperCertificateVo.setId(paperCertificateDto.getId());


    paperCertificateVo.setCertificateUrl(paperCertificateDto.getCertificate

    Url());


    paperCertificateVo.setCertificateName(paperCertificateDto.getCertificat

    eName());


    paperCertificateVo.setCertificateLevel(paperCertificateDto.getCertifica

    teLevel());


    paperCertificateVo.setClassificationId(paperCertificateDto.getClassific

    ationId());


    paperCertificateVo.setUserPhone(paperCertificateDto.getUserPhone());


    paperCertificateVo.setCreateTime(paperCertificateDto.getCreateTime());


    paperCertificateVo.setUpdateTime(paperCertificateDto.getUpdateTime());

    paperCertificateVo.setCertificateStatus(certificateStatus);

    return paperCertificateVo;

    }

    }

    2.PaperCertificateRepository

    3.cudexe 和 qryexe

    @Repositorypublic interface PaperCertificateRepository extends

    JpaRepository,

    JpaSpecificationExecutor {    PaperCertificate

    findByClassificationIdAndUserPhone(String classificationId, String

    userPhone);}

    import com.alibaba.cola.dto.SingleResponse;

    import com.dxznjy.framework.common.utils.SnowflakeIdWorker;

    import com.dxznjy.service.train.tunnel.db.po.PaperCertificate;

    import

    com.dxznjy.service.train.tunnel.db.repository.PaperCertificateRepositor

    y;

    import lombok.RequiredArgsConstructor;

    import org.springframework.stereotype.Service;

    /**

  • @author gcj
  • @since 2024-11-26
  • */

    @Service

    @RequiredArgsConstructor

    public class PaperCertificateCudExe {

    private final SnowflakeIdWorker snowflakeIdWorker;

    private final PaperCertificateRepository

    paperCertificateRepository;

    /**

  • 创建证书
  • @param paperCertificate
  • @return
  • */

    public SingleResponse create(PaperCertificate paperCertificate) {

    return

    SingleResponse.of(paperCertificateRepository.save(paperCertificate));

    }

    }

    import com.alibaba.cola.dto.SingleResponse;

    import com.dxznjy.framework.common.enums.CommonCodeEnum;

    import com.dxznjy.framework.common.utils.SnowflakeIdWorker;

    import com.dxznjy.service.train.dto.exam.PaperCertificateDto;

    import com.dxznjy.service.train.dto.exam.PaperCertificateQryCmd;

    import com.dxznjy.service.train.dto.exam.qry.PaperCertificateQry;

    import

    com.dxznjy.service.train.exam.convertor.PaperCertificateConverter;

    import com.dxznjy.service.train.tunnel.db.po.PaperCertificate;

    import

    com.dxznjy.service.train.tunnel.db.repository.PaperCertificateRepositor

    y;

    import jakarta.persistence.criteria.CriteriaBuilder;

    import jakarta.persistence.criteria.Predicate;

    import lombok.RequiredArgsConstructor;

    import org.apache.commons.collections4.CollectionUtils;

    import org.apache.commons.lang3.ObjectUtils;

    import org.springframework.data.jpa.domain.Specification;

    import org.springframework.stereotype.Service;

    import java.util.ArrayList;

    import java.util.Collections;

    import java.util.List;

    /**

  • @author gcj
  • @since 2024-11-26
  • */

    @Service

    @RequiredArgsConstructor

    public class PaperCertificateQryExe {

    private final SnowflakeIdWorker snowflakeIdWorker;

    private final PaperCertificateRepository

    paperCertificateRepository;

    /**

  • 根据分类id查询证书
  • *

  • @param classificationId 分类id
  • @return
  • */

    public SingleResponse

    execute(PaperCertificateQry qry) {

    PaperCertificate byClassificationIdAndUserPhone =

    paperCertificateRepository.findByClassificationIdAndUserPhone(qry.getCl

    assificationId(), qry.getMobile());

    if (ObjectUtils.isNotEmpty(byClassificationIdAndUserPhone)) {

    return

    SingleResponse.of(PaperCertificateConverter.converterToDto(byClassifica

    tionIdAndUserPhone));

    } else {

    return

    SingleResponse.buildFailure(CommonCodeEnum.DB_NO_VALUE.code(),

    CommonCodeEnum.DB_NO_VALUE.message());

    }

    }

    public SingleResponse>

    execute(PaperCertificateQryCmd qryCmd) {

    List certificateList =

    paperCertificateRepository.findAll(buildQryQuery(qryCmd));

    if (CollectionUtils.isEmpty(certificateList)) {

    return SingleResponse.of(Collections.emptyList());

    }

    List certificateDtoList =

    certificateList.stream().map(PaperCertificateConverter::converterToDto)

    .toList();

    return SingleResponse.of(certificateDtoList);

    }

    private Specification

    buildQryQuery(PaperCertificateQryCmd qryCmd) {

    return (root, query, criteriaBuilder) -> {

    List predicateList = new ArrayList<>();

    if (CollectionUtils.isNotEmpty(qryCmd.getUserPhoneList()))

    {

    CriteriaBuilder.In in =

    criteriaBuilder.in(root.get("userPhone"));

    for (String userPhone : qryCmd.getUserPhoneList()) {

    in.value(userPhone);

    }

    predicateList.add(in);

    }

    Predicate[] p = new Predicate[predicateList.size()];

    return criteriaBuilder.and(predicateList.toArray(p));

    };

    }

    }

    4.Service

    @Service

    @RequiredArgsConstructor

    public class PaperCertificateService {

    private final SnowflakeIdWorker snowflakeIdWorker;

    private final PaperCertificateQryExe paperCertificateQry;

    private final PaperCertificateCudExe paperCertificateCudExe;

    @Autowired

    private PaperCertificateInformationService informationService;

    @Autowired

    private SysUserRoleService userService;

    @Autowired

    private ExaminationCourseService courseService;

    @Autowired

    private CategoryCourseRelationService

    categoryCourseRelationService;

    @Autowired

    private ExaminationDictService dictService;

    @Autowired

    private PaperProgressService paperProgressService;

    @Autowired

    private ExaminationCourseService examService;

    @Autowired

    private PrintService printService;



    /**

  • 根据分类id查询证书
  • @param classificationId 分类id
  • @param mobile 手机号
  • @return
  • */

    public SingleResponse

    selectCertificate(String classificationId, String mobile) {

    PaperCertificateQry build =

    PaperCertificateQry.builder().classificationId(classificationId).mobile

    (mobile).build();

    SingleResponse execute =

    paperCertificateQry.execute(build);

    if (execute.isSuccess()) {

    return SingleResponse.of(execute.getData());

    } else {


    本文整理自实践经验,如有问题欢迎交流讨论。


    评论