2012-10-16 69 views
2

我正在使用Hibernate 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> 

我想在數據庫中保存實際上完全不含有任何同義詞的標籤,所以我不認爲這有什麼用短暫的同義詞標籤。我不希望這些級聯,這就是爲什麼我沒有給它在<bag>標籤上的級聯屬性。

用戶通常會通過字符串列表將標籤插入系統。我這樣做是因爲我想在包含標籤的對象被保存時同時創建標籤或鏈接到現有標籤。也許這比它的價值更麻煩?如果我只讓他們鏈接到存在的標籤,那麼我知道我可以解決我的問題。在Hibernate中處理動態標記創建的複雜性可能比處理它更復雜一點?

這是我用來一次性保存新/現有標籤的方法。當一個對象被保存到數據庫中,它首先調用這個方法,保留標籤集合:

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); 
} 

出於某種原因,它Tag dbTag = findByTerm(tag.getTerm());失敗,當它拋出TransientObjectException的。這種方法僅僅是一個簡單的查詢:

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集合(被持久化就好了training標籤沒有在數據庫中找到和。)

任何想法只有失敗,而且只有當它到達learning標籤失敗,爲什麼同義詞收集是造成這個問題?

如果我不能很快弄清楚這個問題,爲了節省時間,我非常想用JDBC來做這件事,因爲Hibernate在這種情況下的複雜性並不是那麼好。我認爲這可能是一個給定行爲的錯誤。它對我仍然沒有任何意義。

回答

4

當你準備好查詢時,Hibernate可能會查看它的託管對象。那些髒的將嘗試刷新到數據庫,以便查詢打到最新的數據存儲。在你的代碼中的某處,你正在向該集合添加非託管(可能是新的)同義詞,然後當Hibernate試圖通過刷新髒對象來「幫助」你時,你會得到該異常。

+0

運行此測試的代碼雖然不處理同義詞:(如果我只是從標記映射中刪除同義詞集合,測試會通過。我不知道如何添加同義詞。在調試器中,同義詞所有3個瞬變標記的列表都是空的,我會繼續尋找,這個我完全難倒了 – egervari

+0

你可以評論掉其他每一個測試,只運行這個測試,看看它是如何運行的,你也可以打開hibernate日誌記錄,得到一堆信息 – digitaljoel

+0

是的,測試在100%隔離失敗,我想我會打開所有的日誌,看看有什麼,我希望我能找到它,我有點確信這是一個在休眠。我試圖使用Spring的JdbcTemplate作爲標籤 – egervari

相關問題