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();// 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:返回与 匹配的实体数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 {@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)));}
}

9:测试运行

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/893960.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从ai产品推荐到利用cursor快速掌握一个开源项目再到langchain手搓一个Text2Sql agent

目录 0. 经验分享&#xff1a;产品推荐 1. 经验分享&#xff1a;提示词优化 2. 经验分享&#xff1a;使用cursor 阅读一篇文章 3. 经验分享&#xff1a;使用cursor 阅读一个完全陌生的开源项目 4. 经验分享&#xff1a;手搓一个text2sql agent &#xff08;使用langchain l…

【Java-数据结构】Java 链表面试题下 “最后一公里”:解决复杂链表问题的致胜法宝

我的个人主页 我的专栏&#xff1a;Java-数据结构&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;点赞❤ 收藏❤ 引言&#xff1a; Java链表&#xff0c;看似简单的链式结构&#xff0c;却蕴含着诸多有趣的特性与奥秘&#xff0c;等待我们去挖掘。它就像一…

智慧园区系统的类型及其在企业管理效率提升中的关键作用解析

内容概要 在智慧园区的建设中&#xff0c;各类系统的采用是提升管理效率的关键所在。快鲸智慧园区(楼宇)管理系统&#xff0c;通过其全面数字化的管理手段&#xff0c;已经成为了企业管理的新标杆。这一系统能够有效整合租赁管理、资产管理、招商管理和物业管理等功能&#xf…

多级缓存(亿级并发解决方案)

多级缓存&#xff08;亿级流量&#xff08;并发&#xff09;的缓存方案&#xff09; 传统缓存的问题 传统缓存是请求到达tomcat后&#xff0c;先查询redis&#xff0c;如果未命中则查询数据库&#xff0c;问题如下&#xff1a; &#xff08;1&#xff09;请求要经过tomcat处…

第27篇 基于ARM A9处理器用C语言实现中断<三>

Q&#xff1a;基于ARM A9处理器怎样设计C语言工程&#xff0c;同时使用按键中断和定时器中断在红色LED上计数&#xff1f; A&#xff1a;基本原理&#xff1a;设置HPS Timer 0和按键中断源&#xff0c;主程序调用set_A9_IRQ_stack( )函数设置中断模式的ARM堆栈指针&#xff0c…

C++ 中用于控制输出格式的操纵符——setw 、setfill、setprecision、fixed

目录 四种操纵符简要介绍 setprecision基本用法 setfill的基本用法 fixed的基本用法 setw基本用法 以下是一些常见的用法和示例&#xff1a; 1. 设置字段宽度和填充字符 2. 设置字段宽度和对齐方式 3. 设置字段宽度和精度 4. 设置字段宽度和填充字符&#xff0c;结合…

【1.安装ubuntu22.04】

目录 参考文章链接电脑参数安装过程准备查看/更改引导方式查看/更改磁盘的分区格式关闭BitLocker加密压缩分区关闭独显直连制作Ubuntu安装盘下载镜像制作启动盘 进入BIOS模式进行设置Secure Boot引导项顺序try or install ubuntu 进入安装分区启动引导器个人信息和重启 参考文章…

代码随想录算法【Day34】

Day34 62.不同路径 思路 第一种&#xff1a;深搜 -> 超时 第二种&#xff1a;动态规划 第三种&#xff1a;数论 动态规划代码如下&#xff1a; class Solution { public:int uniquePaths(int m, int n) {vector<vector<int>> dp(m, vector<int>(n,…

计算机毕业设计PySpark+hive招聘推荐系统 职位用户画像推荐系统 招聘数据分析 招聘爬虫 数据仓库 Django Vue.js Hadoop

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

强化学习数学原理(三)——迭代算法

一、值迭代过程 上面是贝尔曼最优公式&#xff0c;之前我们说过&#xff0c;f(v)v&#xff0c;贝尔曼公式是满足contraction mapping theorem的&#xff0c;能够求解除它最优的策略和最优的state value&#xff0c;我们需要通过一个最优v*&#xff0c;这个v*来计算状态pi*&…

AI 浪潮席卷中国年,开启科技新春新纪元

在这博主提前祝大家蛇年快乐呀&#xff01;&#xff01;&#xff01; 随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;其影响力已经渗透到社会生活的方方面面。在中国传统节日 —— 春节期间&#xff0c;AI 技术也展现出了巨大的潜力&#xff0c;为中国年带…

vim的特殊模式-可视化模式

可视化模式&#xff1a;按 v进入可视化模式 选中 y复制 d剪切/删除 可视化块模式: ctrlv 选中 y复制 d剪切/删除 示例&#xff1a; &#xff08;vim可视化模式的进阶使用&#xff1a;vim可视化模式的进阶操作-CSDN博客&#xff09;

sunrays-framework配置重构

文章目录 1.common-log4j2-starter1.目录结构2.Log4j2Properties.java 新增两个属性3.Log4j2AutoConfiguration.java 条件注入LogAspect4.ApplicationEnvironmentPreparedListener.java 从Log4j2Properties.java中定义的配置读取信息 2.common-minio-starter1.MinioProperties.…

相互作用感知的蛋白-小分子对接模型 - Interformer 评测

Interformer 是一个应用于分子对接和亲和力预测的深度学习模型&#xff0c;基于 Graph-Transdormer 架构的模型&#xff0c;利用相互作用&#xff08;氢键、疏水&#xff09;感知的混合密度网络&#xff08;interaction-aware mixture den sity network&#xff0c; MDN&#x…

Ceisum无人机巡检直播视频投射

接上次的视频投影&#xff0c;Leader告诉我这个视频投影要用在两个地方&#xff0c;一个是我原先写的轨迹回放那里&#xff0c;另一个在无人机起飞后的地图回显&#xff0c;要实时播放无人机拍摄的视频&#xff0c;还要能转镜头&#xff0c;让我把这个也接一下。 我的天&#x…

【漫话机器学习系列】065.梯度(Gradient)

梯度&#xff08;Gradient&#xff09; 在数学和机器学习中&#xff0c;梯度是一个向量&#xff0c;用来表示函数在某一点的变化方向和变化率。它是多变量函数的一阶偏导数的组合。 梯度的定义 设有一个标量函数 &#xff0c;它对 ​ 是可微的&#xff0c;则该函数在某一点的…

基于SpringBoot多数据源解决方案

最近在学习SpringBoot的时候&#xff0c;需要同时用两个不同的数据库连接服务&#xff0c;在网上学习了之后&#xff0c;下文以连接一个MySQL数据库和一个SqlServer数据库为例。 配置数据源连接信息 在配置文件中&#xff0c;配置对应的数据库连接信息&#xff0c;相比于单数…

二叉树的最大深度(C语言详解版)

一、摘要 嗨喽呀大家&#xff0c;leetcode每日一题又和大家见面啦&#xff0c;今天要讲的是104.二叉树的最大深度&#xff0c;思路互相学习&#xff0c;有什么不足的地方欢迎指正&#xff01;好啦让我们开始吧&#xff01;&#xff01;&#xff01; 二、题目简介 给定一个二…

穿心莲内酯(andrographolide)生物合成CYP72-文献精读106

Two CYP72 enzymes function as Ent-labdane hydroxylases in the biosynthesis of andrographolide in Andrographis paniculata 两种CYP72酶在穿心莲&#xff08;Andrographis paniculata&#xff09;中作为Ent-labdane羟化酶&#xff0c;在穿心莲内酯&#xff08;andrograp…

[SaaS] 内容创意生产平台

1.即梦 2.讯飞绘镜 typemovie 3.Krea.ai 4.Pika 5.runway 6.pixVerse 7.