比@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 ...
随机推荐
- 【linux】驱动-15-定时器
目录 前言 15. 定时器 15.1 内核函数汇总 15.2 内核滴答 15.3 相关结构体 15.4 setup_timer() 设置定时器 15.5 add_timer() 向内核添加定时器 15 ...
- 【NLP学习其二】什么是隐马尔可夫模型HMM?
概念 隐马尔可夫模型描述的是两个时序序列联合分布p(x,y)的概率模型,其中包含了两个序列: x序列外界可见(外界指的是观测者),称为观测序列(obsevation seuence) y序列外界不可见 ...
- 整理一波Go工程化目录结构~
在Go语言领域遨游了几个月后,发现自己对Go语言相关的工程目录结构有些不了解,按照原来的习惯在Go工程中建立的目录结构显得比较奇怪,好的目录结构绝对是可以加强工程效率的,所以接下来会参考煎鱼.毛大等大 ...
- Mongo3基础操作
由于3.X的文档是在3.X当前最新版本前记录,所以这里列出一些常用的操作,比如建立库,删除库,等一些格式,然后在描述开启远程和创建用户的一些区别,以及讲解2.X和3.X配置文件区别. 1. Mongo ...
- .Net Core:限流
一.环境 1.vs2019 2..Net Core 3.1 3.引用 AspNetCoreRateLimit 4.0.1 二.基础使用 1.设置 在Startup文件中配置如下,把配置项都放在前面: ...
- 聊一聊Unity协程背后的实现原理
Unity开发不可避免的要用到协程(Coroutine),协程同步代码做异步任务的特性使程序员摆脱了曾经异步操作加回调的编码方式,使代码逻辑更加连贯易读.然而在惊讶于协程的好用与神奇的同时,因为不清楚 ...
- MIT6.828 Lab3 User Environments
Lab3 这个实验分成了两个大部分. 1. PartA User Environments and Exception Handling kernel使用Env这个数据结构来trace每一个user ...
- 04 jumpserver资产管理
4.资产管理: (1)管理用户: 管理用户是资产(被控服务器)上的 root,或拥有 NOPASSWD: ALL sudo 权限的用户, JumpServer 使用该用户来 `推送系统用户`.`获取资 ...
- 42、mysql数据库(函数)
1.mysql中提供的内置函数: (1)数学函数: 1)ROUND(x,y): 返回参数x的四舍五入的有y位小数的值.x不可转换时返回0,x为null时返回null. 2)RAND(): 返回0到1内 ...
- Vue 动态参数
v-on和v-bind可以动态绑定一个参数,用[]来绑定一个可以改变的值. <li v-on:[event]="print"></li> <scrip ...