Hibernate error persists an entity in @PostPersist method

I have a weird error of double insert in the DB. I have the following classes:

  • TestEntity - entity with a @PrePersist and a @PostPersist methods.
  • Auditoria - audit entity
  • Dataset - interface of DatasetBean
  • DatasetBean - Stateless bean which implements Dataset
  • DatasetFactory - instance an EJB of Dataset (lookup)
  • I put the problem in a junit test (I'm using embedded 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");
    }
    

    The flow of the test is the following:

  • After gettin a Dataset object, I try to insert a TestEntity object

    @Stateless @EJB(name = "...", beanInterface = Dataset.class) public class DatasetBean implements Dataset {

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

    }

  • Using the DatasetFactory, I try to insert an Auditing entity in a @PostPersist method of the TestEntity

    public class DatasetFactory { public static Dataset createDataset() { try { return (Dataset) new InitialContext().lookup("..."); } catch (Exception ex) { throw new RuntimeException(ex); } } }

    @Entity public class TestEntity implements MyEntity { @Id private Integer id; private String name; // sets and gets

    @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 public class Auditoria implements MyEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String idEntity; //sets and gets }

    public interface MyEntity extends Serializable { Integer getId(); }

  • Log:

    INFO: embedded was successfully deployed in 47.154 milliseconds. PlainTextActionReporterSUCCESSDescription: deploy AdminCommandApplication deployed with name embedded.

    2012-01-06 02:56:54,826 [main] INFO com.joaosavio.model.db.DatasetBean (DatasetBean.java:30) - Inserting: TestEntity{id=null, name=ea5c2af4-0ca7-48a2-a82a-dbf582c570a9}

    Hibernate: select max(testentity0_.id) as col_0_0_ from TestEntity testentity0_

    Hibernate: insert into TestEntity (name, id) values (?, ?)

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

    Hibernate: insert into TestEntity (name, id) values (?, ?)

    2012-01-06 02:56:56,350 [main] WARN org.hibernate.engine.jdbc.spi.SqlExceptionHelper (SqlExceptionHelper.java:143) - SQL Error: 2627, SQLState: 23000

    2012-01-06 02:56:56,352 [main] ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper (SqlExceptionHelper.java:144) - Violation of PRIMARY KEY constraint 'PK_TestEntity_76818E95'. Cannot insert duplicate key in object 'dbo.TestEntity'.

    06/01/2012 02:56:56 com.sun.ejb.containers.BaseContainer postInvoke

    WARN: A system exception occurred during an invocation on EJB DatasetBean method public void com.joaosavio.model.db.DatasetBean.insert(java.lang.Object) javax.ejb.TransactionRolledbackLocalException: Exception thrown from bean ...

    Caused by: javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: Violation of PRIMARY KEY constraint 'PK_TestEntity_76818E95'. Cannot insert duplicate key in object 'dbo.TestEntity'. ...

    Caused by: org.hibernate.exception.ConstraintViolationException: Violation of PRIMARY KEY constraint 'PK_TestEntity_76818E95'. Cannot insert duplicate key in object 'dbo.TestEntity'.

    Considerations:

    Relying on the fact that everything works fine if I clear the entity manager before the insertion of the Auditing entity (comented code in @PostPersist method in TestEntity), I believe that the TestEntity is getting stuck in the transaction.

    What am I doing wrong???


    Ive seen a very similar issue once.... You should ---

    Be very careful with @PostPersist ! hibernate bean persist or save actions ARE NOT the same as database Insertions!

    The problem is likely that are that you are assuming that @PostPersist methods are invoked after the data has been inserted .... However , that is not always the case! PostPersist methods are callbacks BUT they are not callbacks from the database !!! As you know - hibernate may not have committed your transaction And flushed it completely. if you try to use PostPersist to coordinate barriers between your database transactions, you are making a mistake.

    The solution is to do all your inserts in a single properly planned and managed transaction, with keys and cascades worked out so that hibernate will be able to organize the inserts for you in the right way--- or just hardcode a stored procedure to do the work for you.

    I think you might be merging stateful and stateless transaction logic here.

    链接地址: http://www.djcxy.com/p/54072.html

    上一篇: 如何将jsf @ConversationScoped bean与hibernate组合主键一起使用?

    下一篇: Hibernate错误会使@PostPersist方法中的实体存在