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的状态和转换关系
- 临时状态(new):刚刚创建出来的状态的,没有和entityManager发生关系,没有被持久化,不处于entityManager中的对象,通过persist()进入持久态
- 持久状态(managed):与entityManager发生关系,已经被持久化,可以将持久化的状态当做实实在在的数据库的记录
- 删除状态(removed):执行remove方法,事务提交之前
- 游离状态(detached):提交到数据后,事务commit之后的状态,由于事务已经提交,属性如何改变都不会同步到数据库,不在持久化的上下文中
persist() -> for insert
- 如果A是临时状态,触发persist之后,将会进行被管理状态
- 如果A是被管理状态,触发persist之后,状态不会发生改变,但是系统仍然会insert操作
- 如果A是删除状态,调用persist之后,将会转成被管理状态
- 如果A是游离状态,触发persist之后,会抛出异常
merge() -> for update
- 如果A是临时状态,调用merge之后,会根据A产生一个新的被管理状态实例A2
- 如果A是被管理状态,调用merge之后,状态不会发生改变,但是系统仍然会upsert操作
- 如果A是删除状态,调用merge之后,将会抛出异常
- 如果A是游离状态,调用merge之后,会将A的修改提交到数据库,并返回一个新的被管理状态实例A2
refresh()
- 如果A是临时状态,触发refresh之后,不会发生任何的操作,但是可能会抛出异常【取决于JPA实现】
- 如果A是被管理状态,触发refresh之后,属性将会和数据库同步
- 如果A是删除状态,调用refresh之后,会抛出异常
- 如果A是游离状态,触发refresh之后,会抛出异常
remove() -> for delete
- 如果A是临时状态,触发remove之后,不会发生任何的操作,但是系统仍然会delete操作
- 如果A是被管理状态,触发remove之后,状态会转成删除状态
- 如果A是删除状态,调用remove之后,不会进行任何的操作
- 如果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:返回与 匹配的实体数Predicate。
4:返回匹配的实体是否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)));
}
}