Hibernate Interceptor(拦截器)

需求:
对所有操作数据库的事件,添加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--

猜你喜欢

转载自xfxlch.iteye.com/blog/2206923