SpringBoot使用Hibernate拦截器实现时间自动注入的操作代码
作者:zhuzZi
最近项目有个改动:另一个系统根据更新时间戳来拉取本系统数据。这就要求基本上所有的数据表都要及时更新时间戳。以前的方式是在新增数据或者修改数据时手动调用setProperty(TimeStamp),因为没有用到这两个字段加上每个人的编码习惯不同,有时候没设置createTime或者updateTime,可能存在遗漏,导致数据库中有的时间戳默认值0。
前提:本系统时间戳在数据库类型为int,长度为int默认值,存储示例:1665295775,Java类型Integer,单位: /s
因为没怎么使用过Hibernate框架,就想Hibernate有没有MybatisPlus一样的自动填充功能呢?查了一下还真有日期相关的注解:@Temporal(TemporalType.XXX),但是对应Java的类型为:java.sql.Date,java.sql.Time,java.sql.Timestamp
,数据库的类型为时间相关的类型:date,time,datetime,timestamp
,在不改动历史数据表字段类型的情况下,只能寻求其它方式。
Hibernate拦截器:
- Interceptor接口:
允许用户代码检查和/或更改属性值。检查发生在属性值写入之前和从数据库中读取之后。 SessionFactory可能有一个Interceptor实例,或者可能为每个Session指定一个新实例。无论使用哪种方法,如果Session要可序列化,则拦截器必须是可序列化的。这意味着SessionFactory范围的拦截器应该实现readResolve() 。 Session不能从回调中调用(回调也不能导致集合或代理被延迟初始化)。与其直接实现此接口,不如继承EmptyInterceptor并仅覆盖感兴趣的回调方法。
/** 在刷新期间检测到对象脏时调用。拦截器可能会修改检测到的currentState ,这将被传播到数据库和持久对象。 请注意,并非所有刷新都以与数据库的实际同步结束,在这种情况下,新的currentState将传播到对象,但不一定(立即)传播到数据库。 强烈建议拦截器不要修改previousState 。 */ onFlushDirty(): /** 在保存对象之前调用。拦截器可能会修改状态,该状态将用于 SQL INSERT并传播到持久对象。 */ onSave():
那我们去EmptyInterceptor看一下。
- EmptyInterceptor类:
一个什么都不做的拦截器。可用作应用程序定义的自定义拦截器的基类 。
参看上面接口的方法描述,我们只需要继承EmptyInterceptor并重写onSave()和onFlushDirty()两个方法,来分别实现保存(SQL INSERT)
和更新(SQL UPDATE)
即可。Hibernate在检测到save和update操作时先执行自定义逻辑。
代码实现
看完了拦截器相关,来实现一下代码,如下:
为了控制每个DDO的行为,可以设计一个BaseEntityControl接口
/** * 数据的顶层接口 */ public interface BaseEntityControl{ /** 可以加入其它逻辑 / /** * 控制是否需要自动写入createTime和updateTime * @return */ default boolean writeTimeStamp() { return true; } }
import com.xx.xxx.BaseEntityControl; import java.io.Serializable; /** * Interceptor for entity audits. */ public class AuditInterceptor extends EmptyInterceptor { /** * Serial version UID */ private static final long serialVersionUID = 6892420119984901561L; /** * @see org.hibernate.Interceptor#onFlushDirty(Object, Serializable, Object[], Object[], String[], Type[]) */ @Override public boolean onFlushDirty(final Object entity, final Serializable id, final Object[] currentState, final Object[] previousState, final String[] propertyNames, final Type[] types) { if (entity instanceof BaseEntityControl) { if (!((BaseEntityControl) entity).writeTimeStamp()) { return false; } for (int i = 0; i < propertyNames.length; i++) { if ("updateTime".equals(propertyNames[i])) { currentState[i] = (int) (System.currentTimeMillis() / 1000); return true; } } } return false; } /** * @see org.hibernate.Interceptor#onSave(Object, Serializable, Object[], String[], Type[]) */ @Override public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { if (entity instanceof BaseEntityControl) { if (!((BaseEntityControl) entity).writeTimeStamp()) { return false; } boolean crtModify = false; boolean uptModify = false; int i = 0; while (true) { if (i >= propertyNames.length) { if (crtModify || uptModify) { return true; } break; } if ("createTime".equals(propertyNames[i])) { crtModify = true; state[i] = (int) (System.currentTimeMillis() / 1000L); } if ("updateTime".equals(propertyNames[i])) { uptModify = true; state[i] = (int) (System.currentTimeMillis() / 1000L); } ++i; } } return false; } }
将自定义Interceptor配置到session factory
在session factory配置类中将
public class xxxConfig { ... public FactoryBean<SessionFactory> getSessionFactory() { LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean(); sessionFactoryBean.setEntityInterceptor(new AuditInterceptor()); ... } ... }
对比测试
4.0.9:加入拦截器之前
4.3.1:加入拦截器之后
单个对象20个property情况下:
到此这篇关于SpringBoot使用Hibernate拦截器实现时间自动注入的操作代码的文章就介绍到这了,更多相关SpringBoot使用Hibernate拦截器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!