2012-01-06 52 views
2

我在數據庫中出現了一個奇怪的雙重插入錯誤。我有以下類別:Hibernate錯誤在@PostPersist方法中持續存在實體

  • TestEntity - 具有@PrePersist和@PostPersist方法的實體。
  • 禮堂 - 審計實體
  • 數據集 - DatasetBean的接口
  • DatasetBean - 無狀態Bean上實現數據集
  • DatasetFactory - 例如數據集(查找)的EJB

我把這個問題在junit測試(我正在使用嵌入式Glassfish):

@Test 
public void test() throws NamingException { 
    Dataset<TestEntity> dataset = this.lookupBy(DatasetBean.class); 
    Assert.assertNotNull(dataset); 

    TestEntity t = new TestEntity();   
    t.setName(UUID.randomUUID().toString()); 

    dataset.insert(t); 
    System.out.println("end"); 
} 

測試流程如下:

  1. 剛開DataSet對象後,我嘗試插入TestEntity對象

    @Stateless @EJB(name = 「...」,beanInterface = Dataset.class) 公共類DatasetBean實現數據集{

    @PersistenceContext(type = PersistenceContextType.TRANSACTION) 
    private EntityManager entityManager; 
    
    @Override 
    public void insert(T entidade) { 
        LOG.info("Inserting: " + entidade); 
        entityManager.persist(entidade); 
    } 
    //... 
    

    }

  2. 使用DatasetFactory,我嘗試在@PostPer插入一個審計實體在TestEntity的SIST方法

    公共類DatasetFactory { 公共靜態數據集createDataset(){ 嘗試{ 回報率(數據集)新的InitialContext()查詢( 「...」)。 (ex){ } catch(Exception ex){ throw new RuntimeException(ex); }} }

    @Entity 公共類TestEntity實現myEntity所{ @Id 私人整數ID; 私人字符串名稱; //設置和獲取

    @PrePersist 
    public void fillId() { 
        if (getId() == null || getId() == 0) { 
         Dataset d = DatasetFactory.createDataset(); 
         Integer i = (Integer) d.fetchJPQLFirstResult("SELECT MAX(te.id) FROM TestEntity te"); 
         if (i == null || i < 100) { 
          setId(100); 
         } else { 
          setId(i + 1); 
         } 
        } 
    } 
    
    @PostPersist 
    public void audit() { 
        Dataset<Auditing> dataset = DatasetFactory.createDataset(); 
        // dataset.getEntityManager().clear(); 
        Auditing auditing = new Auditing(); 
        auditing.setIdEntidade(String.valueOf(this.getId())); 
        dataset.insert(auditing); 
    } 
    

    }

    @Entity 公共類禮堂實現myEntity所{ @Id @GeneratedValue(策略= GenerationType.IDENTITY) 私人整數ID; private String idEntity; //設置並獲取 }

    public interface MyEntity extends Serializable {整數getId();} }

登錄:

信息:已嵌入了成功部署於47.154毫秒。 PlainTextActionReporterSUCCESSDescription:部署名爲embedded的AdminCommandApplication。

2012-01-06 02:56:54826 [主要] INFO com.joaosavio.model.db.DatasetBean(DatasetBean.java:30) - 插入:TestEntity {ID = NULL,姓名= ea5c2af4-0ca7-48a2 -a82a-dbf582c570a9}

休眠:選擇最大(testentity0_.id)從TestEntity col_0_0_ testentity0_

休眠:(?)插入TestEntity(姓名,身份證)值

2012 -01-06 02:56:56,344 [main] INFO com.joaosavio.model.db.DatasetBean(DatasetBean.java:30) - 插入:Auditoria {id = null,idEntidade = 100}

休眠:插入TestEntity(姓名,ID)值

2012-01-06 02(?,?):56:56350 [主要] WARN org.hibernate.engine.jdbc.spi。 SqlExceptionHelper(SqlExceptionHelper.java:143) - SQL錯誤:2627,SQLSTATE:23000

2012-01-06 02:56:56352 [主要] ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper(SqlExceptionHelper.java :144) - 違反PRIMARY KEY約束'PK_ TestEntity_76818E95'。無法在對象'dbo.TestEntity'中插入重複鍵。

06/01/2012 2點56分56秒com.sun.ejb.containers.BaseContainer postInvoke

警告:在EJB DatasetBean方法public void com.joaosavio.model.db調用期間發生系統異常.DatasetBean.insert(java.lang.Object中) javax.ejb.TransactionRolledbackLocalException:異常來自豆 拋出...

造成的:javax.persistence.PersistenceException:org.hibernate.exception.ConstraintViolationException:初級違規KEY約束'PK_ TestEntity _76818E95'。無法在對象'dbo.TestEntity'中插入重複鍵。 ...

造成的:org.hibernate.exception.ConstraintViolationException:PRIMARY KEY約束違反 'PK_ TestEntity _76818E95'。無法在對象'dbo.TestEntity'中插入重複鍵。

注意事項:

依託,一切工作正常,如果我的審計實體(在TestEntity @PostPersist方法comented碼)的插入之前清除實體管理的事實,我相信TestEntity是陷入交易中。

我在做什麼錯?

回答

9

我見過一個非常類似的問題一次....你應該---

要非常小心@PostPersist! hibernate bean持久化或保存操作與數據庫插入不一樣!

問題是可能是這就是假設@PostPersist方法在插入數據後被調用....但是,情況並非總是如此! PostPersist方法是回調,但他們不是數據庫的回調!如您所知 - hibernate可能沒有提交您的事務並完全刷新它。如果您嘗試使用PostPersist來協調數據庫事務之間的障礙,那麼您就犯了一個錯誤。

解決方法是在單個正確計劃和管理的事務中完成所有插入操作,然後按鍵和級聯解決,以便hibernate能夠以正確的方式爲您組織插入操作---或者只是硬編碼存儲過程爲您完成工作。

我想你可能會在這裏合併有狀態和無狀態的事務邏輯。

+1

+1,文檔中說「執行實體管理器持久操作後實際執行或級聯。執行數據庫INSERT後調用此調用。」並且它還說「回調方法可能會引發RuntimeException。必須回滾當前事務(如果有)。」然後在提交事務之前它必須等待回調方法完成。 – Reddy 2012-08-21 12:39:36