Jpa 如何使用@EntityListeners 實(shí)現(xiàn)實(shí)體對象的自動賦值
1、簡介
1.1 @EntityListeners
官方解釋:可以使用生命周期注解指定實(shí)體中的方法,這些方法在指定的生命周期事件發(fā)生時(shí)執(zhí)行相應(yīng)的業(yè)務(wù)邏輯。
簡單來說,就是監(jiān)聽實(shí)體對象的增刪改查操作,并對實(shí)體對象進(jìn)行相應(yīng)的處理。
1.2 生命周期對應(yīng)注解
JPA一共提供了7種注解,分別是:
@PostLoad :實(shí)體對象查詢之后
@PrePersist : 實(shí)體對象保存之前
@PostPersist :實(shí)體對象保存之后
@PreUpdate :實(shí)體對象修改之前
@PostUpdate :實(shí)體對象修改之后
@PreRemove : 實(shí)體對象刪除之前
@PostRemove :實(shí)體對象刪除之后
通常情況下,數(shù)據(jù)表中都會記錄創(chuàng)建人、創(chuàng)建時(shí)間、修改人、修改時(shí)間等通用屬性。如果每個(gè)實(shí)體對象都要對這些通用屬性手動賦值,就會過于繁瑣。
現(xiàn)在,使用這些生命周期注解,就可以實(shí)現(xiàn)對通用屬性的自動賦值,或者記錄相應(yīng)操作日志。
2、環(huán)境準(zhǔn)備
數(shù)據(jù)庫:mysql
項(xiàng)目搭建:演示項(xiàng)目通過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
// 指定對應(yīng)監(jiān)聽類
@EntityListeners(CreateListener.class)
public abstract class IdMapped {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String createBy;
private Date createTime;
}
@Getter @Setter
@MappedSuperclass
// 指定對應(yīng)監(jiān)聽類
@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)聽類
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 {
// 日志對象不需要再記錄變更日志
if (object instanceof ModifyLogEntity) {
return;
}
ModifyLogEntity modifyLogEntity = new ModifyLogEntity();
modifyLogEntity.setAction(action);
modifyLogEntity.setEntityKey(String.valueOf(object.getId()));
modifyLogEntity.setEntityName(object.getClass().getSimpleName());
// 對象轉(zhuǎn)json字符串存儲
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、測試
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 測試
3.3.1 創(chuàng)建用戶
@SpringBootTest
public class SchoolApplicationTests {
@Autowired
private UserService userService;
@Test
public void testAdd() {
userService.add("test1", "123456");
}
}
測試結(jié)果

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


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

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
三分鐘帶你掌握J(rèn)ava開發(fā)圖片驗(yàn)證碼功能方法
這篇文章主要來為大家詳細(xì)介紹Java實(shí)現(xiàn)開發(fā)圖片驗(yàn)證碼的具體方法,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以參考一下2023-02-02
解決springmvc使用@PathVariable路徑匹配問題
這篇文章主要介紹了解決springmvc使用@PathVariable路徑匹配問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02
Spring Cloud 2020.0.0正式發(fā)布再見了Netflix
這篇文章主要介紹了Spring Cloud 2020.0.0正式發(fā)布再見了Netflix,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12
基于Transactional事務(wù)的使用以及注意說明
這篇文章主要介紹了Transactional事務(wù)的使用以及注意說明,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07

