如何自定义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]}

结论

尽管默认的字段级检查或字节码检测替代方法足以满足大多数应用程序的需求,但有时您还是需要获得对变更检测过程的控制权。 在长期项目中,定制某些内置机制以满足特殊的服务质量要求并不少见。 框架采用决定还应该考虑框架的可扩展性和定制支持。

翻译自: https://www.javacodegeeks.com/2014/09/how-to-customize-hibernate-dirty-checking-mechanism.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值