Exception in thread "main" org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: org.pojo.Group
at org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:219)
at org.hibernate.type.EntityType.getIdentifier(EntityType.java:397)
at org.hibernate.type.ManyToOneType.isDirty(ManyToOneType.java:242)
at org.hibernate.type.TypeFactory.findDirty(TypeFactory.java:597)
at org.hibernate.persister.entity.AbstractEntityPersister.findDirty(AbstractEntityPersister.java:3123)
at org.hibernate.event.def.DefaultFlushEntityEventListener.dirtyCheck(DefaultFlushEntityEventListener.java:479)
at org.hibernate.event.def.DefaultFlushEntityEventListener.isUpdateNecessary(DefaultFlushEntityEventListener.java:204)
at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:127)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:196)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:76)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at org.control.AccountTest.main(AccountTest.java:37)
說到這個例外的問題,看第一行就大概明瞭了
object references an unsaved transient instance
看到這個大致上的可能情況就是 "處於持久狀態的物件不能引用(reference)暫態物件(transition)"
他的理由很簡單 一個受 session 控管的物件根本無法參照到一個處於暫態的物件
因為處於暫態的物件不可能會在資料庫後方有對應的資料存在,故無法參照。
以下以一個多對一的例子來看一下,我僅顯示兩個物件 User(多) 和 Group(一) 的映射檔
以下以 JUint 進行這個錯誤的測試
package org.test; import org.pojo.*; import java.util.Date; import org.control.HibernateUtil; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.tool.hbm2ddl.SchemaExport; import org.model.Account; import junit.framework.TestCase; public class test extends TestCase //繼承 JUnit 的 TestCase { public void testManyToOne() { SessionFactory factory = HibernateUtil.getSessionFactory(); Session session = factory.openSession(); Transaction tx = session.beginTransaction(); //底下建立兩個 Group Group g1 = new Group(); g1.setName("group1"); Group g2 = new Group(); g2.setName("group2"); User u; for(int i=0;i<5;i++){ u = new User(); u.setName("Username" + i); if(i < 3){ u.setGroup(g1); } else{ u.setGroup(g2); } session.save(u); } //以上程式過程中都未執行session.save(g1)&session.save(g2) //所以這兩個物件都處於Transition狀態 tx.commit(); session.close(); } }
以上的範例如果執行時應該會產生五個 SQL 的 INSERT 語句
Hibernate: insert into t_user (name, group_id) values (?, ?)
Hibernate: insert into t_user (name, group_id) values (?, ?)
Hibernate: insert into t_user (name, group_id) values (?, ?)
Hibernate: insert into t_user (name, group_id) values (?, ?)
Hibernate: insert into t_user (name, group_id) values (?, ?)
接著在拋出 object references an unsaved transient instance 的例外
解決問題很簡單阿 就把 g1和g2物件 save() 即可,所以在以上程式的迴圈加上
session.save(g1) 和 session.save(g2) 就好了
這時候你可能有個問題,如果我把這個 save()動作移動到迴圈後再執行呢
他最後結果還是會成功 只是會多跑幾個SQL語句 如下所示
Hibernate: insert into t_user (name, group_id) values (?, ?)
Hibernate: insert into t_user (name, group_id) values (?, ?)
Hibernate: insert into t_user (name, group_id) values (?, ?)
Hibernate: insert into t_user (name, group_id) values (?, ?)
Hibernate: insert into t_user (name, group_id) values (?, ?)
Hibernate: insert into t_group (name) values (?)
Hibernate: insert into t_group (name) values (?)
Hibernate: update t_user set name=?, group_id=? where id=?
Hibernate: update t_user set name=?, group_id=? where id=?
Hibernate: update t_user set name=?, group_id=? where id=?
Hibernate: update t_user set name=?, group_id=? where id=?
Hibernate: update t_user set name=?, group_id=? where id=?
以上顯示出了 Hibernate 會先執行t_user 的 INSERT 但是外鍵欄位則尚未賦值
待 t_Group INSERT 之後再去對 t_user 執行 UPDATE的動作 以更新外來建
沒有留言:
張貼留言