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.hqwc.cn/news/675874.html

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

相关文章

63-HDMI转VGA电路设计

视频链接 HDMI转VGA电路设计01_哔哩哔哩_bilibili HDMI转VGA电路设计 HDMI电路设计&#xff08;参考第13课&#xff09; VGA电路设计&#xff08;参考第15课&#xff09; DP转VGA电路设计&#xff08;参考第75课&#xff09; 1、HDMI转VGA转换器 2、HDMI转VGA简介 1、解…

Dask简介

目录 一、概述 二、编程模型 2.1 High-Level Collection 2.2 Low level Interface 三、调度框架 3.1 任务图 3.2 调度 3.3 优化 3.4 动态任务图 一、概述 Dask是一个灵活的Python并行计算库。 Dask由两部分组成&#xff1a; 为计算优化的动态任务调度&#xff1a;和A…

静态分配IP,解决本地连接不上Linux虚拟机的问题

在Window环境下&#xff0c;使用远程终端工具连接不了VMware搭建的Linux虚拟机&#xff08;CentOS 7&#xff09;&#xff0c;并且在命令行ping不通该Linux虚拟机的IP地址。下面通过配置网关解决本地与Linux虚拟机连接问题&#xff1a; 1 查看虚拟机网关地址 在VMware虚拟机上…

面试集中营—JVM篇

一、JVM内存模型 线程独占&#xff1a;栈&#xff0c;本地方法栈&#xff0c;程序计数器; 线程共享&#xff1a;堆&#xff0c;方法区 虚拟机栈&#xff1a;线程私有的&#xff0c;线程执行方法是会创建一个栈阵&#xff0c;用来存储局部变量表&#xff0c;操作栈&#xff0c;…

DI-engine强化学习入门(十又二分之一)如何使用RNN——数据处理、隐藏状态、Burn-in

一、数据处理 用于训练 RNN 的 mini-batch 数据不同于通常的数据。 这些数据通常应按时间序列排列。 对于 DI-engine, 这个处理是在 collector 阶段完成的。 用户需要在配置文件中指定 learn_unroll_len 以确保序列数据的长度与算法匹配。 对于大多数情况&#xff0c; learn_un…

什么是web3D?应用场景有哪些?如何实现web3D展示?

Web3D是一种将3D技术与网络技术完美结合的全新领域&#xff0c;它可以实现将数字化的3D模型直接在网络浏览器上运行&#xff0c;从而实现在线交互式的浏览和操作。 Web3D通过将多媒体技术、3D技术、信息网络技术、计算机技术等多种技术融合在一起&#xff0c;实现了它在网络上…

2024深圳杯数学建模C题完整思路+配套解题代码+半成品参考论文持续更新

所有资料持续更新&#xff0c;最晚我们将于5.9号更新参考论文。 【无水印word】2024深圳杯A题成品论文23页mtlab(python)双版本代码https://www.jdmm.cc/file/27105652024深圳杯数学建模C题完整思路配套解题代码半成品参考论文持续更新https://www.jdmm.cc/file/2710545 深圳杯…

扭蛋机小程序在互联网浪潮中的崛起与发展

随着互联网的快速发展&#xff0c;各种线上娱乐方式层出不穷&#xff0c;其中扭蛋机小程序凭借其独特的魅力&#xff0c;在互联网浪潮中迅速崛起并发展壮大。扭蛋机小程序不仅打破了传统扭蛋机的地域限制和操作不便&#xff0c;还融入了丰富的互动元素和便捷性&#xff0c;满足…

C#高级编程笔记-委托、lambda表达式和事件

本章的主要内容如下&#xff1a; ● 委托 ● lambda表达式 ● 闭包 ● 事件 目录 1.1 引用方法 1.2 委托 1.2.1 在C#中声明委托 1.2.2 在C#中使用委托 1.2.3 Action和Func委托 1.2.4 多播委托 1.2.5 匿名方法 1.3 lambda表达式 1.3.…

LED显示屏的维护与使用指南

LED显示屏作为一种先进的显示技术&#xff0c;广泛应用于广告、信息显示、舞台背景等领域。然而&#xff0c;为了确保显示屏的长期稳定运行和良好的显示效果&#xff0c;对其进行正确的维护和使用是非常必要的。以下是一些专业的维护与使用建议&#xff1a; 维护建议&#xff1…

ws注入js逆向调用函数

这里需要选择一个文件夹 随便 紫色为修改保存 记得ctrls保存 注入代码如下 (function() {var ws new WebSocket("ws://127.0.0.1:8080")ws.onmessage function(evt) {console.log("收到消息&#xff1a;" evt.data);if (evt.data "exit") {…

Vue + Element-plus 快速入门

1. 构建项目 npm init vuelatest # 可选项一路回车&#xff0c;使用默认NO,按提示执行3条命令 cd 项目名 npm install npm run dev 2. 下载element-plus npm install element-plus --save 3.替换main.js import { createApp } from vue import ElementPlus from element-plu…