0

私はSpringAOPを使用して、すべてのテーブルをそれほど面倒なく監査できるようにするための適切なアプローチを定義しようとしています。シナリオの例:Personという名前のテーブルとそれぞれのテーブルPersonLogがあります。このテーブルには、更新ごとに、イベントの種類を変更したユーザーに加えて、Personの値が格納されます。

簡単に言えば、私の質問は次のとおりです。

私のアドバイスクラスが、監査対象の新しいテーブルを変更することなく処理できるように賢くする方法を考え出そうとしています...可能であれば、テーブルCarとそのCarLogテーブルを作成したとしましょう。アドバイスの実装で何かを変更する必要はありません(Carが監査対象として自動的に識別され、CarLogエンティティを永続化できるようになります)--->テーブルCarが監査対象として(注釈によって)非常に簡単に識別できますが、 CarLogインスタンスを動的に作成して永続化する方法を見つけるのに苦労しています。

誰かがそれを達成する方法を考えることができますか?ありがとう。

4

3 に答える 3

2

これは、「変更データ キャプチャ」または CDC と呼ばれます。

個人的には、これは Spring や AOP の良い使い方ではないと思います。特にデータベースが複数のアプリケーションによって共有/変更されている場合は、データベース自体で行う方がよいと思います。

使用しているデータベースはわかりませんが、ベンダーのドキュメントを調べて、CDC をサポートするためにすぐに使用できるものを見つけることをお勧めします。

于 2012-11-07T00:50:59.300 に答える
1

保存する前に複雑なオブジェクトグラフのスナップショットを撮ると思われるプロジェクトでも、同様の要件がありました。

私が適用した解決策は、1)nullify、ignore、orignal、setArchiveFlagなどの特定の属性を持つ開発されたカスタムアノテーション@Archivableです。

2)オブジェクトのレプリカを作成して同じテーブルに挿入するhiberanteディープクローンユーティリティを作成しました。ディープクローン作成機能は、単純なトリックであるsearlizeを実行してから、オブジェクトをdesearlizeします。これにより、新しいインスタンスが作成され、idとversionがnullに設定されます。

3)エンティティインターセプターのclonerユーティリティを使用して、アーカイブするかどうかを決定しました。

以下はそのコードの一部です。

@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;
}
}
于 2012-11-07T05:18:48.467 に答える
0

Envers は、監査目的で必要なものです

于 2012-11-07T01:29:02.210 に答える