hibernate自定义
介绍
在上一篇文章中,我描述了Hibernate自动脏检查机制。 尽管您应该始终喜欢它,但是有时您可能想添加自己的自定义污垢检测策略。
自定义脏检查策略
Hibernate提供以下定制机制:
手动检查脏物
作为练习,我将构建一个手动的脏检查机制,以说明自定义更改检测策略的难易程度:
自脏检查实体
首先,我将定义一个DirtyAware接口,所有手动脏检查实体都必须实现:
public interface DirtyAware {
Set<String> getDirtyProperties();
void clearDirtyProperties();
}
接下来,我将在基类中封装当前的脏检查逻辑:
public abstract class SelfDirtyCheckingEntity implements DirtyAware {
private final Map<String, String> setterToPropertyMap = new HashMap<String, String>();
@Transient
private Set<String> dirtyProperties = new LinkedHashSet<String>();
public SelfDirtyCheckingEntity() {
try {
BeanInfo beanInfo = Introspector.getBeanInfo(getClass());
PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor descriptor : descriptors) {
Method setter = descriptor.getWriteMethod();
if (setter != null) {
setterToPropertyMap.put(setter.getName(), descriptor.getName());
}
}
} catch (IntrospectionException e) {
throw new IllegalStateException(e);
}
}
@Override
public Set<String> getDirtyProperties() {
return dirtyProperties;
}
@Override
public void clearDirtyProperties() {
dirtyProperties.clear();
}
protected void markDirtyProperty() {
String methodName = Thread.currentThread().getStackTrace()[2].getMethodName();
dirtyProperties.add(setterToPropertyMap.get(methodName));
}
}
所有手动的脏检查实体都必须扩展此基类,并通过调用markDirtyProperty方法显式标记脏属性。
实际的自我脏检查实体如下所示:
@Entity
@Table(name = "ORDER_LINE")
public class OrderLine extends SelfDirtyCheckingEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Long number;
private String orderedBy;
private Date orderedOn;
public Long getId() {
return id;
}
public Long getNumber() {
return number;
}
public void setNumber(Long number) {
this.number = number;
markDirtyProperty();
}
public String getOrderedBy() {
return orderedBy;
}
public void setOrderedBy(String orderedBy) {
this.orderedBy = orderedBy;
markDirtyProperty();
}
public Date getOrderedOn() {
return orderedOn;
}
public void setOrderedOn(Date orderedOn) {
this.orderedOn = orderedOn;
markDirtyProperty();
}
}
每当调用setter时,关联的属性就会变脏。 为简单起见,当我们将属性还原为其原始值时,此简单练习不涵盖用例。
脏检查测试
为了测试自脏检查机制,我将运行以下测试用例:
@Test
public void testDirtyChecking() {
doInTransaction(new TransactionCallable<Void>() {
@Override
public Void execute(Session session) {
OrderLine orderLine = new OrderLine();
session.persist(orderLine);
session.flush();
orderLine.setNumber(123L);
orderLine.setOrderedBy("Vlad");
orderLine.setOrderedOn(new Date());
session.flush();
orderLine.setOrderedBy("Alex");
return null;
}
});
}
Hibernate拦截器解决方案
Hibernate Interceptor findDirty回调使我们可以控制脏属性发现过程。 该方法可能返回:
- null ,将脏检查委托给Hibernate默认策略
- 一个int []数组,包含修改后的属性索引
我们的Hibernate脏检查拦截器如下所示:
public class DirtyCheckingInterceptor extends EmptyInterceptor {
@Override
public int[] findDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
if(entity instanceof DirtyAware) {
DirtyAware dirtyAware = (DirtyAware) entity;
Set<String> dirtyProperties = dirtyAware.getDirtyProperties();
int[] dirtyPropertiesIndices = new int[dirtyProperties.size()];
List<String> propertyNamesList = Arrays.asList(propertyNames);
int i = 0;
for(String dirtyProperty : dirtyProperties) {
LOGGER.info("The {} property is dirty", dirtyProperty);
dirtyPropertiesIndices[i++] = propertyNamesList.indexOf(dirtyProperty);
}
dirtyAware.clearDirtyProperties();
return dirtyPropertiesIndices;
}
return super.findDirty(entity, id, currentState, previousState, propertyNames, types);
}
}
将此拦截器传递到当前的SessionFactory配置时,我们将获得以下输出:
INFO [main]: c.v.h.m.l.f.InterceptorDirtyCheckingTest - The number property is dirty
INFO [main]: c.v.h.m.l.f.InterceptorDirtyCheckingTest - The orderedBy property is dirty
INFO [main]: c.v.h.m.l.f.InterceptorDirtyCheckingTest - The orderedOn property is dirty
DEBUG [main]: o.h.e.i.AbstractFlushingEventListener - Flushed: 0 insertions, 1 updates, 0 deletions to 1 objects
DEBUG [main]: n.t.d.l.SLF4JQueryLoggingListener - Name: Time:1 Num:1 Query:{[update ORDER_LINE set number=?, orderedBy=?, orderedOn=? where id=?][123,Vlad,2014-08-20 07:35:05.649,1]}
INFO [main]: c.v.h.m.l.f.InterceptorDirtyCheckingTest - The orderedBy property is dirty
DEBUG [main]: o.h.e.i.AbstractFlushingEventListener - Flushed: 0 insertions, 1 updates, 0 deletions to 1 objects
DEBUG [main]: n.t.d.l.SLF4JQueryLoggingListener - Name: Time:0 Num:1 Query:{[update ORDER_LINE set number=?, orderedBy=?, orderedOn=? where id=?][123,Alex,2014-08-20 07:35:05.649,1]}
手动脏检查机制已检测到传入的更改,并将其传播到刷新事件侦听器。
鲜为人知的CustomEntityDirtinessStrategy
CustomEntityDirtinessStrategy是Hibernate API的新增功能,使我们能够提供特定于应用程序的脏检查机制。 该接口可以如下实现:
public static class EntityDirtinessStrategy implements CustomEntityDirtinessStrategy {
@Override
public boolean canDirtyCheck(Object entity, EntityPersister persister, Session session) {
return entity instanceof DirtyAware;
}
@Override
public boolean isDirty(Object entity, EntityPersister persister, Session session) {
return !cast(entity).getDirtyProperties().isEmpty();
}
@Override
public void resetDirty(Object entity, EntityPersister persister, Session session) {
cast(entity).clearDirtyProperties();
}
@Override
public void findDirty(Object entity, EntityPersister persister, Session session, DirtyCheckContext dirtyCheckContext) {
final DirtyAware dirtyAware = cast(entity);
dirtyCheckContext.doDirtyChecking(
new AttributeChecker() {
@Override
public boolean isDirty(AttributeInformation attributeInformation) {
String propertyName = attributeInformation.getName();
boolean dirty = dirtyAware.getDirtyProperties().contains( propertyName );
if (dirty) {
LOGGER.info("The {} property is dirty", propertyName);
}
return dirty;
}
}
);
}
private DirtyAware cast(Object entity) {
return DirtyAware.class.cast(entity);
}
}
要注册CustomEntityDirtinessStrategy实现,我们必须设置以下Hibernate属性:
properties.setProperty("hibernate.entity_dirtiness_strategy", EntityDirtinessStrategy.class.getName());
运行我们的测试将产生以下输出:
INFO [main]: c.v.h.m.l.f.CustomEntityDirtinessStrategyTest - The number property is dirty
INFO [main]: c.v.h.m.l.f.CustomEntityDirtinessStrategyTest - The orderedBy property is dirty
INFO [main]: c.v.h.m.l.f.CustomEntityDirtinessStrategyTest - The orderedOn property is dirty
DEBUG [main]: o.h.e.i.AbstractFlushingEventListener - Flushed: 0 insertions, 1 updates, 0 deletions to 1 objects
DEBUG [main]: n.t.d.l.SLF4JQueryLoggingListener - Name: Time:1 Num:1 Query:{[update ORDER_LINE set number=?, orderedBy=?, orderedOn=? where id=?][123,Vlad,2014-08-20 12:51:30.068,1]}
INFO [main]: c.v.h.m.l.f.CustomEntityDirtinessStrategyTest - The orderedBy property is dirty
DEBUG [main]: o.h.e.i.AbstractFlushingEventListener - Flushed: 0 insertions, 1 updates, 0 deletions to 1 objects
DEBUG [main]: n.t.d.l.SLF4JQueryLoggingListener - Name: Time:0 Num:1 Query:{[update ORDER_LINE set number=?, orderedBy=?, orderedOn=? where id=?][123,Alex,2014-08-20 12:51:30.068,1]}
结论
尽管默认的字段级检查或字节码检测替代方法足以满足大多数应用程序的需求,但有时您还是需要对变更检测过程进行控制。 在长期项目中,定制某些内置机制以满足特殊的服务质量要求并不少见。 框架采用决定还应该考虑框架的可扩展性和定制支持。
- 代码可在GitHub上获得 。
翻译自: https://www.javacodegeeks.com/2014/09/how-to-customize-hibernate-dirty-checking-mechanism.html
hibernate自定义