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--
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值