簡體   English   中英

除了遍歷每個字段之外,是否有更簡單的方法來告知JPA實體已被修改?

[英]Is there an easier way to tell that a JPA Entity has been modified other than looping through each field?

我有一個自定義的EmptyInterceptor ,可用於通過覆蓋onSaveonFlushDirty來設置有關創建日期,上次修改日期,用戶創建和上次修改用戶的onFlushDirty

這個系統對我們來說運作良好,但是我們發現一個問題,就是我們的代碼使用一些輸入數據盲目地調用實體上的設置器。 在這種情況下,即使數據沒有更改,休眠也會觸發我們的自定義攔截器,並設置最后修改的用戶和日期。 這將導致Hibernate更新數據庫中的實體。 我們知道這一點是因為我們在此表上有一個插入和更新觸發器。 如果禁用攔截器,則Hibernate將不會更新數據庫中的實體。

如果實體沒有真正更改,我們希望避免設置用戶和日期,因此在這種情況下不會發生任何更新。 我已經了解了Hibernate進行臟實體檢查的方式,並且已經將previousStatecurrentState數組傳遞到onFlushDirty ,我可以循環執行此檢查。 有沒有更簡單的方法可以做到這一點?

我查看了HibernateSession.isDirty(),但這並沒有告訴我這個特定的實體是否已更改,即使會話中有更改的實體也是如此。


更新

事實證明,有問題的代碼盲目地調用setter並不是問題。 設置子對象的集合而不是修改已經存在的集合是令人討厭的代碼。 在這種情況下,Hibernate認為該實體已發生更改-或至少認為足以調用Interceptor。

從設計的角度來看,該檢查實際上應該在前端/客戶端進行。 如果前端確定該記錄已由用戶修改,則它應將更新提交給服務器。

如果要在中間層(服務器端)執行此操作,則應考慮Hibernate實體的生命周期:瞬態,持久性,分離,已刪除,還應考慮session.save()session.merge()有何不同session.merge()session.saveOrUpdate()

此外,在管理會話時,還必須考慮不同的設計模式,例如“每次操作會話” 反模式 ,每次請求會話或每次會話會話模式等。

如果您有一個打開的會話,並且您的實體已經在該會話中(通過另一個操作),則Hibernate實際上可以為您進行臟檢查。 但是,如果您有一個分離的實體,並且該實體在會話中不存在,並說您進行了merge ,則Hibernate首先會通過發出SELECT從數據存儲(數據庫)中獲取該實體,並將該托管實體放入持久性存儲中。上下文,然后它將合並兩個實體,一個是您提供的實體,另一個是休眠的實體,並將其放入持久性上下文中,然后檢查是否有任何更改,從而進行臟檢查。

在您的情況下,由於要從臟檢查中排除最后修改的用戶名和時間,因此最好通過ID獲取實體(因為您可能在分離的實體上擁有ID),然后使用equals()hashCode()來執行您自己的臟檢查版本,如果不是,則調用merge。

您可能會認為這是對數據庫的一次額外旅行,但事實並非如此,因為即使在正常情況下,如果該實體尚未處於持久性上下文(即會話)中並且如果該實體還處於休眠狀態,Hibernate仍會進行對數據庫的額外旅行。是的,並且您執行一個獲取ID,Hibernate只會返回給您會話中已經擁有的內容,而不會訪問數據庫。

此參數不適用於saveOrUpdate,在這種情況下,Hibernate會直接將更新推送到數據庫,而不會進行臟檢查(如果實體不在會話中),並且如果它已在會話中,它將拋出一個說該實體已經在會話中的異常。

萬一任何人需要解決相同的問題而無需更改導致此問題的代碼,我將比較實現為:

    /**
     * Called upon entity UPDATE. For our BaseEntity, populates updated by with logged in user ID and
     * updated date-time.
     * <p>
     * Note that this method is called even if an object has NOT changed but someone's called a setter on
     * a Collection related to a child object (as opposed to modifying the Collection itself). Because of
     * that, the comparisons below are necessary to make sure the entity has really changed before setting
     * the update date and user.
     *
     * @see org.hibernate.EmptyInterceptor#onFlushDirty(java.lang.Object, java.io.Serializable, java.lang.Object[], java.lang.Object[], java.lang.String[], org.hibernate.type.Type[])
     */
    @Override
    public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState,
            String[] propertyNames, Type[] types)
    {

        boolean changed = false;

        if (entity instanceof BaseEntity) {
            logger.debug("onFlushDirty method called on " + entity.getClass().getCanonicalName());

            // Check to see if this entity really changed(see Javadoc above).
            boolean reallyChanged = false;
            for (int i = 0; i < propertyNames.length; i++) {
                // Don't care about the collection types because those can change and we still don't consider
                // this object changed when that happens.
                if (!(types[i] instanceof CollectionType)) {
                    boolean equals = Objects.equals(previousState[i], currentState[i]);
                    if (!equals) {
                        reallyChanged = true;
                        break;
                    }
                }
            }

            if (reallyChanged) {
                String userId = somehowGetUserIdForTheUserThatMadeTheRequest();
                Date dateTimeStamp = new Date();

                // Locate the correct field and update it.
                for (int i = 0; i < propertyNames.length; i++) {
                    if (UPDATE_BY_FIELD_NAME.equals(propertyNames[i])) {
                        currentState[i] = userId;
                        changed = true;
                    }

                    if (UPDATE_DATE_FIELD_NAME.equals(propertyNames[i])) {
                        currentState[i] = dateTimeStamp;
                        changed = true;
                    }
                }
            }
        }

        return changed;
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM