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

news2024/12/27 11:01:50

前言

在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 {
    @Override
    public 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;

    @Transient
    private 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 {

    @Id
    private long memberId;
    @Id
    private 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 {

    @Resource
    private 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 {

    @Resource
    private 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;

    // 添加审计的注解
    @CreatedBy
    private Long createBy;
    @CreatedDate
    private Date createTime;
    @LastModifiedBy
    private Long modifyBy;
    @LastModifiedDate
    private 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等
     */
    @Override
    public 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 {

    @Resource
    private 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 {

    @Resource
    private 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;

    // 添加审计的注解
    @CreatedBy
    private Long createBy;
    @CreatedDate
    private Date createTime;
    @LastModifiedBy
    private Long modifyBy;
    @LastModifiedDate
    private 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.coloradmin.cn/o/1648472.html

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

相关文章

什么牌子的骨传导耳机质量好?五大宝藏热门机型测评对比!

我作为一名音乐发烧友&#xff0c;对各类耳机产品都有深入的了解&#xff0c;最近也经常被人问及骨传导耳机哪个牌子好。通过交流&#xff0c;我发现很多人在选择骨传导耳机的时候&#xff0c;都有出现踩坑的情况&#xff0c;这也难怪&#xff0c;随着骨传导耳机热度逐渐增加&a…

书生浦语训练营第2期-第7节笔记

一、为什么要研究大模型的评测&#xff1f; 首先&#xff0c;研究评测对于我们全面了解大型语言模型的优势和限制至关重要。尽管许多研究表明大型语言模型在多个通用任务上已经达到或超越了人类水平&#xff0c;但仍然存在质疑&#xff0c;即这些模型的能力是否只是对训练数据的…

Labels and Databases for Mac:强大的标签与数据库管理工具

Labels and Databases for Mac是一款集标签制作与数据库管理于一体的强大工具&#xff0c;专为Mac用户打造&#xff0c;旨在提供高效、便捷的标签制作与数据管理体验。 这款软件拥有丰富的内置标签格式&#xff0c;用户可轻松创建各种标签、信封和卡片&#xff0c;满足个性化需…

【软考信处考前几页纸】抱佛脚必背!不过软考算我输

一、考前几页纸是什么&#xff1f; 考前几页纸是各科目考点的高度精华总结。也是我们今年考前冲刺蕞后一份资料[酷] 二、考前几页纸好在哪里&#xff1f; 每科只有十几页的样子&#xff0c;方便携带[赞] 三、如何使用&#xff1f; 打印出来在吃饭、刷朋友圈的时候看看&…

干货!Kali Linux命令大全(建议收藏)

系统信息 arch 显示机器的处理器架构 name -m 显示机器的处理器架构 name -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件 -(SMBIOS/DMI) hdparm -i /dev/hda 罗列一个磁盘的架构特性 hdparm -tT /dev/sda 在磁盘上执行测试读取操作 cat /proc/cpuinfo …

Kettle连接Mysql数据库时报错——Driver class ‘org.gjt.mm.mysql.Driver‘ could not be found

一、问题描述 当我们使用ETL工具Kettle需要连接Mysql数据库进行数据清洗操作,在配置好Mysql的连接串内容后,点击【测试】按钮时报错【错误连接数据库 [MysqlTestConnection] : org.pentaho.di.core.exception.KettleDatabaseException: Error occurred while trying to conne…

Facebook之道:探索社交媒体领域的未来

随着科技的不断发展&#xff0c;社交媒体已经成为我们日常生活中不可或缺的一部分。而在这个领域中&#xff0c;Facebook一直是引领者和领头羊。然而&#xff0c;随着时间的推移&#xff0c;社交媒体领域正在发生着翻天覆地的变化&#xff0c;而Facebook又将何去何从&#xff1…

护眼灯排名前十的品牌有哪些?护眼灯品牌排行前十名推荐

近视在儿童中愈发普遍&#xff0c;许多家长开始认识到&#xff0c;除了学业成绩之外&#xff0c;孩子的视力健康同样重要。毕竟&#xff0c;学业的落后可以逐渐弥补&#xff0c;而一旦孩子近视&#xff0c;眼镜便可能成为长期伴随。因此&#xff0c;专业的护眼台灯对于每个家庭…

如何为数据库中新建用户B复制用户A的表和视图权限?

故事背景&#xff1a; 公司使用的是SQL Server数据库&#xff0c;经常会碰到一种情况&#xff0c;需要为新入职的员工赋予同组内其他同事的权限。 常用方法: 1) 为同一组申请创建统一的Security Group(安全组)&#xff0c;为创建的组分配相关表和视图的访问权限。不管员工入职…

一站式IT运维管理平台CAT

什么是 CAT &#xff1f; CAT&#xff08;Coffee And Tea&#xff09;是专为 IT 运维从业者打造的一个开源的、开放的一站式 IT 运维管理平台。包含资产管理、工单、工作流、仓储等功能模块&#xff0c;以及可靠的移动端应用&#xff08;Uniapp&#xff09;支持。 CAT 项目是 c…

Java+SpringBoot+JSP实现在线心理评测与咨询系统

前言介绍 随着互联网技术的高速发展&#xff0c;人们生活的各方面都受到互联网技术的影响。现在人们可以通过互联网技术就能实现不出家门就可以通过网络进行系统管理&#xff0c;交易等&#xff0c;而且过程简单、快捷。同样的&#xff0c;在人们的工作生活中&#xff0c;也就…

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…

欧盟委员会发布《数据法》指南

文章目录 前言一、B to B和B to C的数据共享二、企业间数据共享三、不公平合同条款四、企业对政府的数据共享五、数据处理服务之间的切换六、关于第三国政府非法访问数据七、关于可互操作性八、关于《数据法》的执行前言 4月21日,欧盟委员会在其官方网站发布了《数据法》(Th…

神经网络极简入门

神经网络是深度学习的基础&#xff0c;正是深度学习的兴起&#xff0c;让停滞不前的人工智能再一次的取得飞速的发展。 其实神经网络的理论由来已久&#xff0c;灵感来自仿生智能计算&#xff0c;只是以前限于硬件的计算能力&#xff0c;没有突出的表现&#xff0c;直至谷歌的A…

Zabbix+Grafana-常见报错及异常处理方式记录

文章目录 Zabbix安装篇Zabbix Web页面连接数据库失败 Zabbix使用篇中文显示不全 Zabbix报警篇新建的用户&#xff0c;配置报警后&#xff0c;无法收到报警 Grafana安装篇Windows系统安装时&#xff0c;添加zabbix报错&#xff1a;An error occurred within the plugin Zabbix安…

电商老板们,换个地方抢流量吧,抖音已经进入中后期了!

大家好&#xff0c;我是电商糖果 所有的电商平台&#xff0c;都会从巅峰时段&#xff0c;慢慢回归平稳&#xff0c;也就是大家说的中后期阶段。 中后期阶段的平台&#xff0c;各种流程化已经成熟。 新手商家再入局&#xff0c;很难从老商家手里抢到流量&#xff0c;这个时期…

双ISP住宅IP有何优势?

双ISP住宅IP在当前的互联网环境中具有显著的优势&#xff0c;这些优势主要体现在网络连接的稳定性、安全性、速度以及业务适用范围等方面。以下是对双ISP住宅IP优势的详细分析&#xff1a; 第一点网络连接的稳定性&#xff0c;双ISP住宅IP使用两个不同的互联网服务提供商&…

ICC2:optimize_routability

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 在postroute阶段,一些pin access引起的绕线问题,通常以end of line和short/spacing的形式扎堆出现,总量兴许不多,但是反复绕线仍难解决,返回preplace去设置keepout margin或placement label又得…

2024年Q1脱毛膏线上市场(京东天猫淘宝)销量销额排行榜

鲸参谋监测的2024年Q1季度线上电商平台&#xff08;天猫淘宝京东&#xff09;脱毛膏行业销售数据已出炉&#xff01; 根据鲸参谋数据显示&#xff0c;今年Q1季度在线上电商平台&#xff08;天猫淘宝京东&#xff09;&#xff0c;脱毛膏的销量累计接近220万件&#xff0c;环比增…

3D模型实时变形算法

最近&#xff0c;在尝试渲染一些奇怪的形状后&#xff0c;我陷入了计算机图形学的困境。事实证明&#xff0c;对于我试图解决的具体问题&#xff0c;没有现有的选项完全适合我想要做的事情。几周后&#xff0c;我终于带着一些答案再次浮出水面&#xff0c;写了很多行代码&#…