比@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 ...
随机推荐
- noip模拟10[入阵曲·将军令·星空](luogu)
对于这次考试来说,总体考得还是不错的 就是有一个小问题,特判一定要判对,要不然和不判一样,甚至错了还会挂掉30分 还有一个就是时间分配问题,总是在前几个题上浪费太多时间,导致最后一个题完全没有时间思考 ...
- 学习Qt Charts - 不使用UI的情况下使用QTCharts
新建一个Qt Widgets Application项目,不添加UI文件,如下图: 建立工程后,在.pro文件中添加: QT += charts 然后在.h文件中添加: #include " ...
- 永恒之蓝MS17-010漏洞复现
永恒之蓝MS17-010漏洞复现 1.漏洞描述: 起因: 永恒之蓝(Eternalblue)是指2017年4月14日晚,黑客团体Shadow Brokers(影子经纪人)公布一大批网络攻击工具,其中包 ...
- .NET Core如何全局获取用户信息?
前言 在增删改查中的增和改操作中,我们经常需要更新数据流的创建人和修改人,无论我们项目是基于DDD,抑或是简单仅有服务层,此时我们都需要获取用户信息,那么我们只能将用户标识从控制器层层传递到服务或仓储 ...
- 在windows的情况下面右键添加vim
1, 首先打开注册表,然后打开下面路径: HKEY_CLASSES_ROOT\*\Shell 2, 右键新建项(也就是右键的文字): 用vim编辑 3, 在"用vim编辑"下面再 ...
- keycloak文章汇总
keycloak文章汇总 Keycloak是一个致力于解决应用和服务身份验证与访问管理的开源工具.可以通过简单的配置达到保护应用和服务的目的. 用户管理 你的应用不需要开发登录模块,验证用户和保存用户 ...
- Raspberry Pi:树莓派开发板配置USB启动系统
准备材料 树莓派4B U盘 TF卡 树莓派基础镜像2020-08-20稳定版(这个系统是必须的并拷录在TF卡) Kali树莓派系统(这个是我想要学习的系统,大家可以准备自己的系统,拷录在U盘的) SD ...
- SpringCloud:feign对象传参和普通传参及遇到的坑
对象传参: #使用@RequestBody来指定传参对象 @RequestMapping(value = "/v2/matterCode/genCode", method = Re ...
- Docker:docker创建容器时报错:WARNING: IPv4 forwarding is disabled. Networking will not work.
创建容器时报错: WARNING: IPv4 forwarding is disabled. Networking will not work. # docker run -it -p 30001:2 ...
- 南京大学计算机基础 X64函数调用和链接器原理和可重定位的文件.o
一. 1.函数调用差别 X64的函数调用,和X86函数调用区别,在于参数的传递不一样了,X64的参数传递不在依靠栈来传,而是寄存器,不过还是具有局限性的 比如只能允许六个寄存器来传,分别是RDI,RS ...