集成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();// deletevoid 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();// 分页,且排序规则是sortPageable 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 {@Autowiredprivate CustomerQbeRepository customerQbeRepository;@Testpublic void findTest() {// 可以动态的指定Customer customer = new Customer();customer.setCustName("cui haida");// 构建exampleExample<Customer> example = Example.of(customer);List<Customer> customerList = (List<Customer>) customerQbeRepository.findAll(example);System.out.println(customerList);}@Testpublic 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 {@Autowiredprivate CustomerSpecificationsRepository customerSpecificationsRepository;@Testpublic void findTest() {Customer customer = new Customer();customer.setCustId(1L);customer.setCustName("cui haida");// 需要实现函数式接口List<Customer> customerList = customerSpecificationsRepository.findAll(new Specification<Customer>() {@Overridepublic 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 {@Autowiredprivate CustomerQueryDslRepository customerQueryDslRepository;@Testpublic 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 {@Autowiredprivate CustomerRepository customerRepository;@Testpublic void saveTest() {// define dataAccount 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);}}@Testpublic 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 {@Autowiredprivate CustomerRepository repository;@Testpublic void createTest() {// 创建role信息List<Role> roleList = new ArrayList<>();roleList.add(new Role("admin"));roleList.add(new Role("super admin"));// 创建customerCustomer customer = new Customer();customer.setCustName("崔海达");// set rolecustomer.setRoleList(roleList);// 保存repository.save(customer);}@Test@Transactional(readOnly = true)public void findTest() {Optional<Customer> customer = repository.findById(1L);customer.ifPresent(System.out::println);}@Testpublic 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 版本有冲突,所以还需要先解决这个 bugmatching-strategy: ANT_PATH_MATCHER# 数据源配置datasource:url: jdbc:mysql://127.0.0.1:3306/mytest?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghaidriver-class-name: com.mysql.cj.jdbc.Driver # 8.0 +username: 用户名password: 密码# JPA 配置jpa:generate-ddl: false # 是否自动创建数据库表show-sql: true # 是否打印生成的 sqlproperties:hibernate:dialect: org.hibernate.dialect.MySQL8Dialect # 数据库方言 mysql8format_sql: true # 是否格式化 sqluse_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方法*/@Overridepublic 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相同,如果相同就认为是equalRole role = (Role) o;return getId() != null && Objects.equals(getId(), role.getId());}/*** 重写hashCode方法*/@Overridepublic 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> {// 获取 BaseDaopublic abstract IBaseDao<T, I> getBaseDao();/*** findById.** @param id id* @return T*/@Overridepublic T find(I id) {return getBaseDao().findById(id).orElse(null);}/*** @return List*/@Overridepublic List<T> findAll() {return getBaseDao().findAll();}/*** @param ids ids* @return List*/@Overridepublic List<T> findList(I[] ids) {List<I> idList = Arrays.asList(ids);return getBaseDao().findAllById(idList);}/*** find list.** @param spec spec* @return list*/@Overridepublic List<T> findList(Specification<T> spec) {return getBaseDao().findAll(spec);}/*** find list.** @param spec spec* @param sort sort* @return List*/@Overridepublic List<T> findList(Specification<T> spec, Sort sort) {return getBaseDao().findAll(spec, sort);}/*** find one.** @param spec spec* @return T*/@Overridepublic T findOne(Specification<T> spec) {return getBaseDao().findOne(spec).orElse(null);}/*** @param pageable pageable* @return Page*/@Overridepublic Page<T> findAll(Pageable pageable) {return getBaseDao().findAll(pageable);}/*** count.** @return long*/@Overridepublic long count() {return getBaseDao().count();}/*** count.** @param spec spec* @return long*/@Overridepublic long count(Specification<T> spec) {return getBaseDao().count(spec);}/*** exists.** @param id id* @return boolean*/@Overridepublic boolean exists(I id) {return getBaseDao().findById(id).isPresent();}/*** save.** @param entity entity*/@Overridepublic void save(T entity) {getBaseDao().save(entity);}/*** save.** @param entities entities*/@Overridepublic void save(List<T> entities) {getBaseDao().saveAll(entities);}/*** update.** @param entity entity* @return T*/@Overridepublic T update(T entity) {return getBaseDao().saveAndFlush(entity);}/*** delete.** @param id id*/@Overridepublic void delete(I id) {getBaseDao().deleteById(id);}/*** delete by ids.** @param ids ids*/@Overridepublic void deleteByIds(List<I> ids) {getBaseDao().deleteAllById(ids);}/*** delete all.*/@Overridepublic void deleteAll() {getBaseDao().deleteAllInBatch();}/*** delete.** @param entities entities*/@Overridepublic void delete(T[] entities) {List<T> tList = Arrays.asList(entities);getBaseDao().deleteAll(tList);}/*** delete.** @param entities entities*/@Overridepublic void delete(Iterable<T> entities) {getBaseDao().deleteAll(entities);}/*** delete.** @param entity entity*/@Overridepublic void delete(T entity) {getBaseDao().delete(entity);}/*** @param ids ids* @return List*/@Overridepublic List<T> findList(Iterable<I> ids) {return getBaseDao().findAllById(ids);}/*** @param spec spec* @param pageable pageable* @return Page*/@Overridepublic Page<T> findAll(Specification<T> spec, Pageable pageable) {return getBaseDao().findAll(spec, pageable);}/*** flush.*/@Overridepublic 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*/@Overridepublic IBaseDao<User, Long> getBaseDao() {return this.userDao;}/*** 分页查询用户信息。** @param queryBean 查询条件封装对象* @param pageRequest 分页请求参数* @return 分页结果*/@Overridepublic 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*/@Overridepublic IBaseDao<Role, Long> getBaseDao() {return this.roleDao;}/*** 根据查询条件分页查找角色信息。** @param roleQueryBean 查询条件,包含角色名称和描述等信息* @param pageRequest 分页请求,包含页码和每页大小等信息* @return 返回符合条件的角色信息分页结果*/@Overridepublic 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)));}
}