Jpa 如何使用@EntityListeners 實(shí)現(xiàn)實(shí)體對(duì)象的自動(dòng)賦值
1、簡(jiǎn)介
1.1 @EntityListeners
官方解釋:可以使用生命周期注解指定實(shí)體中的方法,這些方法在指定的生命周期事件發(fā)生時(shí)執(zhí)行相應(yīng)的業(yè)務(wù)邏輯。
簡(jiǎn)單來(lái)說(shuō),就是監(jiān)聽(tīng)實(shí)體對(duì)象的增刪改查操作,并對(duì)實(shí)體對(duì)象進(jìn)行相應(yīng)的處理。
1.2 生命周期對(duì)應(yīng)注解
JPA一共提供了7種注解,分別是:
@PostLoad :實(shí)體對(duì)象查詢之后
@PrePersist : 實(shí)體對(duì)象保存之前
@PostPersist :實(shí)體對(duì)象保存之后
@PreUpdate :實(shí)體對(duì)象修改之前
@PostUpdate :實(shí)體對(duì)象修改之后
@PreRemove : 實(shí)體對(duì)象刪除之前
@PostRemove :實(shí)體對(duì)象刪除之后
通常情況下,數(shù)據(jù)表中都會(huì)記錄創(chuàng)建人、創(chuàng)建時(shí)間、修改人、修改時(shí)間等通用屬性。如果每個(gè)實(shí)體對(duì)象都要對(duì)這些通用屬性手動(dòng)賦值,就會(huì)過(guò)于繁瑣。
現(xiàn)在,使用這些生命周期注解,就可以實(shí)現(xiàn)對(duì)通用屬性的自動(dòng)賦值,或者記錄相應(yīng)操作日志。
2、環(huán)境準(zhǔn)備
數(shù)據(jù)庫(kù):mysql
項(xiàng)目搭建:演示項(xiàng)目通過(guò)Spring Boot 2.2.6構(gòu)建,引入spring-boot-starter-data-jpa
2.1 數(shù)據(jù)表
-- 用戶表 CREATE TABLE `acc_user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `username` varchar(40) NOT NULL DEFAULT '' COMMENT '用戶名', `password` varchar(40) NOT NULL DEFAULT '' COMMENT '密碼', `create_by` varchar(80) DEFAULT NULL, `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_by` varchar(80) DEFAULT NULL, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 日志表 CREATE TABLE `modify_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `action` varchar(20) NOT NULL DEFAULT '' COMMENT '操作', `entity_name` varchar(40) NOT NULL DEFAULT '' COMMENT '實(shí)體類名', `entity_key` varchar(20) DEFAULT NULL COMMENT '主鍵值', `entity_value` varchar(400) DEFAULT NULL COMMENT '實(shí)體值', `create_by` varchar(80) DEFAULT NULL, `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2.2 實(shí)體類
@MappedSuperclass
@Getter @Setter
@MappedSuperclass
// 指定對(duì)應(yīng)監(jiān)聽(tīng)類
@EntityListeners(CreateListener.class)
public abstract class IdMapped {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String createBy;
private Date createTime;
}
@Getter @Setter
@MappedSuperclass
// 指定對(duì)應(yīng)監(jiān)聽(tīng)類
@EntityListeners(EditListener.class)
public abstract class EditMapped extends IdMapped{
private String updateBy;
private Date updateTime;
}
用戶類
@Entity
@Table(name = "acc_user")
@Getter @Setter
public class UserEntity extends EditMapped {
private String username;
private String password;
}
日志類
@Entity
@Table(name = "modify_log")
@Getter @Setter
public class ModifyLogEntity extends IdMapped{
private String action;
private String entityName;
private String entityKey;
private String entityValue;
}
2.3 監(jiān)聽(tīng)類
public class CreateListener extends BasicListener {
// 保存之前,為創(chuàng)建時(shí)間和創(chuàng)建人賦值
@PrePersist
public void prePersist(IdMapped idMapped) {
if (Objects.isNull(idMapped.getCreateTime())) {
idMapped.setCreateTime(new Date());
}
if (StringUtils.isBlank(idMapped.getCreateBy())) {
// 根據(jù)鑒權(quán)系統(tǒng)實(shí)現(xiàn)獲取當(dāng)前操作用戶,此處只做模擬
idMapped.setCreateBy("test_create");
}
}
// 保存之后,記錄變更日志
@PostPersist
public void postPersist(IdMapped idMapped) throws JsonProcessingException {
recordLog(ACTION_INSERT, idMapped);
}
}
public class EditListener extends BasicListener {
// 修改之前,為修改人和修改時(shí)間賦值
@PreUpdate
public void preUpdate(EditMapped editMapped) {
if (Objects.isNull(editMapped.getUpdateTime())) {
editMapped.setCreateTime(new Date());
}
if (StringUtils.isBlank(editMapped.getUpdateBy())) {
// 根據(jù)鑒權(quán)系統(tǒng)實(shí)現(xiàn)獲取當(dāng)前操作用戶,此處只做模擬
editMapped.setUpdateBy("test_update");
}
}
// 修改之后,記錄變更日志
@PostUpdate
public void postUpdate(EditMapped editMapped) throws JsonProcessingException {
recordLog(ACTION_UPDATE, editMapped);
}
// 刪除之前,記錄變更日志
@PreRemove
public void preRemove(EditMapped editMapped) throws JsonProcessingException {
recordLog(ACTION_DELETE, editMapped);
}
}
public class BasicListener implements ApplicationContextAware {
private ApplicationContext applicationContext;
protected static final String ACTION_INSERT = "insert";
protected static final String ACTION_UPDATE = "update";
protected static final String ACTION_DELETE = "delete";
// 記錄變更日志
protected void recordLog(String action, IdMapped object) throws JsonProcessingException {
// 日志對(duì)象不需要再記錄變更日志
if (object instanceof ModifyLogEntity) {
return;
}
ModifyLogEntity modifyLogEntity = new ModifyLogEntity();
modifyLogEntity.setAction(action);
modifyLogEntity.setEntityKey(String.valueOf(object.getId()));
modifyLogEntity.setEntityName(object.getClass().getSimpleName());
// 對(duì)象轉(zhuǎn)json字符串存儲(chǔ)
modifyLogEntity.setEntityValue(new ObjectMapper().writeValueAsString(object));
Optional.ofNullable(applicationContext.getBean(ModifyLogDao.class))
.ifPresent(modifyLogDao -> modifyLogDao.save(modifyLogEntity));
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
3、測(cè)試
3.1 Dao
@Repository
public interface UserDao extends JpaRepository<UserEntity, Long> {
}
@Repository
public interface ModifyLogDao extends JpaRepository<ModifyLogEntity, Long> {
}
3.2 Service
模擬用戶的創(chuàng)建、修改和刪除操作
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
@Transactional
public void add(String userName, String password) {
UserEntity userEntity = new UserEntity();
userEntity.setUsername(userName);
userEntity.setPassword(password);
userDao.save(userEntity);
}
@Override
@Transactional
public void update(Long id, String password) {
UserEntity userEntity = userDao.findById(id).orElseThrow(() -> new RuntimeException("用戶不存在"));
userEntity.setPassword(password);
userDao.save(userEntity);
}
@Override
@Transactional
public void delete(Long id) {
UserEntity userEntity = userDao.findById(id).orElseThrow(() -> new RuntimeException("用戶不存在"));
userDao.delete(userEntity);
}
}
3.3 測(cè)試
3.3.1 創(chuàng)建用戶
@SpringBootTest
public class SchoolApplicationTests {
@Autowired
private UserService userService;
@Test
public void testAdd() {
userService.add("test1", "123456");
}
}
測(cè)試結(jié)果

3.3.2 修改用戶
@Test
public void testUpdate() {
userService.update(1L, "654321");
}
測(cè)試結(jié)果


3.3.3 刪除用戶
@Test
public void testRemove() {
userService.delete(1L);
}
測(cè)試結(jié)果

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java使用IO流實(shí)現(xiàn)音頻的剪切和拼接
這篇文章主要為大家詳細(xì)介紹了Java使用IO流實(shí)現(xiàn)音頻的剪切和拼接,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06
Java如何實(shí)現(xiàn)實(shí)體類轉(zhuǎn)Map、Map轉(zhuǎn)實(shí)體類
這篇文章主要介紹了Java 實(shí)現(xiàn)實(shí)體類轉(zhuǎn)Map、Map轉(zhuǎn)實(shí)體類的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
MyBatis查詢時(shí)屬性名和字段名不一致問(wèn)題的解決方法
這篇文章主要給大家介紹了關(guān)于MyBatis查詢時(shí)屬性名和字段名不一致問(wèn)題的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
SpringBoot中Druid連接池與多數(shù)據(jù)源切換的方法
微服務(wù)架構(gòu)中多數(shù)據(jù)源切換是個(gè)常見(jiàn)的需求,Spring Boot 提供了強(qiáng)大的支持來(lái)簡(jiǎn)化這一過(guò)程.本文給大家介紹了SpringBoot中Druid連接池與多數(shù)據(jù)源切換的方法,需要的朋友可以參考下2024-11-11
詳解Spring中實(shí)現(xiàn)接口動(dòng)態(tài)的解決方法
最近在工作遇到的一個(gè),發(fā)現(xiàn)網(wǎng)上的資料較少,所以想著總結(jié)分享下,下面這篇文章主要給大家介紹了關(guān)于Spring中實(shí)現(xiàn)接口動(dòng)態(tài)的解決方法,文中通過(guò)完整的示例代碼給大家介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-07-07
Springboot打包為Docker鏡像并部署的實(shí)現(xiàn)
這篇文章主要介紹了Springboot打包為Docker鏡像并部署的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
實(shí)例解析Java單例模式編程中對(duì)抽象工廠模式的運(yùn)用
這篇文章主要介紹了實(shí)例解析Java單例模式編程中對(duì)抽象工廠模式的運(yùn)用,抽象工廠模式可以看作是工廠方法模式的升級(jí)版,本需要的朋友可以參考下2016-02-02
springboot vue組件開(kāi)發(fā)實(shí)現(xiàn)接口斷言功能
這篇文章主要為大家介紹了springboot+vue組件開(kāi)發(fā)實(shí)現(xiàn)接口斷言功能,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
IDEA編譯報(bào)錯(cuò):Error:(2048,1024) java: 找不到符號(hào)的解決方案
在使用 Lombok 的過(guò)程中,你是否曾遇到過(guò) IDEA 編譯報(bào)錯(cuò) Error:(2048,1024) java: 找不到符號(hào)?下面就讓我們來(lái)深入剖析這一問(wèn)題的根源,并給出相應(yīng)的解決方案,需要的朋友可以參考下2025-02-02

