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://xiahunao.cn/news/3017749.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

java-springboot项目添加swagger2/Knife4j,附注解

文章目录 添加依赖config工作包中新增SwaggerConfig报错注解 环境&#xff1a; jdk1.8 java8 springboot2.6.13 swagger2.9.2 添加依赖 pom.xml <!-- 添加swagger2--><dependency><groupId>io.springfox</groupId><artifactId>springfo…

ChatGPT-Next-Web漏洞利用分析(CVE-2023-49785)

1. 漏洞介绍 ​ 日常网上冲浪&#xff0c;突然粗看以为是有关Chat-GPT的CVE披露出来了&#xff0c;但是仔细一看原来是ChatGPT-Next-Web的漏洞。漏洞描述大致如下&#xff1a;&#xff08;如果有自己搭建了还没更新的速速修复升级防止被人利用&#xff0c;2.11.3已经出来了&am…

代码训练LeetCode(17)存在重复元素

代码训练(17)LeetCode之存在重复元素 Author: Once Day Date: 2024年5月7日 漫漫长路&#xff0c;才刚刚开始… 全系列文章可参考专栏: 十年代码训练_Once-Day的博客-CSDN博客 参考文章: 219. 存在重复元素 II - 力扣&#xff08;LeetCode&#xff09;力扣 (LeetCode) 全球…

【ElasticSearch】IK分词器中停用词问题

问题描述 在ES中进行部分关键词搜索时&#xff0c;搜索无结果&#xff0c;如搜索 【IT】 环境描述 中文分词插件 这里使用的是 analysis-ik 分词调试 POST test_index/_analyze {"text":"IT Manager","analyzer": "ik_max_word"…

Spring Security基础教程:从入门到实战

作者介绍&#xff1a;✌️大厂全栈码农|毕设实战开发&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 推荐订阅精彩专栏 &#x1f447;&#x1f3fb; 避免错过下次更新 Springboot项目精选实战案例 更多项目&#xff1a;CSDN主页YAML墨韵 学如逆水行舟&#xff0c…

S型曲线的几种设计(图像对比度调节)

一般来讲&#xff0c;图像调色模块都会提供“曲线”工具&#xff0c;这是一个极其灵活的功能&#xff0c;绝大部分的调色都可以通过该工具实现&#xff0c;但是曲线功能的交互相对而言比较复杂。出于简便性和效率方面的考量&#xff0c;调色模块往往还会提供一些具有很强的功能…

初识C语言——第十四天

指针 总结&#xff1a;指针变量是用来干啥的呢&#xff0c;一是用来存放别人的地址的&#xff0c;指针是有类型的&#xff0c;这个类型是如何写的&#xff1b;二是当我有一天通过*(解引用符&#xff09;找到我所要找的对象&#xff0c;来操作所指向的对象。 #define _CRT_SECUR…

Three.js的几何形状

在创建物体的时候&#xff0c;需要传入两个参数&#xff0c;一个是几何形状【Geometry】&#xff0c;一个是材质【Material】 几何形状主要是存储一个物体的顶点信息&#xff0c;在Three中可以通过指定一些特征来创建几何形状&#xff0c;比如使用半径来创建一个球体。 立方体…

VS2022快捷键修改

VS2022快捷键修改 VS2022快捷键修改 VS2022快捷键修改

芸众商城电商专业版400+插件源码+搭建教程

介绍&#xff1a; 芸众商城社交电商系统SAAS平台前端基于vue开发&#xff0c;后端基于研发积分商城系统源码 php&#xff0c;本文安装芸众商城全插件&#xff08;400多个&#xff09;商业版平台源码&#xff0c;可同时支持多端口部署运行&#xff1b;使用宝塔面板一键部署的形…

2022 年全国职业院校技能大赛高职组云计算赛项试卷(私有云)

#需要资源&#xff08;软件包及镜像&#xff09;或有问题的&#xff0c;可私聊博主&#xff01;&#xff01;&#xff01; #需要资源&#xff08;软件包及镜像&#xff09;或有问题的&#xff0c;可私聊博主&#xff01;&#xff01;&#xff01; #需要资源&#xff08;软件包…

docker部署常用工具

1.创建mysql docker run -p 3306:3306 --name mysql -v /home/mysql/conf:/etc/mysql/mysql.conf.d -v /home/mysql/log:/var/log/ -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD2022qwer -d mysql:5.7 而后再执行升级&#xff1a; docker update --restar…

React中的高阶组件的封装,高阶函数,HOC的含义及用法:

含义及作用: 高阶函数代码案例: 调用高阶组价:

CP AUTOSAR之CANXL Driver详细说明(正在更新中)

本文遵循autosar标准&#xff1a;R22-11 1 简介及功能概述 本规范描述了AUTOSAR 基础软件模块CAN XL 驱动程序的功能、API和配置。   本文档的基础是[1,CiA610-1]和[2,CiA611-1]。假设读者熟悉这些规范。本文档不会再次描述CAN XL 功能。   CAN XL 驱动程序是最低层的一部…

算法提高之玉米田

算法提高之玉米田 核心思想&#xff1a;状态压缩dp 将图存入g数组 存的时候01交换一下方便后面判断即g数组中0为可以放的地方 state中1为放的地方 这样只要state为1 g为0就可以判断不合法 #include <iostream>#include <cstring>#include <algorithm>#includ…

Read timed out. (python 安装第三方库超时)

不少人在安装python第三方库的时候经常发生下面情况 解决方法就是往上找 我这里就是 jupyterlab-4.1.8-py3-none-any.whl安装时间过长&#xff0c;失败 那就去国内镜像网站下载下来离线安装 https://pypi.tuna.tsinghua.edu.cn/simple/xxx&#xff08;xxx就是你的包名&#…

Linux内核之获取文件系统超级块:sget用法实例(六十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

【Linux 命令操作】如何在 Linux 中使用多行注释呢?

文章目录 1. 给代码进行多行注释2. 给代码取消多行注释 1. 给代码进行多行注释 &#x1f427;① 首先用 vim 打开代码&#xff0c;按 Esc进入命令模式(Normal mode)&#xff1b; &#x1f427;② 然后按住 ctrl v 进入列模式&#xff1b; &#x1f427;③ 再通过按 h(左)、j(…

PHP 匿名函数和闭包在数据结构中的应用

匿名函数和闭包在数据结构处理中的应用php 中的匿名函数和闭包可用于处理数组、链表和队列等数据结构。针对数组&#xff0c;匿名函数可用于过滤元素&#xff1b;针对链表&#xff0c;闭包可用于创建节点&#xff1b;针对队列&#xff0c;匿名函数和闭包可实现 fifo 队列操作。…

数仓分层——ODS、DW、ADS

一、什么是数仓分层 数据仓库分层是一种组织和管理数据仓库的结构化方法&#xff0c;它将数据仓库划分为不同的层次或级别&#xff0c;每个层次具有特定的功能和目的。这种分层方法有助于管理数据仓库中的数据流程、数据处理和数据访问&#xff0c;并提供一种清晰的结构来支持…