当前位置: 首页 > article >正文

Spring Boot - 数据库集成02 - 集成JPA

集成JPA

文章目录

  • 集成JPA
    • 一:JPA概述
      • 1:JPA & JDBC
      • 2:JPA规范
      • 3:JPA的状态和转换关系
    • 二:Spring data JPA
      • 1:JPA_repository
        • 1.1:CurdRepostory<T, ID>
        • 1.2:PagingAndSortingRepository
        • 1.3:使用测试
      • 2:自定义查询
        • 2.1:JPQL & SQL -> @Query
        • 2.2:规定方法名
        • 2.3:Query by Example(了解)
        • 2.4:Specifications(繁琐,了解)
        • 2.5:Querydsl(了解)
      • 3:多表关联四个相关注解
        • 3.1:@OneToOne
        • 3.2:@OneToMany
        • 3.3:@ManyToOne
        • 3.4:@ManyToMany
    • 三:spring boot JPA实战
      • 1:数据库准备
      • 2:项目整体结构
      • 3:pom依赖和配置说明
      • 4:常量类和实体类
      • 5:查询条件bean封装
      • 6:dao层接口
      • 7:service接口及其实现
      • 8:控制层实现
      • 9:测试运行

一:JPA概述

1:JPA & JDBC

JPA和JDBC都和数据库的操作有关,JDBC和JPA都是一组规范的接口,都是SUN推出的。JPA是JDBC的升华版本[依赖于JDBC]

JDBC是由各个关系型数据库实现的, JPA是由ORM框架实现的【0RM -> Object Relational Mapping对象关系映射】

JDBC使用SQL语句和数据库进行通信,JPA用面向对象方式,通过ORM框架生成SQL,进行操作

JDBC

在这里插入图片描述

JPA

在这里插入图片描述
JPA简化了持久化操作的开发工作,让开发者从繁琐的JDBC和SQL代码中解脱出来,直接面向对象持久化操作

Hibernate是实现了JPA接口的ORM框架

也就是说JPA是一套ORM规范,Hibernate实现了JPA规范:全自动的ORM框架【MyBatis是半自动的】

在这里插入图片描述

2:JPA规范

JPA规范为我们提供了下面的内容:

ORM映射元数据

JPA支持XML和注解两种元数据的形式

元数据描述表和对象之间的映射关系,框架基于此将实体对象持久化到数据库表中

例如:@Entity, @Table, @Id, @Column

JPA的API

用来操作实体对象,执行CRUD操作,框架在后台替我们完成了所有的事情,从繁琐的JDBC和SQL中解脱

例如:entityManager.merge(T t)

JPQL查询语言

通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合

例如:select * from Student s where s.name = ?

3:JPA的状态和转换关系

  1. 临时状态(new):刚刚创建出来的状态的,没有和entityManager发生关系,没有被持久化,不处于entityManager中的对象,通过persist()进入持久态
  2. 持久状态(managed):与entityManager发生关系,已经被持久化,可以将持久化的状态当做实实在在的数据库的记录
  3. 删除状态(removed):执行remove方法,事务提交之前
  4. 游离状态(detached):提交到数据后,事务commit之后的状态,由于事务已经提交,属性如何改变都不会同步到数据库,不在持久化的上下文中

persist() -> for insert

  1. 如果A是临时状态,触发persist之后,将会进行被管理状态
  2. 如果A是被管理状态,触发persist之后,状态不会发生改变,但是系统仍然会insert操作
  3. 如果A是删除状态,调用persist之后,将会转成被管理状态
  4. 如果A是游离状态,触发persist之后,会抛出异常

merge() -> for update

  1. 如果A是临时状态,调用merge之后,会根据A产生一个新的被管理状态实例A2
  2. 如果A是被管理状态,调用merge之后,状态不会发生改变,但是系统仍然会upsert操作
  3. 如果A是删除状态,调用merge之后,将会抛出异常
  4. 如果A是游离状态,调用merge之后,会将A的修改提交到数据库,并返回一个新的被管理状态实例A2

refresh()

  1. 如果A是临时状态,触发refresh之后,不会发生任何的操作,但是可能会抛出异常【取决于JPA实现】
  2. 如果A是被管理状态,触发refresh之后,属性将会和数据库同步
  3. 如果A是删除状态,调用refresh之后,会抛出异常
  4. 如果A是游离状态,触发refresh之后,会抛出异常

remove() -> for delete

  1. 如果A是临时状态,触发remove之后,不会发生任何的操作,但是系统仍然会delete操作
  2. 如果A是被管理状态,触发remove之后,状态会转成删除状态
  3. 如果A是删除状态,调用remove之后,不会进行任何的操作
  4. 如果A是游离状态,触发remove之后,会抛出异常

二:Spring data JPA

Spring data JPA通过JDK动态代理,封装了JPA的底层实现

在这里插入图片描述

1:JPA_repository

1.1:CurdRepostory<T, ID>

curd repostory顾名思义,提供了操作Spring data的基本的增删改查方法,所有spring data下的数据库都有这个

package org.springframework.data.repository;

import java.util.Optional;

@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
    // 增、改 -> save
    <S extends T> S save(S entity);
    <S extends T> Iterable<S> saveAll(Iterable<S> entities);

    // 通过 id 查询
    Optional<T> findById(ID id);
    boolean existsById(ID id);
    Iterable<T> findAll();
    Iterable<T> findAllById(Iterable<ID> ids);

    // 计数
    long count();

    // delete
    void deleteById(ID id); // 通过id删除
    void delete(T entity); // 通过传入实体删除
    void deleteAllById(Iterable<? extends ID> ids); // 通过ids删除
    void deleteAll(Iterable<? extends T> entities); // 通过传入的实体删除所有
    void deleteAll(); // 删除所有的
}
1.2:PagingAndSortingRepository

继承自curd repostory,在原有的基础上增加了排序和分页的两个泛型方法

@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
    // 排序
    Iterable<T> findAll(Sort sort);
    // 分页
    Page<T> findAll(Pageable pageable);
}
1.3:使用测试

在使用的时候,只需要让自己的xxxRepository继承PagingAndSortingRepository即可

public interface CustomerRepository extends PagingAndSortingRepository<Customer, Long> {
    // ....
}
@Test
public void pageTest() {
    Pageable pageable = PageRequest.of(0, 3);
    Page<Customer> all = customerRepository.findAll(pageable);
    System.out.println(all.getTotalPages());
    System.out.println(all.getTotalElements());
    System.out.println(all.getContent());
}

@Test
public void sortTest() {
    Sort sort = Sort.by("custId").descending();
    Iterable<Customer> all = customerRepository.findAll(sort);
    System.out.println(all);
}

@Test
public void sortTypeTest() {
    Sort.TypedSort<Customer> sort = Sort.sort(Customer.class);
    Sort descending = sort.by(Customer::getCustId).descending();
    Iterable<Customer> all = customerRepository.findAll(descending);
    System.out.println(all);
}

@Test
public void pageAndSortTest() {
    // 通过custId倒序排列
    Sort sort = Sort.by("custId").descending();
    // 分页,且排序规则是sort
    Pageable pageable = PageRequest.of(0, 3, sort);
    // 拿到结果
    Page<Customer> all = customerRepository.findAll(pageable);
    System.out.println(all.getTotalPages());
    System.out.println(all.getTotalElements());
    System.out.println(all.getContent());
}

2:自定义查询

2.1:JPQL & SQL -> @Query

主要通过@Query注解

package com.mytest.repositories;

import com.mytest.pojo.Customer;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

public interface CustomerRepository extends PagingAndSortingRepository<Customer, Long> {

    // 查询1 ? + 参数顺序,例如 ?1 -> 第一个参数
    // nativeQuery 说明可以使用原生的sql语句
    @Query(value = "select * from cst_customer as c where c.cust_name = ?1", nativeQuery = true)
    List<Customer> findCustomerByCustName(String custName);

    // 也可以使用@Param注解的方式
    @Query(value = "select c from Customer as c where c.custName=:custName")
    List<Customer> findCustomerByCustName2(@Param("custName") String custName);

    // 增加,修改,删除,必须使用事务
    @Transactional
    @Modifying // 通知jpa是增删改的操作
    @Query(value = "update Customer as c set c.custName=:custName where c.custId=:custId")
    int updateCustomer(@Param("custName") String custName, @Param("custId") Long id);

    // 增加,修改,删除,必须使用事务
    @Transactional
    @Modifying // 通知jpa是增删改的操作
    @Query(value = "delete from Customer as c where c.custId=:id")
    int deleteCustomerByCustId(@Param("id") Long id);
}
2.2:规定方法名

JPA支持的查询方法谓词关键字和修饰符决定查询条件,只要定义的方法名满足规范,就不用书写查询SQL

规范如下:主题关键字决定当前方法的作用

在这里插入图片描述

// find + 对象名 + By + 字段名
List<Customer> findCustomerByCustName(String custName);
// exists + By + 字段名
boolean existsByCustName(String custName);
2.3:Query by Example(了解)

只支持查询,只支持字符串

  • 不支持嵌套或者分组的属性约束
  • 只支持字符串, start/contains/ends/regex匹配和其他类型的精确匹配

1:在repository中添加继承QueryByExampleExecutor

package com.mytest.repositories;

import com.mytest.pojo.Customer;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.QueryByExampleExecutor;

public interface CustomerQbeRepository extends PagingAndSortingRepository<Customer, Long>, QueryByExampleExecutor<Customer> {

}

2:直接使用

package com.mytest.test;

import com.mytest.pojo.Customer;
import com.mytest.repositories.CustomerQbeRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.Collections;
import java.util.List;

@ContextConfiguration("/spring.xml") // 指定要加载的配置类
@RunWith(SpringJUnit4ClassRunner.class) // junit4
public class QbeTest {

    @Autowired
    private CustomerQbeRepository customerQbeRepository;

    @Test
    public void findTest() {
        // 可以动态的指定
        Customer customer = new Customer();
        customer.setCustName("cui haida");
        // 构建example
        Example<Customer> example = Example.of(customer);
        List<Customer> customerList = (List<Customer>) customerQbeRepository.findAll(example);
        System.out.println(customerList);
    }

    @Test
    public void findByMatcherTest() {
        // 可以动态的指定条件
        Customer customer = new Customer();
        customer.setCustName("cui haida");
        customer.setCustAddress("天津");
        // 构建matcher,对条件行为进行设置
        ExampleMatcher matching = ExampleMatcher.matching()
                .withIgnorePaths("custAddress") // 忽略的字段
                .withIgnoreCase() // 忽略大小写
                .withStringMatcher(ExampleMatcher.StringMatcher.ENDING); // 按照结尾匹配
        // 构建example, 将matcher装载
        Example<Customer> example = Example.of(customer, matching);
        List<Customer> customerList = (List<Customer>) customerQbeRepository.findAll(example);
        System.out.println(customerList);
    }
}
2.4:Specifications(繁琐,了解)

支持对所有类型的动态查询

1:继承接口

package com.mytest.repositories;

import com.mytest.pojo.Customer;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.PagingAndSortingRepository;

public interface CustomerSpecificationsRepository extends PagingAndSortingRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {
}

Specification中只有一个方法没有实现,就是toPredicate

@Nullable
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder);
  • root -> 可以视为from Customer,可以从root中得到列
  • criteriaBuilder -> 可以视为where, 可以设置各种条件
  • query -> 可以视为组合(order by, where)
package com.mytest.test;

import com.mytest.pojo.Customer;
import com.mytest.repositories.CustomerSpecificationsRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.*;
import java.util.ArrayList;
import java.util.List;

@ContextConfiguration("/spring.xml") // 指定要加载的配置类
@RunWith(SpringJUnit4ClassRunner.class) // junit4
public class SpecificationsTest {

    @Autowired
    private CustomerSpecificationsRepository customerSpecificationsRepository;

    @Test
    public void findTest() {

        Customer customer = new Customer();
        customer.setCustId(1L);
        customer.setCustName("cui haida");

        // 需要实现函数式接口
        List<Customer> customerList = customerSpecificationsRepository.findAll(new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                // 拿到对应的字段
                Path<Long> custId = root.get("custId");
                Path<String> custName = root.get("custName");
                Path<String> custAddress = root.get("custAddress");
                // 设置where条件
                List<Predicate> predicateList = new ArrayList<>();
                if (!StringUtils.isEmpty(customer.getCustAddress())) {
                    predicateList.add(criteriaBuilder.equal(custAddress, "内蒙古"));
                }
                if (customer.getCustId() != null) {
                    predicateList.add(criteriaBuilder.greaterThan(custId, 0L));
                }
                Predicate and = criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));
                // 设置排序规则
                Order order = criteriaBuilder.desc(custId);
                
                return query.where(and).orderBy(order).getRestriction();
            }
        });
        customerList.forEach(System.out::println);
    }
}
2.5:Querydsl(了解)

独立的,通用的查询框架,不属于JPA规范

官网:http://querydsl.com/

package com.mytest.repositories;

import com.mytest.pojo.Customer;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.PagingAndSortingRepository;

public interface CustomerQueryDslRepository extends PagingAndSortingRepository<Customer, Long>, QuerydslPredicateExecutor<Customer> {
}
package com.mytest.test;

import com.mytest.repositories.CustomerQueryDslRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@ContextConfiguration("/spring.xml") // 指定要加载的配置类
@RunWith(SpringJUnit4ClassRunner.class) // junit4
public class QuerydslTest {

    @Autowired
    private CustomerQueryDslRepository customerQueryDslRepository;

    @Test
    public void findTest() {
        // 创建对应的Q类
        // 项目结构中将Q类加入到sources中即可
        // spring data JPA官方文档 4.8节
    }
}
public interface QuerydslPredicateExecutor<T> {

  Optional<T> findById(Predicate predicate);  (1)

  Iterable<T> findAll(Predicate predicate);   (2)

  long count(Predicate predicate);            (3)

  boolean exists(Predicate predicate);        (4)

  // … more functionality omitted.
}

1:查找并返回与Predicate.
2:查找并返回与Predicate.
3:返回与 匹配的实体数Predicate4:返回匹配的实体是否Predicate存在。

3:多表关联四个相关注解

3.1:@OneToOne

单向一对一

package com.mytest.pojo;


import lombok.Data;

import javax.persistence.*;

@Data
@Entity
@Table(name = "tb_customer")
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long custId;

    @Column(name = "cust_name")
    private String custName;

    @Column(name  ="cust_address")
    private String custAddress;

    // ------------------- cascade设置关联操作 --------------------
    // 1:persist 表示只有插入的时候同步插入
    // 2:all 所有的操作都进行关联同步 <------- 一般都是这个
    // 3:merge 只有更新的时候关联更新
    // 4:remove 只有删除的时候关联删除
    
    // ------------------ fetch设置懒加载还是立即加载 --------------
    // 1:eager 立即加载,默认
    // 2:lazy 懒加载,只有用到的时候才会去查询,因为不一定所有的关联对象都会用到,LAZY下要配置事务
    // 查询默认支持关联
    
    // ----------------- orphanRemoval设置关联移除(通常修改的时候会用到) -------------------------
    // 一旦将关联的数据设置成为null, 或者修改为其他的数据,如果想删除关联数据,设置为true
    
    // ------------------optional设置关联字段是否可以为空---------------------------
    // true -> 可以为空,false -> 不能为空
    
    // id <-> account_id,查询的时候自然的上account表中拿到对应的account信息(where user.id = account.account_id)
    @OneToOne(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY, orphanRemoval = true, optional=false)
    @JoinColumn(name = "account_id") // 指明关联的外键
    private Account account;
}
package com.mytest.pojo;

import lombok.Data;

import javax.persistence.*;

@Data
@Entity
@Table(name = "tb_account")
public class Account {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long accountId;

    private String userName;

    private String password;
}

repository类

package com.mytest.repository;

import com.mytest.pojo.Customer;
import org.springframework.data.repository.PagingAndSortingRepository;

public interface CustomerRepository extends PagingAndSortingRepository<Customer, Long> {
}

测试类

package com.mytest;

import com.mytest.config.SpringBootJpaConfig;
import com.mytest.pojo.Account;
import com.mytest.pojo.Customer;
import com.mytest.repository.CustomerRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;

@ContextConfiguration(classes = SpringBootJpaConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class OneToOneTest {

    @Autowired
    private CustomerRepository customerRepository;

    @Test
    public void saveTest() {
        // define data
        Account account = new Account();
        account.setUserName("张三");

        Customer customer = new Customer();
        customer.setCustName("张三");
        customer.setAccount(account);

        customerRepository.save(customer);
    }

    @Test
    // 如果是懒加载,就要指定配置事务readOnly = true
    // 因为当repository调用完查询方法, session就会立即关闭,一旦关闭就无法执行查询了,所以要定义事务,保证session不关闭
    @Transactional(readOnly = true)
    public void findTest() {
        Optional<Customer> customer = customerRepository.findById(1L);
        if (customer.isPresent()) {
            System.out.println(customer);
        }
    }
    
    @Test
    public void updateTest() {
        Customer customer = new Customer();
        customer.setCustId(1L);
        customer.setCustName("崔海达");
        customer.setCustAddress("内蒙古");
        customer.setAccount(null);
        customerRepository.save(customer); // 主要测试orphanRemoval
    }
}

双向一对一

重点就是一方要放弃对外键的控制约束,需要在一方中的@OneToOne注解中指定mappedBy属性,指定外键约束由哪一方维护

@OneToOne(
    mappedBy = "customer", // 指定外键约束由customer维护
    cascade = CascadeType.PERSIST, // 指定persist模式,表示只有插入的时候同步插入
    fetch = FetchType.LAZY, // 只有用到的时候才会去查询,因为不一定所有的关联对象都会用到,LAZY下要配置事务
    orphanRemoval = true // 一旦将关联的数据设置成为null, 或者修改为其他的数据,如果想删除关联数据,设置为true
)
3.2:@OneToMany
// 一个consumer对应多个message,其他的参数都一样
// 指定cascade,fetch在这里默认的是懒加载(懒加载的优势是提升性能),其他的属性和一对一一致
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name="customer_id") // Message表中的外键名称是啥
private List<Message> messages;
// 测试下
public void createTest() {
    List<Message> messageList = new ArrayList<>();
    messageList.add(new Message("你好"));
    messageList.add(new Message("在吗?"));
    Customer customer = new Customer();
    customer.setCustName("诸葛亮");
    customer.setCustAddress("南阳");
    customer.setMessages(messageList);
    customerRepository.save(customer);
}
3.3:@ManyToOne

1:创建实体和repository

@ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name = "customer_id")  // 可以指定也可以不指定,因为在一对多的一方已经指定了
private Customer customer;

2:测试CURD,测试说明:

  • 在多对一的一方插入是更加合理的
  • 在一对多的一方查询是更加合理的
  • 不应该设置@ManyToOne中的cascade = CascadeType.REMOVE[!!!]
public void findTest() {
    Customer customer = new Customer();
    customer.setCustId(1L);
    customer.setCustName("诸葛亮");
    List<Message> messageList = messageRepository.findByCustomer(customer);
    for (Message message : messageList) {
        System.out.println(message);
    }
}
3.4:@ManyToMany

在这里插入图片描述

package com.mytest.pojo;

import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;
import java.util.List;

@Data
@NoArgsConstructor
@Entity
@Table(name = "tb_role")
public class Role {
    
    // ......

    // 维护双向多对多
    @ManyToMany(cascade = CascadeType.ALL)
    private List<Customer> customerList;
    
    // ......
}
package com.mytest.pojo;


import lombok.Data;

import javax.persistence.*;
import java.util.List;

@Data
@Entity
@Table(name = "tb_customer")
public class Customer {
    
    // ..........

    // 维护多对多
    @ManyToMany(cascade = CascadeType.PERSIST)
    // joinTable维护中间表
    @JoinTable(name = "tb_customer_role", // 中间表的名字
            joinColumns = {@JoinColumn(name = "c_id")}, // 设置本表的外键名称
            inverseJoinColumns = {@JoinColumn(name = "r_id")} // 设置关联表的外键名称
    )
    private List<Role> roleList;
    
    // .......
}

测试CURD

@ContextConfiguration(classes = SpringBootJpaConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class ManyToManyTest {
    @Autowired
    private CustomerRepository repository;

    @Test
    public void createTest() {
        // 创建role信息
        List<Role> roleList = new ArrayList<>();
        roleList.add(new Role("admin"));
        roleList.add(new Role("super admin"));
        // 创建customer
        Customer customer = new Customer();
        customer.setCustName("崔海达");
        // set role
        customer.setRoleList(roleList);
        // 保存
        repository.save(customer);
    }

    @Test
    @Transactional(readOnly = true)
    public void findTest() {
        Optional<Customer> customer = repository.findById(1L);
        customer.ifPresent(System.out::println);
    }

    @Test
    public void deleteTest() {
        repository.deleteById(1L);
    }

    @Test
    @Transactional
    @Commit // 只适用于单元测试
    public void findAndDeleteTest() {
        Optional<Customer> customer = repository.findById(1L);
        if (customer.isPresent()) {
            System.out.println(customer.get());
            repository.delete(customer.get());
        }
    }
}

三:spring boot JPA实战

1:数据库准备

用户表,角色表和用户角色表。

DROP TABLE IF EXISTS `tb_role`;
CREATE TABLE `tb_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `role_key` varchar(255) NOT NULL,
  `description` varchar(255) DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

LOCK TABLES `tb_role` WRITE;
INSERT INTO `tb_role` VALUES (1,'admin','admin','admin','2021-09-08 17:09:15','2021-09-08 17:09:15');
UNLOCK TABLES;


DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(45) NOT NULL,
  `password` varchar(45) NOT NULL,
  `email` varchar(45) DEFAULT NULL,
  `phone_number` int(11) DEFAULT NULL,
  `description` varchar(255) DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;


LOCK TABLES `tb_user` WRITE;
INSERT INTO `tb_user` VALUES (1,'pdai','dfasdf','suzhou.daipeng@gmail.com',1212121213,'afsdfsaf','2021-09-08 17:09:15','2021-09-08 17:09:15');
UNLOCK TABLES;


DROP TABLE IF EXISTS `tb_user_role`;
CREATE TABLE `tb_user_role` (
  `user_id` int(11) NOT NULL,
  `role_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

LOCK TABLES `tb_user_role` WRITE;
INSERT INTO `tb_user_role` VALUES (1,1);
UNLOCK TABLES;

2:项目整体结构

在这里插入图片描述

3:pom依赖和配置说明

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.cui</groupId>
    <artifactId>jpa_demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>jpa_demo</name>
    <description>jpa_demo</description>
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.6.13</spring-boot.version>
    </properties>
    <dependencies>

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>com.github.wenhao</groupId>
            <artifactId>jpa-spec</artifactId>
            <version>3.2.4</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </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-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>com.cui.jpa_demo.JpaDemoApplication</mainClass>
                    <skip>true</skip>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>
spring:
  # swagger配置
  mvc:
    path match:
      # 由于 springfox 3.0.x 版本 和 Spring Boot 2.6.x 版本有冲突,所以还需要先解决这个 bug
      matching-strategy: ANT_PATH_MATCHER
  # 数据源配置
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mytest?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver # 8.0 +
    username: 用户名
    password: 密码
  # JPA 配置
  jpa:
    generate-ddl: false # 是否自动创建数据库表
    show-sql: true # 是否打印生成的 sql
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL8Dialect # 数据库方言 mysql8
        format_sql: true # 是否格式化 sql
        use_new_id_generator_mappings: true # 是否使用新的 id 生成器

4:常量类和实体类

package com.cui.jpa_demo.constant;

import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * 响应状态
 * @author cui haida
 * 2025/1/23
 */
@Getter
@AllArgsConstructor
public enum ResponseStatus {
    // 成功失败通用状态
    SUCCESS("200", "success"),
    FAIL("500", "failed"),
    // http 状态
    HTTP_STATUS_200("200", "ok"),
    HTTP_STATUS_400("400", "request error"),
    HTTP_STATUS_401("401", "no authentication"),
    HTTP_STATUS_403("403", "no authorities"),
    HTTP_STATUS_500("500", "server error");

    public static final List<ResponseStatus> HTTP_STATUS_ALL = Collections.unmodifiableList(
            Arrays.asList(HTTP_STATUS_200, HTTP_STATUS_400, HTTP_STATUS_401, HTTP_STATUS_403, HTTP_STATUS_500
            ));

    /**
     * response code
     */
    private final String responseCode;

    /**
     * description.
     */
    private final String description;
}
package com.cui.jpa_demo.entity.response;

import com.cui.jpa_demo.constant.ResponseStatus;
import lombok.Builder;
import lombok.Data;

import java.io.Serializable;

/**
 * @author cui haida
 * 2025/1/23
 */
@Data
@Builder
public class ResponseResult<T> {

    /**
     * response timestamp.
     */
    private long timestamp;

    /**
     * response code, 200 -> OK.
     */
    private String status;

    /**
     * response message.
     */
    private String message;

    /**
     * response data.
     */
    private T data;

    /**
     * response success result wrapper.
     *
     * @param <T> type of data class
     * @return response result
     */
    public static <T> ResponseResult<T> success() {
        return success(null);
    }

    /**
     * response success result wrapper.
     *
     * @param data response data
     * @param <T>  type of data class
     * @return response result
     */
    public static <T> ResponseResult<T> success(T data) {
        return ResponseResult.<T>builder()
                .data(data)
                .message(ResponseStatus.SUCCESS.getDescription())
                .status(ResponseStatus.SUCCESS.getResponseCode())
                .timestamp(System.currentTimeMillis())
                .build();
    }

    /**
     * response error result wrapper.
     *
     * @param message error message
     * @param <T>     type of data class
     * @return response result
     */
    public static <T extends Serializable> ResponseResult<T> fail(String message) {
        return fail(null, message);
    }

    /**
     * response error result wrapper.
     *
     * @param data    response data
     * @param message error message
     * @param <T>     type of data class
     * @return response result
     */
    public static <T> ResponseResult<T> fail(T data, String message) {
        return ResponseResult.<T>builder()
                .data(data)
                .message(message)
                .status(ResponseStatus.FAIL.getResponseCode())
                .timestamp(System.currentTimeMillis())
                .build();
    }
}

package com.cui.jpa_demo.entity;

import java.io.Serializable;

/**
 * @author cui haida
 * 2025/1/23
 */
public class BaseEntity implements Serializable {
}
package com.cui.jpa_demo.entity.model;

import com.cui.jpa_demo.entity.BaseEntity;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.Set;

/**
 * <p>
 * 功能描述:
 * </p>
 *
 * @author cui haida
 * @date 2024/04/13/17:40
 */
@Getter
@Setter
@ToString
@Entity
@Table(name = "tb_user")
public class User extends BaseEntity {
    /**
     * user id.
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Long id;

    /**
     * username.
     */
    private String userName;

    /**
     * user pwd.
     */
    private String password;

    /**
     * email.
     */
    private String email;

    /**
     * phoneNumber.
     */
    private long phoneNumber;

    /**
     * description.
     */
    private String description;

    /**
     * create date time.
     */
    private LocalDateTime createTime;

    /**
     * update date time.
     */
    private LocalDateTime updateTime;

    // 多对多关系
    // 一个用户可能有多个角色
    // 一个角色类型可能被多个用户拥有
    // ------------------- cascade设置关联操作 --------------------
    // 1:persist表示只有插入的时候同步插入
    // 2:all所有的操作都进行关联同步
    // 3:merge只有更新的时候关联更新
    // 4:remove只有删除的时候关联删除
    // ------------------ fetch设置懒加载还是立即加载 --------------
    // 1:eager立即加载,默认
    // 2:lazy懒加载,只有用到的时候才会去查询,因为不一定所有的关联对象都会用到,LAZY下要配置事务
    // 查询默认支持关联
    // ----------------- orphanRemoval设置关联移除(通常修改的时候会用到) -------------------------
    // 一旦将关联的数据设置成为null, 或者修改为其他的数据,如果想删除关联数据,设置为true
    // ------------------optional 关联字段能不能为空 -----------------------
    // true -> 可以为空,false -> 不能为空
    @ManyToMany(cascade = {CascadeType.REFRESH}, fetch = FetchType.EAGER)
    // inner join user_id = role_id
    @JoinTable(
            name = "tb_user_role", // 中间表的名称
            joinColumns = {@JoinColumn(name = "user_id")}, // 设置本表的外键名称
            inverseJoinColumns = {@JoinColumn(name = "role_id")} // 设置关联表的外键名称
    )
    private Set<Role> roles;
}
package com.cui.jpa_demo.entity.model;

import com.cui.jpa_demo.entity.BaseEntity;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.proxy.HibernateProxy;

import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.Objects;

/**
 * @author cui haida
 * 2025/1/23
 */
@Getter
@Setter
@ToString
@Entity
@Table(name = "tb_role")
public class Role extends BaseEntity {
    /**
     * role id.
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增主键
    @Column(name = "id", nullable = false)
    private Long id;

    /**
     * role name.
     */
    private String name;

    /**
     * role key.
     */
    private String roleKey;

    /**
     * description.
     */
    private String description;

    /**
     * create date time.
     */
    private LocalDateTime createTime;

    /**
     * update date time.
     */
    private LocalDateTime updateTime;

    /**
     * 重写equals方法
     */
    @Override
    public final boolean equals(Object o) {
        // 满足自反性,满足对称性,满足传递性,满足一致性,满足空对象处理
        // -- 自反性保证
        if (this == o) {
            return true;
        }
        // -- 空对象的处理
        if (o == null) {
            return false;
        }

        // -- 获取对象的实际类型,然后比较
        Class<?> oEffectiveClass = o instanceof HibernateProxy ? ((HibernateProxy) o).getHibernateLazyInitializer().getPersistentClass() : o.getClass();
        Class<?> thisEffectiveClass = this instanceof HibernateProxy ? ((HibernateProxy) this).getHibernateLazyInitializer().getPersistentClass() : this.getClass();
        if (thisEffectiveClass != oEffectiveClass) {
            return false;
        }
        // 判断是不是id相同,如果相同就认为是equal
        Role role = (Role) o;
        return getId() != null && Objects.equals(getId(), role.getId());
    }

    /**
     * 重写hashCode方法
     */
    @Override
    public final int hashCode() {
        return this instanceof HibernateProxy ? ((HibernateProxy) this).getHibernateLazyInitializer().getPersistentClass().hashCode() : getClass().hashCode();
    }
}

5:查询条件bean封装

package com.cui.jpa_demo.entity.bean;

import lombok.Builder;
import lombok.Data;

/**
 * 用户查询条件bean
 * @author cui haida
 * 2025/1/23
 */
@Data
@Builder
public class UserQueryBean {

    private String name;
    private String description;
}
package com.cui.jpa_demo.entity.bean;

import lombok.Builder;
import lombok.Data;

/**
 * 用户查询条件bean
 * @author cui haida
 * 2025/1/23
 */
@Data
@Builder
public class RoleQueueBean {
    private String name;
    private String description;
}

6:dao层接口

package com.cui.jpa_demo.dao;

import com.cui.jpa_demo.entity.BaseEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.NoRepositoryBean;

import java.io.Serializable;


/**
 * 基础dao
 * @author cuihaida
 * 2025/1/23
 * 什么是JpaRepository ?
 * - JpaRepository 是 Spring Data JPA 提供的一个接口,它扩展了 PagingAndSortingRepository 接口
 * - 提供了对 JPA 实体的 CRUD 操作、分页和排序功能。以下是 JpaRepository 的一些关键特性和用法:
 * - CRUD操作:继承自 CrudRepository,提供了基本的增删改查方法。
 * - 分页查询:提供了 findAll(Pageable pageable) 等方法来支持分页查询。
 * - 排序查询:支持通过 Sort 对象进行排序查询。
 * - 批量删除:提供了 deleteInBatch(Iterable<T> entities) 和 deleteAllInBatch() 方法
 * 注意事项 ?
 * - JpaRepository 提供的方法默认是事务性的。
 * - 可以通过在接口中定义方法名来创建自定义查询,Spring Data JPA 会根据方法名解析出查询逻辑。
 * - 如果需要更复杂的查询,可以使用 @Query 注解来编写 JPQL 或原生 SQL 查询。
 * 两个参数?
 * - 实体类 (Entity Class):表示与数据库表对应的 JPA 实体类
 * - 主键类型:表示实体类的主键类型
 * 什么是JpaSpecificationExecutor ?
 * - JpaSpecificationExecutor 是 Spring Data JPA 提供的一个接口,它扩展了 JpaRepository 的功能
 * - 允许你使用 Specification 对象来构建动态查询。
 * - Specification 是一种用于定义查询条件的接口,可以组合多个条件以实现复杂的查询逻辑。
 * JpaSpecificationExecutor 关键特性 ?
 * - 动态查询:通过 Specification 可以构建灵活且可复用的查询条件。
 * - 组合查询条件:可以将多个 Specification 对象组合起来,形成更复杂的查询条件。
 * - 类型安全:使用 Criteria API 实现,提供了编译时检查和类型安全。
 * JpaSpecificationExecutor 常用方法 ?
 * - JpaSpecificationExecutor 接口提供了以下常用方法:
 * - T findOne(Specification<T> spec):根据给定的 Specification 查找单个实体。
 * - List<T> findAll(Specification<T> spec):根据给定的 Specification 查找所有匹配的实体。
 * - Page<T> findAll(Specification<T> spec, Pageable pageable):根据给定的 Specification 和分页信息查找实体。
 * - List<T> findAll(Specification<T> spec, Sort sort):根据给定的 Specification 和排序信息查找实体。
 * - long count(Specification<T> spec):根据给定的 Specification 统计匹配的实体数量。
 */
@NoRepositoryBean // 告诉 Spring Data JPA 这个接口不是个 repository
// 指明实体类是BaseEntity的子类,主键类型是I,是可序列化的对象类型
public interface IBaseDao <T extends BaseEntity, I extends Serializable>
        extends JpaRepository<T, I>, JpaSpecificationExecutor<T> {
}
package com.cui.jpa_demo.dao;

import com.cui.jpa_demo.entity.model.User;
import org.springframework.stereotype.Repository;

/**
 * @author cui haida
 * 2025/1/23
 */
@Repository
public interface IUserDao extends IBaseDao<User, Long> {
}

package com.cui.jpa_demo.dao;

import com.cui.jpa_demo.entity.model.Role;
import org.springframework.stereotype.Repository;

/**
 * @author cuihaida
 * 2025/1/23
 */
@Repository
public interface IRoleDao extends IBaseDao<Role, Long> {

}

7:service接口及其实现

package com.cui.jpa_demo.service;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;

import java.io.Serializable;
import java.util.List;

/**
 * service基类
 * @author cui haida
 * 2025/1/23
 */
public interface IBaseService<T, I extends Serializable> {
    /**
     * 通过Id寻找
     * @param id id
     * @return T
     */
    T find(I id);

    /**
     * 查找全部
     * @return List
     */
    List<T> findAll();

    /**
     * 查找指定id集合的对象集合
     * @param ids ids
     * @return List
     */
    List<T> findList(I[] ids);

    /**
     * 查找指定id集合的对象集合
     * @param ids ids
     * @return List
     */
    List<T> findList(Iterable<I> ids);

    /**
     * 分页findAll
     * @param pageable pageable
     * @return Page
     */
    Page<T> findAll(Pageable pageable);

    /**
     * 序列分页
     * @param spec     spec
     * @param pageable pageable
     * @return Page
     */
    Page<T> findAll(Specification<T> spec, Pageable pageable);

    /**
     * 查找一个
     * @param spec spec
     * @return T
     */
    T findOne(Specification<T> spec);

    /**
     * count.
     *
     * @return long
     */
    long count();

    /**
     * count.
     *
     * @param spec spec
     * @return long
     */
    long count(Specification<T> spec);

    /**
     * exists.
     *
     * @param id id
     * @return boolean
     */
    boolean exists(I id);

    /**
     * save.
     *
     * @param entity entity
     */
    void save(T entity);

    /**
     * save.
     *
     * @param entities entities
     */
    void save(List<T> entities);

    /**
     * update.
     *
     * @param entity entity
     * @return T
     */
    T update(T entity);

    /**
     * delete.
     *
     * @param id id
     */
    void delete(I id);

    /**
     * delete by ids.
     *
     * @param ids ids
     */
    void deleteByIds(List<I> ids);

    /**
     * delete.
     *
     * @param entities entities
     */
    void delete(T[] entities);

    /**
     * delete.
     *
     * @param entities entities
     */
    void delete(Iterable<T> entities);

    /**
     * delete.
     *
     * @param entity entity
     */
    void delete(T entity);

    /**
     * delete all.
     */
    void deleteAll();

    /**
     * find list.
     *
     * @param spec spec
     * @return list
     */
    List<T> findList(Specification<T> spec);

    /**
     * find list.
     *
     * @param spec spec
     * @param sort sort
     * @return List
     */
    List<T> findList(Specification<T> spec, Sort sort);


    /**
     * flush.
     */
    void flush();
}
package com.cui.jpa_demo.service.impl;

import com.cui.jpa_demo.dao.IBaseDao;
import com.cui.jpa_demo.entity.BaseEntity;
import com.cui.jpa_demo.service.IBaseService;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.Arrays;
import java.util.List;

/**
 * BaseDoServiceImpl 基础service服务,封装通用service的功能实现
 * @author cui haida
 * 2025/1/23
 */
@Slf4j
@Transactional
public abstract class BaseDoServiceImpl<T extends BaseEntity, I extends Serializable> implements IBaseService<T, I> {

    // 获取 BaseDao
    public abstract IBaseDao<T, I> getBaseDao();

    /**
     * findById.
     *
     * @param id id
     * @return T
     */
    @Override
    public T find(I id) {
        return getBaseDao().findById(id).orElse(null);
    }

    /**
     * @return List
     */
    @Override
    public List<T> findAll() {
        return getBaseDao().findAll();
    }

    /**
     * @param ids ids
     * @return List
     */
    @Override
    public List<T> findList(I[] ids) {

        List<I> idList = Arrays.asList(ids);

        return getBaseDao().findAllById(idList);
    }

    /**
     * find list.
     *
     * @param spec spec
     * @return list
     */
    @Override
    public List<T> findList(Specification<T> spec) {
        return getBaseDao().findAll(spec);
    }

    /**
     * find list.
     *
     * @param spec spec
     * @param sort sort
     * @return List
     */
    @Override
    public List<T> findList(Specification<T> spec, Sort sort) {
        return getBaseDao().findAll(spec, sort);
    }

    /**
     * find one.
     *
     * @param spec spec
     * @return T
     */
    @Override
    public T findOne(Specification<T> spec) {
        return getBaseDao().findOne(spec).orElse(null);
    }

    /**
     * @param pageable pageable
     * @return Page
     */
    @Override
    public Page<T> findAll(Pageable pageable) {
        return getBaseDao().findAll(pageable);
    }

    /**
     * count.
     *
     * @return long
     */
    @Override
    public long count() {
        return getBaseDao().count();
    }

    /**
     * count.
     *
     * @param spec spec
     * @return long
     */
    @Override
    public long count(Specification<T> spec) {
        return getBaseDao().count(spec);
    }

    /**
     * exists.
     *
     * @param id id
     * @return boolean
     */
    @Override
    public boolean exists(I id) {
        return getBaseDao().findById(id).isPresent();
    }

    /**
     * save.
     *
     * @param entity entity
     */
    @Override
    public void save(T entity) {
        getBaseDao().save(entity);
    }

    /**
     * save.
     *
     * @param entities entities
     */
    @Override
    public void save(List<T> entities) {
        getBaseDao().saveAll(entities);
    }

    /**
     * update.
     *
     * @param entity entity
     * @return T
     */
    @Override
    public T update(T entity) {
        return getBaseDao().saveAndFlush(entity);
    }

    /**
     * delete.
     *
     * @param id id
     */
    @Override
    public void delete(I id) {
        getBaseDao().deleteById(id);
    }

    /**
     * delete by ids.
     *
     * @param ids ids
     */
    @Override
    public void deleteByIds(List<I> ids) {
        getBaseDao().deleteAllById(ids);
    }

    /**
     * delete all.
     */
    @Override
    public void deleteAll() {
        getBaseDao().deleteAllInBatch();
    }

    /**
     * delete.
     *
     * @param entities entities
     */
    @Override
    public void delete(T[] entities) {
        List<T> tList = Arrays.asList(entities);
        getBaseDao().deleteAll(tList);
    }

    /**
     * delete.
     *
     * @param entities entities
     */
    @Override
    public void delete(Iterable<T> entities) {
        getBaseDao().deleteAll(entities);
    }

    /**
     * delete.
     *
     * @param entity entity
     */
    @Override
    public void delete(T entity) {
        getBaseDao().delete(entity);
    }

    /**
     * @param ids ids
     * @return List
     */
    @Override
    public List<T> findList(Iterable<I> ids) {
        return getBaseDao().findAllById(ids);
    }

    /**
     * @param spec     spec
     * @param pageable pageable
     * @return Page
     */
    @Override
    public Page<T> findAll(Specification<T> spec, Pageable pageable) {
        return getBaseDao().findAll(spec, pageable);
    }

    /**
     * flush.
     */
    @Override
    public void flush() {
        getBaseDao().flush();
    }
}
package com.cui.jpa_demo.service;

import com.cui.jpa_demo.entity.bean.UserQueryBean;
import com.cui.jpa_demo.entity.model.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;

/**
 * @author cuihaida
 * 2025/1/23
 */
public interface IUserService extends IBaseService<User, Long> {
    /**
     * 分页查询
     * @param userQueryBean 查询条件
     * @param pageRequest 分页请求
     * @return 查询结果
     */
    Page<User> findPage(UserQueryBean userQueryBean, PageRequest pageRequest);
}

package com.cui.jpa_demo.service.impl;

import com.cui.jpa_demo.dao.IBaseDao;
import com.cui.jpa_demo.dao.IUserDao;
import com.cui.jpa_demo.entity.bean.UserQueryBean;
import com.cui.jpa_demo.entity.model.User;
import com.cui.jpa_demo.service.IUserService;
import com.github.wenhao.jpa.Specifications;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

/**
 * @author cui haida
 * 2025/1/23
 */
@Service
public class UserDoServiceImpl extends BaseDoServiceImpl<User, Long> implements IUserService {

    /**
     * userDao.
     */
    private final IUserDao userDao;

    /**
     * init.
     *
     * @param userDao2 user dao
     */
    public UserDoServiceImpl(final IUserDao userDao2) {
        this.userDao = userDao2;
    }

    /**
     * @return base dao
     */
    @Override
    public IBaseDao<User, Long> getBaseDao() {
        return this.userDao;
    }

    /**
     * 分页查询用户信息。
     *
     * @param queryBean   查询条件封装对象
     * @param pageRequest 分页请求参数
     * @return 分页结果
     */
    @Override
    public Page<User> findPage(UserQueryBean queryBean, PageRequest pageRequest) {
        // 构建查询条件
        Specification<User> specification = Specifications.<User>and()
                // 如果名称不为空,则按名称模糊查询
                .like(StringUtils.isNotEmpty(queryBean.getName()), "user_name", queryBean.getName())
                // 如果描述不为空,则按描述模糊查询
                .like(StringUtils.isNotEmpty(queryBean.getDescription()), "description",
                        queryBean.getDescription())
                .build();
        // 根据构建的查询条件和分页请求参数,查询并返回分页结果
        return this.getBaseDao().findAll(specification, pageRequest);
    }

}

package com.cui.jpa_demo.service;

import com.cui.jpa_demo.entity.bean.RoleQueueBean;
import com.cui.jpa_demo.entity.model.Role;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;

/**
 * @author cuihaida
 * 2025/1/23
 */
public interface IRoleService extends IBaseService<Role, Long> {
    /**
     * 分页查询
     *
     * @param roleQueryBean 查询条件
     * @param pageRequest   分页条件
     * @return 分页结果
     */
    Page<Role> findPage(RoleQueueBean roleQueryBean, PageRequest pageRequest);
}

package com.cui.jpa_demo.service.impl;

import com.cui.jpa_demo.dao.IBaseDao;
import com.cui.jpa_demo.dao.IRoleDao;
import com.cui.jpa_demo.entity.bean.RoleQueueBean;
import com.cui.jpa_demo.entity.model.Role;
import com.cui.jpa_demo.service.IRoleService;
import com.github.wenhao.jpa.Specifications;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;

/**
 * @author cui haida
 * 2025/1/23
 */
public class RoleDoServiceImpl extends BaseDoServiceImpl<Role, Long> implements IRoleService {
    /**
     * roleDao.
     */
    private final IRoleDao roleDao;

    /**
     * init.
     *
     * @param roleDao2 role dao
     */
    public RoleDoServiceImpl(final IRoleDao roleDao2) {
        this.roleDao = roleDao2;
    }

    /**
     * @return base dao
     */
    @Override
    public IBaseDao<Role, Long> getBaseDao() {
        return this.roleDao;
    }

    /**
     * 根据查询条件分页查找角色信息。
     *
     * @param roleQueryBean 查询条件,包含角色名称和描述等信息
     * @param pageRequest   分页请求,包含页码和每页大小等信息
     * @return 返回符合条件的角色信息分页结果
     */
    @Override
    public Page<Role> findPage(RoleQueueBean roleQueryBean, PageRequest pageRequest) {
        // 构建查询条件
        Specification<Role> specification = Specifications.<Role>and()
                .like(StringUtils.isNotEmpty(roleQueryBean.getName()), "name", roleQueryBean.getName())
                .like(StringUtils.isNotEmpty(roleQueryBean.getDescription()), "description", roleQueryBean.getDescription())
                .build();

        // 执行查询并返回分页结果
        return this.roleDao.findAll(specification, pageRequest);
    }
}

8:控制层实现

package com.cui.jpa_demo.controller;

import com.cui.jpa_demo.entity.bean.UserQueryBean;
import com.cui.jpa_demo.entity.model.User;
import com.cui.jpa_demo.entity.response.ResponseResult;
import com.cui.jpa_demo.service.IUserService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;

/**
 * @author cui haida
 * 2025/1/23
 */
@RestController
@RequestMapping("/user")
public class UserController {

    private final IUserService userService;

    public UserController(IUserService userService) {
        this.userService = userService;
    }

    @PostMapping("add")
    public ResponseResult<User> add(User user) {
        if (user.getId()==null || !userService.exists(user.getId())) {
            user.setCreateTime(LocalDateTime.now());
            user.setUpdateTime(LocalDateTime.now());
            userService.save(user);
        } else {
            user.setUpdateTime(LocalDateTime.now());
            userService.update(user);
        }
        return ResponseResult.success(userService.find(user.getId()));
    }


    /**
     * @return user list
     */
    @GetMapping("edit/{userId}")
    public ResponseResult<User> edit(@PathVariable("userId") Long userId) {
        return ResponseResult.success(userService.find(userId));
    }

    /**
     * @return user list
     */
    @GetMapping("list")
    public ResponseResult<Page<User>> list(@RequestParam int pageSize, @RequestParam int pageNumber) {
        return ResponseResult.success(userService.findPage(UserQueryBean.builder().build(), PageRequest.of(pageNumber, pageSize)));
    }
}

9:测试运行

在这里插入图片描述


http://www.kler.cn/a/518115.html

相关文章:

  • Android View 的事件分发机制解析
  • 20.Word:小谢-病毒知识的科普文章❗【38】
  • 《十七》浏览器基础
  • 第05章 12 可视化热量流线图一例
  • 【Linux系统】计算机世界的基石:冯诺依曼架构与操作系统设计
  • 简单看看会议系统(TODO)
  • DSP实验七 综合实验与考查
  • 数据库之PostgreSQL详解
  • 构建基于知识图谱的语义化问答系统
  • ray.rllib 入门实践-2:配置算法
  • Lua 初级教程
  • Android BitmapShader简洁实现马赛克,Kotlin(二)
  • Java设计模式 三十 状态模式 + 策略模式
  • ProfiNet转CANopen应用于汽车总装生产线输送设备ProfiNet与草棚CANopen质量检测系统
  • C++ —— vector 容器
  • 立创开发板入门ESP32C3第八课 修改AI大模型接口为deepseek3接口
  • Redis高阶3-缓存双写一致性
  • 【8】思科IOS AP升级操作
  • 【Flutter】旋转元素(Transform、RotatedBox )
  • 【EI会议推荐】人工智能、电子信息、智能制造、机器人、自动化、控制科学、机械制造等计算机领域多主题可选!
  • STM32 调试小问题记录
  • qsort和std::sort比较函数返回值的说明
  • 《CPython Internals》阅读笔记:p353-p355
  • 正点原子Linux 移植USB Wifi模块MT7601U驱动(上)
  • Android-UI自动化测试环境配置
  • 【C语言算法刷题】第2题 图论 dijkastra