Hibernate Interceptor(拦截器) 博客分类: Java
程序员文章站
2024-03-13 14:42:15
...
需求:
对所有操作数据库的事件,添加audit log, 此log持久化到一张单独的audit_log表,以供操作人员可以查阅跟踪。
方案:
Hibernate Interceptor 提供了一个拦截器,使用切面的方法,拦截所有对DB的操作,like:persist, merge, remove event。
实现:
首先是创建一个AuditlogInterceptor,来实现对数据库操作的拦截。 这个Interceptor要继承Hibernate的EmptyInterceptor, 然后我们同时重写onsave,ondelete,onFlushDirty, postFlush等方法来实现我们自己的需求:
其次把这个拦截器配置到我们的事务里去。
配置文件:比如数据源配置文件:datasource-context.xml:
添加:
由于这里是我自己去创建了一个PersistenceUnit,所以Hibernate会要求有一个persistence.xml文件,在META-INFO 文件夹下面,我们只要创建这个文件,并不需要指定,Hibernate会自动到该目录下去查找这个文件,文件名不能写错:
最后就是要去创建Entity来保持audit log, 比如AuditEntity.java
--EOF--
对所有操作数据库的事件,添加audit log, 此log持久化到一张单独的audit_log表,以供操作人员可以查阅跟踪。
方案:
Hibernate Interceptor 提供了一个拦截器,使用切面的方法,拦截所有对DB的操作,like:persist, merge, remove event。
实现:
首先是创建一个AuditlogInterceptor,来实现对数据库操作的拦截。 这个Interceptor要继承Hibernate的EmptyInterceptor, 然后我们同时重写onsave,ondelete,onFlushDirty, postFlush等方法来实现我们自己的需求:
public class AuditLogInterceptor extends EmptyInterceptor { /** * serialVersionUID */ private static final long serialVersionUID = -4829761117655964386L; private static final Logger logger = LoggerFactory.getLogger(AuditLogInterceptor.class); private static final String EMPTY_STRING = ""; private static final String DELETE = "postFlush - delete"; private static final String INSERT = "postFlush - insert"; private static final String UPDATE = "postFlush - update"; private static EntityManager entityManager = null; static { entityManager = Persistence.createEntityManagerFactory("auditLog").createEntityManager(); } //FIXME thread local private Set<IAuditable> inserts = new HashSet<IAuditable>(); private Set<IAuditable> updates = new HashSet<IAuditable>(); private Set<IAuditable> deletes = new HashSet<IAuditable>(); @Override public synchronized boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) throws CallbackException { logger.info("onSave"); if (entity instanceof IAuditable) { inserts.add((IAuditable)entity); } return false; } @Override public synchronized boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) throws CallbackException { logger.info("onFlushDirty"); if (entity instanceof IAuditable) { updates.add((IAuditable)entity); } return false; } @Override public synchronized void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { logger.info("onDelete"); if (entity instanceof IAuditable) { deletes.add((IAuditable)entity); } } /** * called before commit into database */ @SuppressWarnings("rawtypes") @Override public void preFlush(Iterator iterator) { logger.info("preFlush"); } /** * called after committed into database */ @SuppressWarnings("rawtypes") @Override public synchronized void postFlush(Iterator iterator) { logger.info("postFlush"); String username = SecurityContextHolder.getContext().getAuthentication().getName(); Collection collection = SecurityContextHolder.getContext() .getAuthentication() .getAuthorities(); String role = collection.toString(); if (inserts.isEmpty() && updates.isEmpty() && deletes.isEmpty()) { return; } try { if (!entityManager.getTransaction().isActive()) { entityManager.getTransaction().begin(); } for (IAuditable entity : inserts) { persistenceEntity(entity, entityManager, username, role, INSERT, null); } for (IAuditable entity : updates) { IAuditable preStateEntity = null; preStateEntity = entityManager.find(entity.getClass(), entity.getId()); List<String> valueList = getNewOldValues(entity, preStateEntity); String oldValues = valueList.get(0); String changeValues = valueList.get(1); if (!oldValues.equals(changeValues)) { persistenceEntity(entity, entityManager, username, role, UPDATE, valueList); } } for (IAuditable entity : deletes) { persistenceEntity(entity, entityManager, username, role, DELETE, null); } } catch (Exception e) { e.printStackTrace(); } finally { updates.clear(); inserts.clear(); deletes.clear(); if (entityManager.isOpen() && entityManager.getTransaction().isActive()) { logger.info("finally cause"); entityManager.getTransaction().commit(); } } } private void persistenceEntity(IAuditable entity, EntityManager em, String username, String role, String comments, List<String> changeValueslist) { logger.info(comments); AuditLogEntity logEntity = new AuditLogEntity(); logEntity.setComments(comments); logEntity.setOperator(StringUtils.isEmpty(username) ? "default" : username); logEntity.setRole(StringUtils.isEmpty(role) ? "default" : role); logEntity.setCreatedOn(new Date()); //sql date? logEntity.setUpdatedOn(new Date()); if (changeValueslist == null && DELETE.equals(comments)) { logEntity.setNewvalue(EMPTY_STRING); logEntity.setOldvalue(entity.getLogDeatil()); } else if (changeValueslist == null && INSERT.equals(comments)) { logEntity.setNewvalue(entity.getLogDeatil()); logEntity.setOldvalue(EMPTY_STRING); } else if (UPDATE.equals(comments)) { String newvalue = changeValueslist.get(1); String oldvalue = changeValueslist.get(0); logEntity.setNewvalue(newvalue); logEntity.setOldvalue(oldvalue); } logEntity.setEntity(entity.getClass().getName()); em.persist(logEntity); } }
其次把这个拦截器配置到我们的事务里去。
配置文件:比如数据源配置文件:datasource-context.xml:
添加:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceUnitName" value="auditLog" /> <property name="dataSource" ref="dataSource" /> <property name="packagesToScan" value="com.statestreet.fcm.cfd" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" /> </property> <!-- new added <property name="persistenceXmlLocation" value="classpath:persistence.xml" /> --> <property name="jpaProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.cache.use_second_level_cache">false</prop> <prop key="hibernate.cache.use_query_cache">false</prop> <prop key="hibernate.use_sql_comments">false</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop> <!-- by clu --> <prop key="hibernate.ejb.interceptor">com.statestreet.fcm.cfd.interceptor.AuditLogInterceptor</prop> </props> </property> </bean>
由于这里是我自己去创建了一个PersistenceUnit,所以Hibernate会要求有一个persistence.xml文件,在META-INFO 文件夹下面,我们只要创建这个文件,并不需要指定,Hibernate会自动到该目录下去查找这个文件,文件名不能写错:
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="auditLog" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <properties> <property name="hibernate.archive.autodetection" value="class"/> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.connection.driver_class" value="oracle.jdbc.OracleDriver"/> <property name="hibernate.connection.url" value="jdbc:oracle:thin:@"/> <property name="hibernate.connection.password" value="123"/> <property name="hibernate.connection.username" value="123"/> <property name="hibernate.dialect" value="org.hibernate.dialect.OracleDialect"/> <property name="hibernate.c3p0.min_size" value="5"/> <property name="hibernate.c3p0.max_size" value="20"/> <property name="hibernate.c3p0.timeout" value="300"/> <property name="hibernate.c3p0.max_statements" value="50"/> <property name="hibernate.c3p0.idle_test_period" value="3000"/> </properties> </persistence-unit> </persistence>
最后就是要去创建Entity来保持audit log, 比如AuditEntity.java
@Entity @Table(name = "AUDIT_LOG_DETAIL") public class AuditLogEntity implements Serializable { /** * serialVersionUID */ private static final long serialVersionUID = -1275702854046959229L; @Id @GeneratedValue private Long id; @Column(nullable = false ) private String operator; @Column(nullable = false ) private String role; @Column private String entity; @Column private String oldvalue; @Column private String newvalue; @Column private String comments; @Column(nullable = false) private Date createdOn; @Column(nullable = false) private Date updatedOn; }
--EOF--
推荐阅读
-
Hibernate Interceptor(拦截器) 博客分类: Java
-
dos 命令窗口執行java 命令,帶包路徑 博客分类: Java
-
android中传感器的应用 博客分类: java android javaandroidsensor应用
-
使用android studio 来开发hello world app的时候,遇到的一些坑。 博客分类: Java android
-
tomcat 远程调试 博客分类: Java
-
Caused by: java.lang.UnsupportedOperationException 解决方案 博客分类: Java
-
记一次Java Rest Service Hang住的经历 博客分类: Java jvmjava
-
android开发环境搭建linux篇 博客分类: java android androidlinuxubuntu开发环境
-
Java的按值传递 博客分类: Java java按值传递
-
Spring boot 发送邮件 博客分类: java