比@EnableMongoAuditing功能强大的实现
问题出现
以前通过@EnableMongoAuditing、@CreateDate、@LastModifiedDate进行实体类创建时间、修改时间的自动管理。
但为了实现多数据源的管理以及切换,自己覆盖了mongoTemplate的bean,发现应用所有数据库操作都出现"Couldn't find PersistentEntity"错误,去掉@EnableMongoAuditing注解后才能正常使用,但@CreateDate、@LastModifiedDate失效,创建时间、修改时间这些都要自己去设置,太麻烦了也容易漏,为了方便使用以及偷懒,需要实现所有数据库存储更新操作能自动插入、更新公用字段,如创建时间、修改时间、创建者、修改者信息
解决的思路
为了实现这个功能,翻了下MongoTemplate的源码,下面是关键的几个方法
MongoTemplate.class
public <T> T save(T objectToSave, String collectionName) {
Assert.notNull(objectToSave, "Object to save must not be null!");
Assert.hasText(collectionName, "Collection name must not be null or empty!");
AdaptibleEntity<T> source = this.operations.forEntity(objectToSave, this.mongoConverter.getConversionService());
return source.isVersionedEntity() ? this.doSaveVersioned(source, collectionName) : this.doSave(collectionName, objectToSave, this.mongoConverter);
}
protected <T> T doSave(String collectionName, T objectToSave, MongoWriter<T> writer) {
objectToSave = ((BeforeConvertEvent)this.maybeEmitEvent(new BeforeConvertEvent(objectToSave, collectionName))).getSource();
objectToSave = this.maybeCallBeforeConvert(objectToSave, collectionName);
AdaptibleEntity<T> entity = this.operations.forEntity(objectToSave, this.mongoConverter.getConversionService());
entity.assertUpdateableIdIfNotSet();
MappedDocument mapped = entity.toMappedDocument(writer);
Document dbDoc = mapped.getDocument();
this.maybeEmitEvent(new BeforeSaveEvent(objectToSave, dbDoc, collectionName));
objectToSave = this.maybeCallBeforeSave(objectToSave, dbDoc, collectionName);
Object id = this.saveDocument(collectionName, dbDoc, objectToSave.getClass());
T saved = this.populateIdIfNecessary(objectToSave, id);
this.maybeEmitEvent(new AfterSaveEvent(saved, dbDoc, collectionName));
return this.maybeCallAfterSave(saved, dbDoc, collectionName);
}
protected <T> T maybeCallBeforeConvert(T object, String collection) {
return this.entityCallbacks != null ? this.entityCallbacks.callback(BeforeConvertCallback.class, object, new Object[]{collection}) : object;
}
protected <T> T maybeCallBeforeSave(T object, Document document, String collection) {
return this.entityCallbacks != null ? this.entityCallbacks.callback(BeforeSaveCallback.class, object, new Object[]{document, collection}) : object;
}
查看MongoTemplate的源码会发现,新增以及更新操作调用的save方法,在执行前会有BeforeConvertEvent、BeforeSaveEvent两个事件,我们只需要在这两个事件里面将我们需要保存的内容做下处理,就可以实现所有数据库新增、更新操作都自动加上创建时间这些信息,而且原来MongoAudit只有几个固定的字段能用,自定义BeforeConvertCallback事件后,完全可以按自己的需要多管理几个字段。
具体要实现一个接口,并且将自定义的callback处理set到要用的mongTemplate上面
@FunctionalInterface
public interface BeforeConvertCallback<T> extends EntityCallback<T> {
T onBeforeConvert(T var1, String var2);
}
具体实现
BeforeConvertEvent事件自定义处理
class BeforeConvert implements BeforeConvertCallback<Object> {
@NotNull
@Override
public Object onBeforeConvert(Object o, String s) {
log.info("before convert callback");
Map<String, Field> fieldMap = ReflectUtil.getFieldMap(o.getClass());
String userName = SecurityUtils.getUsername();
Date now = new Date();
if (fieldMap.containsKey("id")) {
Field id = fieldMap.get("id");
if (id == null || StringUtils.isBlank((String) ReflectUtil.getFieldValue(o, id))) {
//没有id时为新增
//创建时间
if (fieldMap.containsKey("createTime")) {
ReflectUtil.setFieldValue(o, "createTime", now);
}
//创建者
if (fieldMap.containsKey("createUser") && StringUtils.isNotBlank(userName)) {
ReflectUtil.setFieldValue(o, "createUser", userName);
}
}
}
//更新日期
if (fieldMap.containsKey("updateTime")) {
ReflectUtil.setFieldValue(o, "updateTime", now);
}
//更新者
if (fieldMap.containsKey("updateUser") && StringUtils.isNotBlank(userName)) {
ReflectUtil.setFieldValue(o, "updateUser", userName);
}
//这里还可以加上其他各种需要设置的值
return o;
}
}
@Bean(name = "mongoTemplate")
public DynamicMongoTemplate dynamicMongoTemplate() {
Iterator<SimpleMongoClientDatabaseFactory> iterator = MONGO_CLIENT_DB_FACTORY_MAP.values().iterator();
DynamicMongoTemplate mongoTemplate = new DynamicMongoTemplate(iterator.next());
//将自定义事件处理放进mongoTemplate中
mongoTemplate.setEntityCallbacks(EntityCallbacks.create(new BeforeConvert()));
return mongoTemplate;
}
@Bean(name = "mongoDbFactory")
public MongoDatabaseFactory mongoDbFactory() {
Iterator<SimpleMongoClientDatabaseFactory> iterator = MONGO_CLIENT_DB_FACTORY_MAP.values().iterator();
return iterator.next();
}
最后的一些话
一开始我自定义BeforeSaveCallback,在里面修改了object的内容,但发现并没有保存在数据库中。在官方的文档中说,在这事件里面修改object内容只是临时的不会做持久化,需要修改转换后document的内容才会保存进数据库,官方原话:Entity callback method invoked before a domain object is saved. Can return either the same or a modified instance of the domain object and can modify Document contents. This method is called after converting the entity to a Document so effectively the document is used as outcome of invoking this callback. Changes to the domain object are not taken into account for saving, only changes to the document. Only transient fields of the entity should be changed in this callback. To change persistent the entity before being converted, use the BeforeConvertCallback.
官方文档
为了避免实体转document后字段名发生改变不好找,就选用了BeforeConvertEvent,在转换前进行了处理,但其实在BeforeSaveEvent处理也是可以的
事件的发生顺序
Event:
BeforeDeleteEvent
AfterDeleteEvent
BeforeConvertEvent
BeforeSaveEvent
AfterSaveEvent
AfterLoadEvent
比@EnableMongoAuditing功能强大的实现的更多相关文章
- Postman - 功能强大的 API 接口请求调试和管理工具
Postman 是一款功能强大的的 Chrome 应用,可以便捷的调试接口.前端开发人员在开发或者调试 Web 程序的时候是需要一些方法来跟踪网页请求的,用户可以使用一些网络的监视工具比如著名的 Fi ...
- 功能强大的滚动播放插件JQ-Slide
查看效果:http://keleyi.com/keleyi/phtml/jqplug/4.htmJQ-Slide插件功能强大,滚动方式自由多样全部滚动方式 方式一 方式二 方式三 方式四 方式五 方式 ...
- 功能强大而又简单易学的编程语言Python
Python是一种面向对象.直译式计算机程序设计语言,也是一种功能强大的通用型语言.首先,Python非常简单,以Hello World为例: Java的Hello World程序一般这么写: pub ...
- 敏捷BI比传统BI功能强大是否属实?
关于大数据的资讯铺天盖地而来,让人眼花缭乱.虽然资讯很精彩,我们也看到了大数据背后的价值,很多企业选择了商业智能BI产品.商业智能在使用上可分为敏捷BI与传统BI,从名字来看敏捷BI要比传统BI显得利 ...
- [转载]什么是FCKeditor?功能强大的HTML编辑器!
天天在用FCKeditor写博客,但一直不清楚FCKeditor到底是什么,今天终于找到了一些相关的资料,大家一起来分享下. FCKeditor文本编辑程序(共享软件)为用户提供在线的文档编辑服务,其 ...
- 整合了一个功能强大完善的OA系统源码,php全开源 界面漂亮美观
整合了一个功能强大完善的OA系统源码,php全开源界面漂亮美观.需要的同学联系Q:930948049
- 功能强大的web打印控件lodop的使用
打印是很多web系统都需要的功能,最近找到一款功能强大,使用简单,价格便宜的web打印工具Lodop,免费也能用,不过有水印,也不贵商业开发建议购买. 废话不多说,拿来就用,从简单的打印开始. 1.下 ...
- 一款开源且功能强大的C#甘特图控件.NET Winforms Gantt Chart Control
甘特图在项目管理中非常重要,甘特图的思想比较简单,即以图示的方式通过活动列表和时间刻度形象地表示出任何特定项目的活动顺序与持续时间.它直观地表明任务计划在什么时候进行,及实际进展与计划要求的对比.管理 ...
- Vanilla Masker – 功能强大的输入过滤插件
Vanilla Masker 是一个纯 JavaScript 实现的输入内容过滤和自动转换插件.现在你可以使用一个简单而纯粹的 JavaScript 库来控制你的 input 元素,而不需要加载 jQ ...
随机推荐
- Redmine部署
Redmine部署文章: 第一篇:Redmine部署 第二篇:Redmine部署中遇到的问题 部门内部需要项目开发维护的网站,这种网站有付费的,也有开源项目.这类项目管理与协作的工具主要的MS Sha ...
- pip安装setuptools_rust报错
公司项目中有主备CDN存在,由于阿里云以及腾讯云的预热功能不支持自动(一般是云函数),所以就根据云厂商给的脚本稍作更改,手动传入数据来进行预热. 由于之前部署在centos7.7系统python2.7 ...
- vue3,后台管理列表页面各组件之间的状态关系
技术栈 vite2 vue 3.0.5 vue-router 4.0.6 vue-data-state 0.1.1 element-plus 1.0.2-beta.39 前情回顾 表单控件 查询控件 ...
- keycloak文章汇总
keycloak文章汇总 Keycloak是一个致力于解决应用和服务身份验证与访问管理的开源工具.可以通过简单的配置达到保护应用和服务的目的. 用户管理 你的应用不需要开发登录模块,验证用户和保存用户 ...
- Algorithm:MD5算法原理说明
MD5算法实现: 输入:不定长度信息(要加密的信息) 输出:固定长度128-bits.由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值. 基本方式为:求余.取余.调整长度.与链接 ...
- java设计模式(9):模板方法模式(TemplateMethod)
一,定义:模板方法模式定义了一个操作中的算法骨架,而将一些步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤. 二,类图: 三,通过小例子讲解: 这个模式一般用在 ...
- bugKu变量1
重点:两个$$,这暗示着第二个$变量的值,作为第一个$的名称. 而且代码审计之后,发现只含有字母. 又暗示在变量中,那么猜想是在全局变量中,而php中全局变量是$GLOBALS.
- 网站图片无缝兼容 WebP/AVIF
前言 WebP 格式发布已有十余年,但不少站点至今仍未使用,只为兼顾极少数低版本浏览器.至于去年发布的 AVIF 格式,使用的站点就更少了. 然而图片往往是流量大户,与其费尽心机优化脚本体积,可能还不 ...
- Servlet核心技术
一.基本概念 1.C/S C/S架构是客户端服务器架构,将需要处理的业务合理的分配到客户端和服务器,客户端负责与用户的交互任务,服务器负责数据管理. 优点: 客户端界面和功能可以很丰富 应用服务器负荷 ...
- Django基础008--model多对多
1.多对多表结构设计 class Student(models.Model): name = models.CharField(verbose_name='学生名字',max_length=100) ...