比@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 ...
随机推荐
- C# 设置Word文本框中的文字旋转方向
在Word中可插入文本框,默认情况下插入的文本框中的文字方向为横向排列,对于一些特殊文档的设计要求,需要改变文字方向,如本次测试中的文档排版为考生试卷类型,考生信息栏的内容为下图中的这种, 本文将以C ...
- RAC+DG修改sys密码
一.版本: 操作系统版本:SUSE 11 数据库版本:11.2.0.4 二.需求 因安全要求,需要修改SYS密码 三.步骤 1节点执行命令: alter user sys identified by ...
- top命令信息详解
top详解 [root@localhost ~]# top top - 09:36:38 up 17:59, 3 users, load average: 0.00, 0.03, 0.00 Tasks ...
- Mysql优化(出自官方文档) - 第六篇
Mysql优化(出自官方文档) - 第六篇 目录 Mysql优化(出自官方文档) - 第六篇 Optimizing Subqueries, Derived Tables, View Reference ...
- Spring @Transaction 注解是如何执行事务的?
前言 相信小伙伴一定用过 @Transaction 注解,那 @Transaction 背后的秘密又知道多少呢? Spring 是如何开启事务的?又是如何进行提交事务和关闭事务的呢? 画图猜测 在开始 ...
- YOLO V4 :win10+cpu环境的体验
1.前言 Yolo V3已经体验了,接下来是V4版本. 关于V4版本,学术界褒贬不一.从工业界实际应用角度看,V4做了不少的优化,精度提升了10%,速度提升了12%.详细参见: <如何评价新出的 ...
- vue调用子组件方法时,参数传不过去
有可能是因为子组件方法用了 async await 子组件去掉async就好了
- 学习Qt Charts-创建一个简单的折线图
一.Qt Charts Qt Charts是基于Qt Graphics View实现的一个图表的组件,可以用来在QT GUI程序中添加现在风格的.可交互的.以数据为中心的图表,可以用作QWidget或 ...
- 9、zabbix监控
9.1.监控: 1.初级(凡人): (1)识别监控的对象: (2)理解监控的对象: (3)细分监控对象的指标: (4)确定告警的基准线: 2.预中级(飞仙): (1)工具化和监控分离: (2)监控对象 ...
- 32、JavaScript介绍
32.1.JavaScript概述: 1.JavaScript的历史: 1992年Nombas开发出C-minus-minus(C--)的嵌入式脚本语言(最初绑定在CEnvi软件中),后将其改名Scr ...