如何自定义Hibernate脏检查机制
介紹
在上一篇文章中,我描述了Hibernate自動(dòng)臟檢查機(jī)制。 盡管您應(yīng)該始終喜歡它,但是有時(shí)您可能想添加自己的自定義污垢檢測(cè)策略。
自定義臟檢查策略
Hibernate提供以下定制機(jī)制:
- 休眠攔截器#findDirty()
- CustomEntityDirtinessStrategy
手動(dòng)檢查臟物
作為練習(xí),我將構(gòu)建一個(gè)手動(dòng)的臟檢查機(jī)制,以說明自定義更改檢測(cè)策略的難易程度:
自臟檢查實(shí)體
首先,我將定義一個(gè)DirtyAware接口,所有手動(dòng)臟檢查實(shí)體都必須實(shí)現(xiàn):
public interface DirtyAware {Set<String> getDirtyProperties();void clearDirtyProperties(); }接下來,我將在基類中封裝當(dāng)前的臟檢查邏輯:
public abstract class SelfDirtyCheckingEntity implements DirtyAware {private final Map<String, String> setterToPropertyMap = new HashMap<String, String>();@Transientprivate 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);}}@Overridepublic Set<String> getDirtyProperties() {return dirtyProperties;}@Overridepublic void clearDirtyProperties() {dirtyProperties.clear();}protected void markDirtyProperty() {String methodName = Thread.currentThread().getStackTrace()[2].getMethodName();dirtyProperties.add(setterToPropertyMap.get(methodName));} }所有手動(dòng)的臟檢查實(shí)體都必須擴(kuò)展此基類,并通過調(diào)用markDirtyProperty方法顯式標(biāo)記臟屬性。
實(shí)際的自臟檢查實(shí)體如下所示:
@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();} }每當(dāng)調(diào)用setter時(shí),關(guān)聯(lián)的屬性就會(huì)變臟。 為簡(jiǎn)單起見,當(dāng)我們將屬性還原為其原始值時(shí),此簡(jiǎn)單練習(xí)不涵蓋用例。
臟檢查測(cè)試
為了測(cè)試自臟檢查機(jī)制,我將運(yùn)行以下測(cè)試用例:
@Test public void testDirtyChecking() {doInTransaction(new TransactionCallable<Void>() {@Overridepublic 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回調(diào)使我們能夠控制臟屬性發(fā)現(xiàn)過程。 該方法可能返回:
- null ,將臟檢查委托給Hibernate默認(rèn)策略
- 一個(gè)int []數(shù)組,其中包含修改后的屬性索引
我們的Hibernate臟檢查攔截器如下所示:
public class DirtyCheckingInterceptor extends EmptyInterceptor {@Overridepublic 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);}}將此攔截器傳遞到當(dāng)前的SessionFactory配置時(shí),我們將獲得以下輸出:
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]}手動(dòng)臟檢查機(jī)制已檢測(cè)到傳入更改,并將其傳播到刷新事件偵聽器。
鮮為人知的CustomEntityDirtinessStrategy
CustomEntityDirtinessStrategy是Hibernate API的新增功能,它使我們能夠提供特定于應(yīng)用程序的臟檢查機(jī)制。 該接口可以實(shí)現(xiàn)如下:
public static class EntityDirtinessStrategy implements CustomEntityDirtinessStrategy {@Overridepublic boolean canDirtyCheck(Object entity, EntityPersister persister, Session session) {return entity instanceof DirtyAware;}@Overridepublic boolean isDirty(Object entity, EntityPersister persister, Session session) {return !cast(entity).getDirtyProperties().isEmpty();}@Overridepublic void resetDirty(Object entity, EntityPersister persister, Session session) {cast(entity).clearDirtyProperties();}@Overridepublic void findDirty(Object entity, EntityPersister persister, Session session, DirtyCheckContext dirtyCheckContext) {final DirtyAware dirtyAware = cast(entity);dirtyCheckContext.doDirtyChecking(new AttributeChecker() {@Overridepublic 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);}}要注冊(cè)CustomEntityDirtinessStrategy實(shí)現(xiàn),我們必須設(shè)置以下Hibernate屬性:
properties.setProperty("hibernate.entity_dirtiness_strategy", EntityDirtinessStrategy.class.getName());運(yùn)行我們的測(cè)試將產(chǎn)生以下輸出:
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]}結(jié)論
盡管默認(rèn)的字段級(jí)檢查或字節(jié)碼檢測(cè)替代方法足以滿足大多數(shù)應(yīng)用程序的需求,但有時(shí)您還是需要獲得對(duì)變更檢測(cè)過程的控制權(quán)。 在長(zhǎng)期項(xiàng)目中,定制某些內(nèi)置機(jī)制以滿足特殊的服務(wù)質(zhì)量要求并不少見。 框架采用決定還應(yīng)該考慮框架的可擴(kuò)展性和定制支持。
- 代碼可在GitHub上獲得 。
翻譯自: https://www.javacodegeeks.com/2014/09/how-to-customize-hibernate-dirty-checking-mechanism.html
總結(jié)
以上是生活随笔為你收集整理的如何自定义Hibernate脏检查机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2个在Java中将Byte []数组转换
- 下一篇: 探索SwitchYard 2.0.0.A