2012-10-16 2 views
2

최대 절전 모드 4.1을 사용하고 있으며 개체 컬렉션에 추가 된 임시 태그 목록을 저장하려고 할 때마다 다음 예외가 발생합니다.정직하게 이해하지 못하는 TransientObjectException을 해결하는 방법

org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.myapplication.domain.Tag 
    at org.hibernate.engine.internal.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:249) 
    at org.hibernate.type.EntityType.getIdentifier(EntityType.java:459) 
    at org.hibernate.type.ManyToOneType.nullSafeSet(ManyToOneType.java:132) 
    at org.hibernate.persister.collection.AbstractCollectionPersister.writeElement(AbstractCollectionPersister.java:811) 

이제 설명을 좀해야합니다. 내 태그 클래스에는 동의어 모음이 있습니다. 나는이 관계를 추가하기 전에,이 예외를 가진 적이 없어 모든 것이 잘 작동 :

여기

<class name="com.myapplication.domain.Tag"> 
    <id name="id" column="tag_id" type="long"> 
     <generator class="native"/> 
    </id> 
    <property name="term" index="termIndex" unique="true" not-null="true" /> 
    <property name="description" /> 
    <property name="approved" /> 

    <many-to-one name="masterTag" column="master_tag_id" 
       class="com.myapplication.domain.Tag" /> 

    <bag name="synonyms"> 
     <key column="master_tag_id" /> 
     <one-to-many class="com.myapplication.domain.Tag" /> 
    </bag> 
</class> 

객체가 태그를 매핑하는 방법입니다. Tags 클래스는 단지 구성 요소 클래스입니다. Tags.list는 Hibernate가 영속하고있는 실제 콜렉션이다.

<component name="tags"> 
     <bag name="list" table="user_to_tag"> 
      <key column="user_id" /> 
      <many-to-many class="com.myapplication.domain.Tag" column="tag_id"/> 
     </bag> 
    </component> 

실제로 전혀 동의어가 포함되지 않은 데이터베이스에 저장하려고 태그는, 그래서 그것을 믿지 않는 일시적인 동의어 태그와 아무 상관이있다. 나는 캐스케이드 (cascade)하기를 원하지 않습니다. 그래서 <bag> 태그에 캐스케이드 속성을 부여하지 않았습니다.

사용자는 대개 문자열 목록을 통해 시스템에 태그를 삽입합니다. 나는 태그를 포함하는 객체가 저장 될 때마다 즉석에서 태그를 만들거나 기존 태그에 링크하기를 원하기 때문에이 방법을 사용했습니다. 어쩌면 이것은 가치가있는 것보다 더 큰 문제일까요? 태그 만 연결하면 문제가 해결 될 수 있습니다. 아마 Hibernate에서의 on-the-fly 태그 생성 처리의 복잡성은 처리 할 수있는 것보다 약간 더 많을 것입니다.

이것은 새로운/기존 태그를 모두 한 번에 저장하는 데 사용하는 방법입니다. 어떤 이유로

public void saveAll(Tags tags) { 
    List<Tag> tagsToOverwrite = new ArrayList<Tag>(); 

    Iterator<Tag> i = tags.getList().iterator(); 
    while(i.hasNext()) { 
     Tag tag = i.next(); 

     if(stopListService.hasWord(tag.getTerm())) { 
      i.remove(); 
     } else { 
      if(tag.isTransient()) { 
       Tag dbTag = findByTerm(tag.getTerm()); 

       if(dbTag == null) { 
        save(tag); 
       } else { 
        tagsToOverwrite.add(dbTag); 
       } 
      } 
     } 
    } 

    tags.overwriteAll(tagsToOverwrite); 
} 

그것은이 TransientObjectException를 throw 할 때의 그 Tag dbTag = findByTerm(tag.getTerm());에 실패 객체가 데이터베이스에 저장 될 때마다, 먼저 태그 컬렉션을 유지하기 위해이 방법으로 호출합니다.

public Tag findByTerm(final String term) { 
    Query query = getCurrentSession().createQuery(
     "from Tag tag " + 
     "where tag.term = :term " 
    ); 

    return (Tag) query 
     .setParameter("term", term.toLowerCase()) 
     .setCacheable(true) 
     .uniqueResult(); // This is where the exception gets thrown specifically 
} 

나는 내가 원하는 모든 데이타베이스에서의 여부를 내가 그것을 저장하기 전에 있는지 확인 단순히 때문에이 방법은,이 예외를 슬로우 왜 확실하지 않다 :이 방법은 간단한 쿼리입니다. 내가하는 모든 일이 질의를 할 때 객체가 저장되지 않는다고 불평하는 이유는 무엇입니까?

태그 목록에 하나의 태그 만 들어 있으면이 방법이 효과적입니다. 저장할 임시 태그의 수가 2 이상일 때만 실패합니다. 예를 들어,이 실패 여기

@Test 
public void saveShouldPersistTransientTags() { 
    User user = userRepository.find(1); 
      // makes list of 3 tags that are transient (id = 0) 
    user.getTags().setTagsAsString("training learning business"); 

    userRepository.save(user); 
    flush(); 

    user = userRepository.find(1); 
    assertEquals(3, user.getTags().getList().size()); 
} 

가 UserRepository.save() 방법 :

@Override 
public void save(User user) { 
    tagRepository.saveAll(user.getTags()); 

    super.save(user); 
} 

당신은 내가 처음 과도 모든 태그를 저장하려고 참조 (또는 그들을 볼 수 있듯이 데이터베이스에 저장). 그러나 Hibernate는 결코 묻지는 않았지만 어쨌든 예외가 던져지는 위치에있는 tagRepository.saveAll(user.getTags()); 콜에서 user.tags.list 콜렉션을 유지하려고합니다.

물론 Hibernate 매핑에서 synonyms 콜렉션을 제거하면이 테스트는 아무 문제없이 통과합니다.나는 synonym 수집을 활성화 할 때 그것은 단지 실패하고 그것은 단지는 learning 태그에 도달 할 때 실패합니다 (training 태그가 데이터베이스에서 발견되지 않고 잘 유지됩니다.)

어떤 생각을 왜 동의어로로 컬렉션이이 문제의 원인입니까?

시간을 절약하기 위해 곧이 문제를 파악할 수 없다면, 이런 경우에 Hibernate의 복잡성이 그렇게 좋은 것은 될 수 없으므로 JDBC를 사용하는 것이 매우 바람직합니다. 나는 이것이 행동에 주어진 버그일지도 모른다라고 생각한다. 그것은 여전히 ​​나에게 어떤 의미가 없습니다.

답변

4

당신이 Hibernate를 질의 할 준비가되었을 때 관리 객체들을 볼 수있다. 더러운 쿼리는 데이터베이스로 플러시 (flush)를 시도하여 쿼리가 최신 데이터 스토어에 도달하려고합니다. 당신의 코드에서 당신은 관리되지 않는 (아마 새로운?) 동의어를 콜렉션에 추가하고, Hibernate가 더티 객체를 플러시함으로써 당신을 '도울'때 당신은 그 예외를 얻는다.

+0

이 테스트를 실행하는 코드는 동의어를 처리하지 않습니다. (태그 매핑에서 동의어 컬렉션을 제거하면 동의어가 어떻게 추가되는지 알 수 없습니다. 디버거에서 동의어 모든 3 개의 일시적인 태그에 대한 목록이 비어 있습니다. 내가 계속보고 싶습니다.이 하나가 완전히 엉망이되었습니다. – egervari

+0

다른 모든 테스트를 주석 처리하고이 테스트 만 실행하고 어떻게 작동하는지 볼 수 있습니다. 또한 최대 절전 모드 로깅을 설정하고 – digitaljoel

+0

예, 테스트가 100 % 격리되지 않았습니다. 모든 로그를 켜서 로그를 확인할 것입니다. 찾을 수 있기를 바랍니다. 이것이 최대 절전 모드의 버그라고 확신합니다. . 그냥 태그에 대한 봄의 JdbcTemplate을 사용하고 싶습니다. – egervari