Spring Data JPA自定义Id生成策略、复合主键配置、Auditing使用

前言

在Spring Data JPA系列的第一篇文章

SpringBoot集成JPA及基本使用-CSDN博客

中讲解了实体类的Id生成策略可以通过@GeneratedValue注解进行配置,该注解的strategy为GenerationType类型,GenerationType为枚举类,支持四种Id的生成策略,分别为TABLE、SEQUENCE、IDENTITY、AUTO,详细信息可以查看第一篇博文。

以上的四种Id生成策略并不能完全满足实际的项目需要,如在分布式系统中,为了实现Id的唯一性,可以采用雪花算法,此时可以使用自定义Id生成策略。

自定义Id生成策略

1.1 自定义Id生成策略

自定义Id生成策略,需要实现org.hibernate.id.IdentifierGenerator接口,重写generate()方法。此处以时间戳作为id为例,代码如下:

package com.jingai.jpa.util;import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;import java.io.Serializable;public class GeneratePK implements IdentifierGenerator {@Overridepublic Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {return System.currentTimeMillis();}
}

1.2 引用自定义Id生成策略

自定义Id生成策略使用,代码如下:

package com.jingai.jpa.dao.entity;import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import org.hibernate.annotations.GenericGenerator;import javax.persistence.*;
import java.util.Date;@Data
@Entity
@JsonIgnoreProperties(value = { "hibernateLazyInitializer"})
@Table(name = "tb_goods")
public class GoodsEntity2 {@Id// 指定id生成策略@GenericGenerator(name = "generatePk", strategy = "com.jingai.jpa.util.GeneratePK")// generator的值为@GenericGenerator的name@GeneratedValue(generator = "generatePk")private Long id;private String name;private String subtitle;private Long classificationId;private Date createTime;@Transientprivate String createTimeStr;}

其他的代码同使用系统提供的Id生成策略一致。

复合主键配置

有些表会存在多个id,如角色功能表。针对这种情况,Spring Data JPA中该如何配置呢?

在Spring Data JPA的实体类中并不支持简单的直接在多个属性中添加@Id注解。而是需要先创建一个复合主键类,然后在实体类中使用@IdClass注解将主键类附加在类中。

下面以会员统计表为例,建表语句:

CREATE TABLE `tb_member_statistics`  (`member_id` int(0) NOT NULL,`type` int(0) NOT NULL,`total_integral` int(0) NULL DEFAULT NULL,PRIMARY KEY (`member_id`, `type`) USING BTREE
)

该表以member_id、type为复合主键。

2.1 添加复合主键类

复合主键类为主键字段。代码如下:

package com.jingai.jpa.dao.entity;import lombok.AllArgsConstructor;
import lombok.Data;import java.io.Serializable;@Data
@AllArgsConstructor
public class MemberStatisticsPk implements Serializable {private long memberId;private int type;}

2.2 在实体类中使用@IdClass注解附加复合主键类

package com.jingai.jpa.dao.entity;import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Table;@Data
@Entity
@JsonIgnoreProperties(value = { "hibernateLazyInitializer"})
// 附加主键类
@IdClass(MemberStatisticsPk.class)
@Table(name = "tb_member_statistics")
public class MemberStatisticsEntity {@Idprivate long memberId;@Idprivate int type;private int totalIntegral;
}

2.3 Repository类

Spring Data JPA的Repository接口格式为Repository<T, ID>,其中T为实体类,ID为实体类中的Id。如果要使用Repository原生的ById接口,则必须传入正确的实体类Id。对于复合主键的实体类,此处传入的Id为复合主键类。代码如下:

package com.jingai.jpa.dao;import com.jingai.jpa.dao.entity.MemberStatisticsEntity;
import com.jingai.jpa.dao.entity.MemberStatisticsPk;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.jpa.repository.support.JpaRepositoryImplementation;import java.util.List;public interface MemberStatisticsRepository extends JpaRepositoryImplementation<MemberStatisticsEntity, MemberStatisticsPk> {@Query("from MemberStatisticsEntity where memberId = ?1")List<MemberStatisticsEntity> find(long memberId);}

2.4 Service类

package com.jingai.jpa.service;import com.jingai.jpa.dao.MemberStatisticsRepository;
import com.jingai.jpa.dao.entity.MemberStatisticsEntity;
import com.jingai.jpa.dao.entity.MemberStatisticsPk;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.List;@Service
public class MemberStatisticsService {@Resourceprivate MemberStatisticsRepository memberStatisticsRepository;public List<MemberStatisticsEntity> find(long memberId) {return memberStatisticsRepository.find(memberId);}/*** 使用原生的ById接口时,需要传入复合主键类作为id*/public MemberStatisticsEntity find(MemberStatisticsPk pk) {return memberStatisticsRepository.findById(pk).get();}}

2.5 Controller类

package com.jingai.jpa.controller;import com.jingai.jpa.dao.entity.MemberStatisticsPk;
import com.jingai.jpa.service.MemberStatisticsService;
import com.jingai.jpa.util.ResponseUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import java.util.Map;@RestController
@RequestMapping("memberstatistics")
public class MemberStatisticsController {@Resourceprivate MemberStatisticsService memberStatisticsService;@GetMapping("find")public Map<String, Object> find(long memberId) {return ResponseUtil.success(memberStatisticsService.find(memberId));}@GetMapping("get")public Map<String, Object> findById(long memberId, int type) {return ResponseUtil.success(memberStatisticsService.find(new MemberStatisticsPk(memberId, type)));}}

复合主键还可以通过@EmbeddedId和@Embeddable注解,采用嵌入式的方式实现,只是没有那么直观。感兴趣的可以自己百度了解一下。

Auditing使用

Auditing翻译过来是审计和审核,在实际的业务中,经常需要记录某条数据的操作人及操作时间,以及记录操作日志,Spring Data JPA通过注解的方式提供了审计功能的架构实现。

3.1 操作时间及操作人注解

Spring Data JPA提供了4个注解解决数据操作人及操作时间数据的维护。

1)@CreatedBy:创建用户

2)CreatedDate:创建时间

3)@LastModifiedBy:修改的用户

4)@LastModifiedDate:最后一次修改的时间

3.2 实体类Auditing的使用

package com.jingai.jpa.dao.entity;import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;import javax.persistence.*;
import java.util.Date;@Data
@Entity
@JsonIgnoreProperties(value = { "hibernateLazyInitializer"})
// 添加AuditingEntityListener实体监听
@EntityListeners(AuditingEntityListener.class)
@Table(name = "tb_user")
public class UserEntity {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String name;private String state;// 添加审计的注解@CreatedByprivate Long createBy;@CreatedDateprivate Date createTime;@LastModifiedByprivate Long modifyBy;@LastModifiedDateprivate Date modifyTime;}

添加AuditingEntityListener实体监听及审计的注解。

3.3 自定义AuditorAware

package com.jingai.jpa.config;import org.springframework.data.domain.AuditorAware;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;import java.util.Optional;@Component
public class AppAuditorAware implements AuditorAware<Long> {/*** 返回当前的审计员,即要添加在@CreateBy和@LastModifiedBy注解中属性的信息。* 此处通过Request中获取用户id。可根据实际项目进行修改,如通过jwt或者security等*/@Overridepublic Optional<Long> getCurrentAuditor() {RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();Object userId = requestAttributes.getAttribute("userId", RequestAttributes.SCOPE_SESSION);if(userId == null)return Optional.empty();return Optional.of((long)userId);}
}

3.4 在SpringBoot的启动类中添加@EnableJpaAuditing注解

package com.jingai.jpa;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;@SpringBootApplication
// 指定扫描的表映射实体Entity的目录,如果不指定,会扫描全部目录
//@EntityScan("com.jingai.jpa.dao.entity")
// 指定扫描的表repository目录,如果不指定,会扫描全部目录
//@EnableJpaRepositories(basePackages = {"com.jingai.jpa.dao"})
// 可选,开启JPA auditing能力,可以自动赋值一些字段,比如创建时间、最后一次修改时间等等
@EnableJpaAuditing
public class JpaApplication {public static void main(String[] args) {SpringApplication.run(JpaApplication.class, args);}}

3.5 Repository、Service类

Repository、Service类不需要任何改动。

package com.jingai.jpa.dao;import com.jingai.jpa.dao.entity.UserEntity;
import org.springframework.data.jpa.repository.support.JpaRepositoryImplementation;public interface UserRepository extends JpaRepositoryImplementation<UserEntity, Long> {}
package com.jingai.jpa.service;import com.jingai.jpa.dao.UserRepository;
import com.jingai.jpa.dao.entity.UserEntity;
import org.springframework.stereotype.Service;import javax.annotation.Resource;@Service
public class UserService {@Resourceprivate UserRepository userRepository;public UserEntity save(UserEntity entity) {return userRepository.save(entity);}public UserEntity find(long id) {return userRepository.findById(id).get();}}

3.6 Controller类

package com.jingai.jpa.controller;import com.jingai.jpa.dao.entity.UserEntity;
import com.jingai.jpa.service.UserService;
import com.jingai.jpa.util.ResponseUtil;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;import javax.annotation.Resource;
import java.util.Map;@RestController
@RequestMapping("user")
public class UserController {@Resourceprivate UserService userService;@PostMapping("save")public Map<String, Object> save(String name) {// 模拟审计员RequestContextHolder.getRequestAttributes().setAttribute("userId", 1000l, RequestAttributes.SCOPE_SESSION);UserEntity entity = new UserEntity();entity.setName(name);entity.setState("1");return ResponseUtil.success(userService.save(entity));}@PostMapping("update")public Map<String, Object> update(long id, String name) {// 模拟审计员RequestContextHolder.getRequestAttributes().setAttribute("userId", 1001l, RequestAttributes.SCOPE_SESSION);UserEntity entity = userService.find(id);entity.setName(name);return ResponseUtil.success(userService.save(entity));}}

在修改的时候需要特别注意,如果不先通过id获取原记录,那么修改后,createBy和createDate会被修改为null,因为修改时传入的实体类对象没有createBy和createDate的值。

访问上面的两个接口如下:

3.7 @MappedSuperClass的使用

在项目中,可能会有很多的实体类需要记录操作人及操作时间,此时可以定义一个父类,专门记录操作人及操作信息。

3.7.1 创建公共的抽象类

package com.jingai.jpa.dao.entity;import lombok.Data;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;import javax.persistence.*;
import java.util.Date;@Data
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class AbstractAuditable {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;// 添加审计的注解@CreatedByprivate Long createBy;@CreatedDateprivate Date createTime;@LastModifiedByprivate Long modifyBy;@LastModifiedDateprivate Date modifyTime;
}

3.7.2 在实体类中继承抽象类

package com.jingai.jpa.dao.entity;import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;import javax.persistence.Entity;
import javax.persistence.Table;@Data
@Entity
@JsonIgnoreProperties(value = {"hibernateLazyInitializer"})
@Table(name = "tb_user")
public class UserEntity extends AbstractAuditable {private String name;private String state;}

结尾

限于篇幅,Spring Data JPA自定义Id生成策略、复合主键配置、Auditing使用就分享到这里。

关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧!

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

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

相关文章

STM32F407VET6 学习笔记2:定时器、串口、自定义串口打印函数

今日继续学习使用嘉立创购买的 立创梁山派天空星&#xff0c;芯片是 STM32F407VET6 因为已经有学习基础了&#xff0c;所以学习进度十分快&#xff0c;这次也是直接一块学习配置定时器与串口了&#xff0c;文章也愈来愈对基础的解释越来越少了...... 文章提供测试代码讲解、完…

【如此简单!数据库入门系列】之思想地图 -- 系列目录

文章目录 1 前言2 基本概念3 基本原理4 数据库历史5 数据模型6 数据库规范化7 数据存储8 总结 1 前言 目录是思想地图&#xff0c;指引我们穿越文字的森林。 为了方便系统性阅读&#xff0c;将【如此简单&#xff01;数据库入门系列】按照模块划分了目录结构。 2 基本概念 【…

Jetpack Compose(一

Intellij IDEA构建Android开发环境 IntelliJ IDEA 2023.2.1 Android开发变化 IDEA配置使用Gradle 新建Compose工程&#xff0c;取名ComposeStudy 可以看到的是IDEA为项目初始化了部分代码 使用Compose开发不再需要使用xml文件来设计布局了 Compose中的Text也不同于Android V…

ogv转mp4怎么转?只需3个步骤~

OGV&#xff08;Ogg Video&#xff09;是一种开源的视频文件格式&#xff0c;起源于对数字媒体领域的开放标准的需求。作为Ogg多媒体容器的一部分&#xff0c;OGV的设计初衷是提供一种自由、高质量的视频编码方案&#xff0c;以满足多样化的应用需求。 支持打开MP4文件格式的软…

119. 再谈接口幂等性

文章目录 0. 前言1. insert前先select2. 加悲观锁3. 加乐观锁5. 加唯一索引【配合 &#xff08;1. insert前先select &#xff09;最常用 】6. 建防重表6. 根据状态机7. 加分布式锁8. 获取token 0. 前言 在 93. 通用防重幂等设计 一文中&#xff0c;已经介绍过幂等的使用。该文…

【一看就懂】UART、IIC、SPI、CAN四种通讯协议对比介绍

UART、IIC、SPI、CAN四种通信协议对比 通信方式传输线通讯方式标准传输速度使用场景UARTTX(发送数据线)、RX(接收数据线)串行、异步、全双工115.2 kbit/s(常用)计算机和外部设备通信&#xff08;打印机&#xff09;IICSCL(时钟线)、SDA(数据线)串行、同步、半双工100 kbit/s(标…

一种由RSOA和PIC集成的宽可调激光器

----翻译自Nouman Zia, Samu-Pekka Ojanen, Jukka Viheriala, Eero Koivusalo, Joonas Hilska, Heidi Tuorila, and Mircea Guina在optics letter上发的文章vol.48, Issue 5, pp. 1319-1322(2023) 摘要&#xff1a;通过光子集成方式实现的2-3μm波长的可调激光器&#xff0c;在…

Adobe Acrobat Pro DC 2022一款高效强大的PDF阅读编辑专业软件(240506)

01 软件介绍 Adobe Acrobat Pro DC 2022&#xff0c;作为一款专业的PDF处理工具&#xff0c;它集成了强大的制作功能&#xff0c;能够与Adobe Photoshop的高级图像编辑能力无缝衔接&#xff0c;进而将各类纸质文档高效转换成可编辑的电子格式&#xff0c;以便于文件的传输和签…

跨越语言界限,多语言盲盒小程序带你领略全球风情

在全球化的今天&#xff0c;我们生活在一个多元文化的世界中&#xff0c;不同的语言、风俗、习惯共同构成了这个五彩斑斓的地球村。为了让每个人都能轻松体验到世界各地的独特风情&#xff0c;一款创新的多语言盲盒小程序应运而生&#xff0c;它跨越了语言的界限&#xff0c;让…

老阳分享:跨境选品师普通人做能否借此赚钱?

在跨境电商日益繁荣的今天&#xff0c;选品师这一职业逐渐进入大众视野。老阳&#xff0c;作为业内知名的跨境选品师&#xff0c;经常分享他的选品经验和心得。那么&#xff0c;对于普通人来说&#xff0c;成为跨境选品师是否真的能赚钱呢? 首先&#xff0c;我们需要明确什么是…

mySQL (基础面试)实物四属性 ACID属性,以及开启事务

mySQL具备四个基本属性 原子性atomicity 事务是一个完整的操作&#xff0c;事务的各个步骤是不可分的&#xff08;原子的&#xff09;&#xff0c;要么执行要么不执行 一致性consistency 当事务完成时&#xff0c;数据处于一致状态 隔离性isolation 并发事物之间彼此隔离…

基于springboot+vue+Mysql的租房网站

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

springboot3项目练习详细步骤(第二部分:文章分类模块)

新增文章分类 接口文档 业务实现 参数校验 文章分类列表 接口文档 业务实现 获取文章分类详情 接口文档 业务实现 更新文章分类 接口文档 业务实现 分组校验 问题 概念 实现步骤 总结 删除文章分类 接口文档 业务实现 该模块大部分请求的路径相同&…

2024最新行业领域名词解释大全

2024最新行业领域名词解释大全 &#x1f680; 大家好&#xff01;我是你们的老朋友猫头虎&#x1f42f;。今天要为大家带来2024年最新的行业领域名词解释大全&#xff01;在这个信息爆炸的时代&#xff0c;准确了解不同领域的行业动态、工作机会和职业前景至关重要。下面我会分…

阿里巴巴中国站关键字搜索API返回值全攻略:精准定位所需商品

当使用阿里巴巴中国站的关键字搜索API时&#xff0c;理解其返回值的结构和内容对于精准定位所需商品至关重要。以下是一份全面的攻略&#xff0c;帮助你更好地利用这个API&#xff1a; 在商品列表中&#xff0c;每个商品对象都包含丰富的信息&#xff0c;以帮助你精准定位所需商…

伙伴匹配(后端)-- 前后端日期格式化

后端时间格式化 后端时间格式化 在expireTime属性加上一个格式化注解&#xff0c;并给定格式 前端过期时间格式化 下载一个moment格式化工具&#xff08;我安装失败了日期格式化也成功了&#xff09; npm install moment这一页和添加队伍新增这一行&#xff08;还要导入mom…

Python数据爬取超简单入门

## 什么是网络爬虫&#xff1f; 网络爬虫是一种自动浏览器程序&#xff0c;能够自动地从互联网获取数据。爬虫的主要任务是访问网页&#xff0c;分析网页内容&#xff0c;然后提取所需的信息。爬虫广泛应用于数据收集、数据分析、网页内容监控等领域。 ## 爬虫的基本步骤 1.…

2025第23届太原煤炭(能源)工业技术与装备展览会

第二十三届太原煤炭&#xff08;能源&#xff09;工业技术与装备展览会 邀 请 函 指导单位&#xff1a; 中国煤炭工业协会 主办单位&#xff1a;山西省煤炭工业协会 承办单位&#xff1a;太原奇新展览有限公司 展览时间&#xff1a;2025年4月22-24日 展览地点&#xff1a…

Obsidian dataview 使用入门

Dataview有四种展示格式&#xff1a;list、table、task、calendar。 本文只介绍前面两种。 语法总结 通过#标签 dataview LIST FROM #标签 通过"文件夹" dataview LIST FROM "文件夹名" 通过[ [ 文件链接 ] ] 选择链接到一个文件&#xff0c;或者…

深入了解C/C++的内存区域划分

&#x1f525;个人主页&#xff1a;北辰水墨 &#x1f525;专栏&#xff1a;C学习仓 本节我们来讲解C/C的内存区域划分&#xff0c;文末会附加一道题目来检验成果&#xff08;有参考答案&#xff09; 一、大体有哪些区域&#xff1f;分别存放什么变量开辟的空间&#xff1f; …