冬眠。实体变更\修订历史 [英] Hibernate. Entity change\revision history

查看:88
本文介绍了冬眠。实体变更\修订历史的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

框架:带有Hibernate 3的Spring 3。

Framework: Spring 3 with Hibernate 3.

数据库:Oracle 11

Database: Oracle 11

要求:我们的应用程序将有事件对象。这些事件对象的每一个都是一组实体属性(表列)的封装。因此,无论何时在我们的系统中更新任何实体,我们都需要确保对该实体所做的更改是否属于任何Event对象的一部分,如果是,则需要将该信息与记录该实体的记录一起存储在数据库中

Requirement: Our application will have Event objects. Each of these Event objects is a encapsulation of a set of Entity properties (table columns). So whenever any Entity is updated in our system, we need to make sure if the changes made to the Entity, is part of any of the Event objects and if yes, we need to store this information in the database along with a transcript recording the actual change.

解决方案(据我所知):

Solutions (That I know of):


  1. 使用像Hibernate Envers这样的功能强大的审计框架,并围绕审计表查询功能编写一个小的包装,以完成我所需要的。一个问题是,休眠Envers是否提供一种简单的方法来提供两个修订之间的更改。

  1. Use a full blown auditing framework like Hibernate Envers and write a small wrapper around the audit table query functionality, to accomplish what I need. One question is, does hibernate Envers have an easy way to provide what has changed between two revisions.

使用自定义注释将属性标记为属于事件,然后使用AOP作为保存操作的一部分来监视对这些属性的更改,并调用自定义写操作。

Use Custom Annotations to mark attributes to belong to an Event and Use AOP to monitor changes to these attributes as part of the save operations and invoke a custom write operations.

我喜欢第二个想法更好。

I like the second idea better.

请分享您对完成此工作的最佳方式的看法。

Please share your opinions\thoughts on the best possible way to get this done. Is there an existing solution for a problem of this sort?

推荐答案

我在需要拍摄快照的项目中有类似的要求吗?保存之前的复杂对象图。

i had similiar requirement in project where i am suppose to take snapshot of complex object graph before saving.

我应用的解决方案是
1)开发了自定义注释@Archivable,具有某些属性,例如nullify,ignore,orignal,setArchiveFlag

solution i have applied is 1) developed custom annotation @Archivable with certain attribute like nullify,ignore, orignal, setArchiveFlag

2)编写了hiberante deep cloner实用程序,该实用程序创建对象的副本并插入同一表中。 deep cloner在简单的技巧searlize上工作,然后对对象进行反earearlize,这将创建新实例,然后将id和version设置为null。

2) written hiberante deep cloner utility which create replica of object and insert into same table. deep cloner works on simple trick searlize and then desearlize object this will create new instances and then set id and version to null.

3)在实体拦截器中使用了cloner实用程序来决定是否存档天气。

3) used cloner utility in entity interceptor to take decision weather to archive or not.

下面是其中一些代码。

below is some of that code.

@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.TYPE })
public @interface Archivable {

    /** This will mark property as null in clone */
    public String[] nullify() default {};

    /**
     * If property is archivable but not from enclosing entity then specify as
     * ignore.
     */
    public String[] ignore() default {};

    /**
     * sets original reference to clone for back refer data. This annotation is
     * applicable to only root entity from where archiving started.
     * 
     * @return
     */
    public String original() default "";

    /**
     * if marks cloned entity to archived, assumes flag to be "isArchived". 
     * @return
     */
    public boolean setArchiveFlag() default false;
}


@Component
public class ClonerUtils {

    private static final String IS_ARCHIVED = "isArchived";
    @Autowired
    private SessionFactory sessionFactory;

    public Object copyAndSave(Serializable obj) throws Exception {
        List<BaseEntity> entities = new ArrayList<BaseEntity>();
        Object clone=this.copy(obj,entities);
        this.save(clone, entities);
        return clone;
    }

    public Object copy(Serializable obj,List<BaseEntity> entities) throws Exception{
        recursiveInitliaze(obj);
        Object clone = SerializationHelper.clone(obj);
        prepareHibernateObject(clone, entities);
        if(!getOriginal(obj).equals("")){
            PropertyUtils.setSimpleProperty(clone, getOriginal(obj), obj);
        }
        return clone;
    }


    private void save(Object obj,List<BaseEntity> entities){
        for (BaseEntity baseEntity : entities) {
            sessionFactory.getCurrentSession().save(baseEntity);
        }
    }

    @SuppressWarnings("unchecked")
    public void recursiveInitliaze(Object obj) throws Exception {
        if (!isArchivable(obj)) {
            return;
        }
        if(!Hibernate.isInitialized(obj))
            Hibernate.initialize(obj);
        PropertyDescriptor[] properties = PropertyUtils.getPropertyDescriptors(obj);
        for (PropertyDescriptor propertyDescriptor : properties) {
            Object origProp = PropertyUtils.getProperty(obj, propertyDescriptor.getName());
            if (origProp != null && isArchivable(origProp) && !isIgnore(propertyDescriptor, obj)) {
                this.recursiveInitliaze(origProp);
            }
            if (origProp instanceof Collection && origProp != null) {               
                for (Object item : (Collection) origProp) {
                    this.recursiveInitliaze(item);
                }
            }
        }
    }


    @SuppressWarnings("unchecked")
    private void prepareHibernateObject(Object obj, List entities) throws Exception {
        if (!isArchivable(obj)) {
            return;
        }
        if (obj instanceof BaseEntity) {
            ((BaseEntity) obj).setId(null);
            ((BaseEntity) obj).setVersion(null);
            if(hasArchiveFlag(obj)){
                PropertyUtils.setSimpleProperty(obj, IS_ARCHIVED, true);
            }
            entities.add(obj);
        }
        String[] nullifyList = getNullifyList(obj);
        for (String prop : nullifyList) {
            PropertyUtils.setProperty(obj, prop, null);
        }
        PropertyDescriptor[] properties = PropertyUtils.getPropertyDescriptors(obj);
        for (PropertyDescriptor propertyDescriptor : properties) {
            if (isIgnore(propertyDescriptor, obj)) {
                continue;
            }
            Object origProp = PropertyUtils.getProperty(obj, propertyDescriptor.getName());         
            if (origProp != null && isArchivable(origProp)) {
                this.prepareHibernateObject(origProp, entities);
            }   
            /**  This code is for element collection */
            if(origProp instanceof PersistentBag){
                Collection elemColl=createNewCollection(origProp);
                PersistentBag pColl=(PersistentBag) origProp;
                elemColl.addAll(pColl.subList(0, pColl.size()));
                PropertyUtils.setSimpleProperty(obj, propertyDescriptor.getName(), elemColl);
                continue;
            }
            if (origProp instanceof Collection && origProp != null) {
                Collection newCollection  = createNewCollection(origProp);
                PropertyUtils.setSimpleProperty(obj, propertyDescriptor.getName(), newCollection);
                for (Object item : (Collection) origProp) {
                    this.prepareHibernateObject(item, entities);
                }
            }
        }
    }



    @SuppressWarnings("unchecked")
    private Collection createNewCollection(Object origProp) {
        try {
            if(List.class.isAssignableFrom(origProp.getClass()))
                return new ArrayList((Collection)origProp);
            else if(Set.class.isAssignableFrom(origProp.getClass()))
                    return new HashSet((Collection)origProp);
            else{
                Collection tempColl=(Collection) BeanUtils.cloneBean(origProp);
                tempColl.clear();
                return tempColl;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new ArrayList();     
    }

    private boolean isIgnore(PropertyDescriptor propertyDescriptor,Object obj){
        String propertyName=propertyDescriptor.getName();
        String[] ignores=getIgnoreValue(obj);
        return ArrayUtils.contains(ignores, propertyName);
    }

    private String[] getIgnoreValue(Object obj) {
        String[] ignore=obj.getClass().getAnnotation(Archivable.class).ignore();
        return ignore==null?new String[]{}:ignore;
    }

    private String[] getNullifyList(Object obj) {
        String[] nullify=obj.getClass().getAnnotation(Archivable.class).nullify();
        return nullify==null?new String[]{}:nullify;
    }

    public boolean isArchivable(Object obj) {
        return obj.getClass().isAnnotationPresent(Archivable.class);
    }


    private String getOriginal(Object obj) {
        String original=obj.getClass().getAnnotation(Archivable.class).original();
        return original==null?"":original;
    }

    private boolean hasArchiveFlag(Object obj) {        
        return obj.getClass().getAnnotation(Archivable.class).setArchiveFlag();
    }

    @SuppressWarnings({ "unchecked", "unused" })
    private Collection getElemColl(Object obj, Object origProp) {
        Collection elemColl=createNewCollection(origProp);
        for (Object object : (Collection)origProp) {
            elemColl.add(object);
        }
        return elemColl;
    }

    @SuppressWarnings("unused")
    private boolean isElementCollection(Object obj, String name) {
        try {
            Annotation[] annotations=obj.getClass().getDeclaredField(name).getAnnotations();
            for (Annotation annotation : annotations) {
                if(annotation.annotationType() == ElementCollection.class)
                    return true;
            }
        } catch (Exception e) {
            e.printStackTrace();            
        }
        return false;
    }
    }

这篇关于冬眠。实体变更\修订历史的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆