欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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等方法来实现我们自己的需求:
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--