Programing

최대 절전 모드에서 분리 된 객체를 다시 부착하는 올바른 방법은 무엇입니까?

lottogame 2020. 5. 21. 08:00
반응형

최대 절전 모드에서 분리 된 객체를 다시 부착하는 올바른 방법은 무엇입니까?


동일한 ID의 객체가 이미 세션에 존재할 수 있지만 분리 된 객체를 최대 절전 모드 세션에 다시 연결 해야하는 상황이 있습니다. 이로 인해 오류가 발생할 수 있습니다.

지금은 두 가지 중 하나를 수행 할 수 있습니다.

  1. getHibernateTemplate().update( obj )최대 절전 모드 세션에 개체가없는 경우에만 작동합니다. 주어진 식별자를 가진 객체가 나중에 필요할 때 세션에 이미 존재한다는 예외가 발생합니다.

  2. getHibernateTemplate().merge( obj )최대 절전 모드 세션에 개체가있는 경우에만 작동합니다. 나중에 이것을 사용하면 객체가 세션에 있어야 할 때 예외가 발생합니다.

이 두 가지 시나리오에서 세션을 객체에 일반적으로 어떻게 첨부 할 수 있습니까? 더 우아한 솔루션이 있어야하기 때문에이 문제의 솔루션 흐름을 제어하기 위해 예외를 사용하고 싶지 않습니다.


따라서 JPA에서 오래된 분리 엔티티를 다시 첨부 할 방법이없는 것 같습니다.

merge() 오래된 상태를 DB로 푸시하고 중재 업데이트를 덮어 씁니다.

refresh() 분리 된 엔티티에서 호출 할 수 없습니다.

lock() 분리 된 엔터티에서 호출 할 수없고, 가능한 경우에도 엔터티를 다시 연결하여 'LockMode.NONE'인수와 함께 'lock'을 호출하면 잠금이 아니라 잠금이 아니라는 것을 암시하는 것이 가장 직관적 인 API 디자인입니다. 나는 본 적이있다.

그래서 당신은 붙어 있습니다. 없다 detach()방법은 있지만, attach()reattach(). 개체 수명주기의 분명한 단계는 사용할 수 없습니다.

JPA에 대한 비슷한 질문의 수로 판단 할 때 JPA가 일관된 모델을 가지고 있다고 주장하더라도 많은 시간을 낭비하는 저주를받은 대부분의 프로그래머의 정신 모델과 일치하지 않는 것 같습니다. JPA는 가장 간단한 작업을 수행하고 응용 프로그램 전체에서 캐시 관리 코드로 끝납니다.

그것을 수행하는 유일한 방법은 오래된 분리 된 엔티티를 버리고 동일한 ID로 찾기 쿼리를 수행하여 L2 또는 DB에 충돌하는 것입니다.

미크


이 모든 대답은 중요한 차이점을 놓치고 있습니다. update ()는 객체 그래프를 세션에 (다시) 첨부하는 데 사용됩니다. 전달한 객체는 관리되는 객체입니다.

merge ()는 실제로 (재) 첨부 API가 아닙니다. merge ()에 반환 값이 있습니까? 전달 된 그래프가 아닌 관리되는 그래프를 반환하기 때문입니다. merge ()는 JPA API이며 해당 동작은 JPA 사양에 의해 관리됩니다. merge ()에 전달한 객체가 이미 관리되어있는 경우 (이미 세션과 연결되어있는 경우) Hibernate와 함께 작동하는 그래프입니다. 전달 된 객체는 merge ()에서 반환 된 것과 동일한 객체입니다. 그러나 merge ()에 전달한 객체가 분리되면 Hibernate는 관리되는 새로운 객체 그래프를 생성하고 분리 된 그래프의 상태를 새로운 관리 형 그래프로 복사합니다. 다시 말하지만, 이는 모두 JPA 사양에 따라 결정되고 적용됩니다.

"이 엔티티가 관리되는지 또는 관리되도록"에 대한 일반적인 전략의 관점에서 아직 삽입되지 않은 데이터를 고려할 것인지에 따라 다릅니다. 당신이 그렇게 가정하면, 같은 것을 사용하십시오

if ( session.contains( myEntity ) ) {
    // nothing to do... myEntity is already associated with the session
}
else {
    session.saveOrUpdate( myEntity );
}

update () 대신 saveOrUpdate ()를 사용했습니다. 아직 삽입되지 않은 데이터를 처리하지 않으려면 update ()를 대신 사용하십시오.


비 외교적 대답 : 확장 된 지속성 컨텍스트를 찾고있을 것입니다. 이것이 Seam Framework 의 주요 이유 중 하나입니다. 특히 Spring에서 Hibernate를 사용하는 데 어려움을 겪고 있다면 Seam의 문서를 확인하십시오 .

외교적 답변 : 이것은 Hibernate docs에 설명되어있다 . 더 자세한 설명이 필요하면 "분리 된 객체로 작업하기"라는 Hibernate를 사용한 Java Persistence 섹션 9.3.2를 살펴보십시오 . 나는 거라고 강하게 당신이 Hibernate에서 CRUD보다 더 많은 것을하고 있다면이 책을받을 것을 권장합니다.


엔터티가 수정되지 않았다고 확신하는 경우 (또는 수정 내용이 손실 될 것이라는 데 동의 한 경우) 잠금을 사용하여 세션에 다시 연결할 수 있습니다.

session.lock(entity, LockMode.NONE);

아무것도 잠그지 않지만 세션 캐시에서 엔티티를 가져 오거나 (없는 경우) DB에서 엔티티를 읽습니다.

"예를 들어 HttpSession의"엔터티에서 관계를 탐색 할 때 LazyInitException을 방지하는 것이 매우 유용합니다. 먼저 엔티티를 "다시 첨부"하십시오.

상속이 매핑 된 경우 (getId ()에서 이미 예외가 발생 함)를 제외하고 get을 사용하는 것도 가능합니다.

entity = session.get(entity.getClass(), entity.getId());

JavaDoc으로 돌아가서 org.hibernate.Session다음을 발견했습니다.

과도 인스턴스 호출에 의해 영속화 할 수있다 save(), persist()또는 saveOrUpdate(). 을 호출하여 영구 인스턴스를 일시적으로 만들 수 있습니다 delete(). get()또는 load()메소드가 반환 한 모든 인스턴스 는 영구적입니다. 분리 된 인스턴스를 호출하여 영속화 할 수있다 update(), saveOrUpdate(), lock()또는 replicate(). 임시 인스턴스 또는 분리 된 인스턴스의 상태는을 호출하여 새 영구 인스턴스로 유지 될 수도 있습니다 merge().

따라서 update(), saveOrUpdate(), lock(), replicate()merge()후보 옵션이 있습니다.

update(): 식별자가 동일한 영구 인스턴스가 있으면 예외가 발생합니다.

saveOrUpdate(): 저장 또는 업데이트

lock(): 더 이상 사용되지 않음

replicate(): 현재 식별자 값을 재사용하여 주어진 분리 된 인스턴스의 상태를 유지합니다.

merge(): Returns a persistent object with the same identifier. The given instance does not become associated with the session.

Hence, lock() should not be used straightway and based on the functional requirement one or more of them can be chosen.


I did it that way in C# with NHibernate, but it should work the same way in Java:

public virtual void Attach()
{
    if (!HibernateSessionManager.Instance.GetSession().Contains(this))
    {
        ISession session = HibernateSessionManager.Instance.GetSession();
        using (ITransaction t = session.BeginTransaction())
        {
            session.Lock(this, NHibernate.LockMode.None);
            t.Commit();
        }
    }
}

First Lock was called on every object because Contains was always false. The problem is that NHibernate compares objects by database id and type. Contains uses the equals method, which compares by reference if it's not overwritten. With that equals method it works without any Exceptions:

public override bool Equals(object obj)
{
    if (this == obj) { 
        return true;
    } 
    if (GetType() != obj.GetType()) {
        return false;
    }
    if (Id != ((BaseObject)obj).Id)
    {
        return false;
    }
    return true;
}

Session.contains(Object obj) checks the reference and will not detect a different instance that represents the same row and is already attached to it.

Here my generic solution for Entities with an identifier property.

public static void update(final Session session, final Object entity)
{
    // if the given instance is in session, nothing to do
    if (session.contains(entity))
        return;

    // check if there is already a different attached instance representing the same row
    final ClassMetadata classMetadata = session.getSessionFactory().getClassMetadata(entity.getClass());
    final Serializable identifier = classMetadata.getIdentifier(entity, (SessionImplementor) session);

    final Object sessionEntity = session.load(entity.getClass(), identifier);
    // override changes, last call to update wins
    if (sessionEntity != null)
        session.evict(sessionEntity);
    session.update(entity);
}

This is one of the few aspects of .Net EntityFramework I like, the different attach options regarding changed entities and their properties.


I came up with a solution to "refresh" an object from the persistence store that will account for other objects which may already be attached to the session:

public void refreshDetached(T entity, Long id)
{
    // Check for any OTHER instances already attached to the session since
    // refresh will not work if there are any.
    T attached = (T) session.load(getPersistentClass(), id);
    if (attached != entity)
    {
        session.evict(attached);
        session.lock(entity, LockMode.NONE);
    }
    session.refresh(entity);
}

Sorry, cannot seem to add comments (yet?).

Using Hibernate 3.5.0-Final

Whereas the Session#lock method this deprecated, the javadoc does suggest using Session#buildLockRequest(LockOptions)#lock(entity)and if you make sure your associations have cascade=lock, the lazy-loading isn't an issue either.

So, my attach method looks a bit like

MyEntity attach(MyEntity entity) {
    if(getSession().contains(entity)) return entity;
    getSession().buildLockRequest(LockOptions.NONE).lock(entity);
    return entity;

Initial tests suggest it works a treat.


Perhaps it behaves slightly different on Eclipselink. To re-attach detached objects without getting stale data, I usually do:

Object obj = em.find(obj.getClass(), id);

and as an optional a second step (to get caches invalidated):

em.refresh(obj)

try getHibernateTemplate().replicate(entity,ReplicationMode.LATEST_VERSION)


In the original post, there are two methods, update(obj) and merge(obj) that are mentioned to work, but in opposite circumstances. If this is really true, then why not test to see if the object is already in the session first, and then call update(obj) if it is, otherwise call merge(obj).

The test for existence in the session is session.contains(obj). Therefore, I would think the following pseudo-code would work:

if (session.contains(obj))
{
    session.update(obj);
}
else 
{
    session.merge(obj);
}

to reattach this object, you must use merge();

this methode accept in parameter your entity detached and return an entity will be attached and reloaded from Database.

Example :
    Lot objAttach = em.merge(oldObjDetached);
    objAttach.setEtat(...);
    em.persist(objAttach);

calling first merge() (to update persistent instance), then lock(LockMode.NONE) (to attach the current instance, not the one returned by merge()) seems to work for some use cases.


try getHibernateTemplate().saveOrUpdate()

참고URL : https://stackoverflow.com/questions/912659/what-is-the-proper-way-to-re-attach-detached-objects-in-hibernate

반응형