Olá pessoas!
Estou com um erro estranho de inserção duplicada em minha aplicacao. Vou resumir o problema e depois coloco os codigos.
Tenho as seguintes classes:
- TestEntity - entidade simples com um metodo @PrePersist que está permanecendo na transação apos ela ser comitada pelo container
- Auditoria - entidade de auditoria
- Dataset - interface do DatasetBean
- DatasetBean - Stateless bean que implementa Dataset
- DatasetFactory - instancia um EJB de dataset (faz um lookup)
- PersistenceLifeCycleListener - um listener do ciclo de vida das entidades, definido no arquivo ORM.xml (segundo o site http://docs.jboss.org/hibernate/core/4.0/hem/en-US/html/listeners.html)
Coloquei o problema em um teste junit (estou usando Glassfish embedded):
@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.inserir(t);
System.out.println("fim");
}
O fluxo do teste é o seguinte:
- Após ter um objeto Dataset realizo a inserção
@Stateless
@EJB(name = "java:global/br/com/joaosavio/dataset/Dataset", beanInterface = Dataset.class)
public class DatasetBean<T> implements Dataset<T> {
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
private EntityManager entityManager;
@Override
public void inserir(T entidade) {
LOG.info("Inserting: " + entidade);
entityManager.persist(entidade);
}
...
}
- Após a inserção, o listener PostPersist é chamado, e tento inserir uma entidade de auditoria. Aqui, se eu descomentar //dataset.getEntityManager().clear(), o teste passa. Caso contrário dá erro (log abaixo).
public class PersistenceLifeCycleListener {
public void postPersist(Entidade entidade) {
LOG.info("Post-persist event");
if (!entidade.getClass().isAnnotationPresent(ClasseNaoAuditavel.class)) {
Dataset<Auditoria> dataset = DatasetFactory.criarDataset(); // basicamente faz um lookup
//dataset.getEntityManager().clear();
Auditoria auditoria = new Auditoria();
auditoria.setIdEntidade(String.valueOf(entidade.getId()));
dataset.inserir(auditoria);
}
}
public class DatasetFactory {
public static Dataset criarDataset() {
try {
return (Dataset) new InitialContext().lookup("java:global/br/com/joaosavio/dataset/Dataset");
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
Log - reparem nas linhas 8 e 12, o hibernate insere a mesma entidade:
...
INFO: embedded was successfully deployed in 28.963 milliseconds.
PlainTextActionReporterSUCCESSDescription: deploy AdminCommandApplication deployed with name embedded.
[name=embedded
2012-01-05 01:32:47,726 [main] INFO com.joaosavio.model.db.DatasetBean (DatasetBean.java:32) - Inserting: TestEntity{id=null, name=221918b3-747f-4b7e-96c7-51d80e81d1ee}
2012-01-05 01:32:48,129 [main] INFO com.joaosavio.event.PersistenceLifeCycleListener (PersistenceLifeCycleListener.java:26) - Pre-persist event
Hibernate: select max(testentity0_.id) as col_0_0_ from TestEntity testentity0_
Hibernate: insert into TestEntity (name, id) values (?, ?)
2012-01-05 01:32:49,932 [main] INFO com.joaosavio.event.PersistenceLifeCycleListener (PersistenceLifeCycleListener.java:30) - Post-persist event
2012-01-05 01:32:49,935 [main] INFO com.joaosavio.model.db.DatasetBean (DatasetBean.java:32) - Inserting: Auditoria{id=null, idEntidade=100}
2012-01-05 01:32:49,937 [main] INFO com.joaosavio.event.PersistenceLifeCycleListener (PersistenceLifeCycleListener.java:26) - Pre-persist event
Hibernate: insert into TestEntity (name, id) values (?, ?)
05/01/2012 01:32:49 com.sun.ejb.containers.BaseContainer postInvoke
AVISO: A system exception occurred during an invocation on EJB DatasetBean method public void com.joaosavio.model.db.DatasetBean.inserir(java.lang.Object)
javax.ejb.TransactionRolledbackLocalException: Exception thrown from bean
at com.sun.ejb.containers.BaseContainer.checkExceptionClientTx(BaseContainer.java:5049)
at com.sun.ejb.containers.BaseContainer.postInvokeTx(BaseContainer.java:4884)
at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:2039)
at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:1990)
at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:222)
at com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:88)
at $Proxy121.inserir(Unknown Source)
at com.joaosavio.event.PersistenceLifeCycleListener.postPersist(PersistenceLifeCycleListener.java:36)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.hibernate.ejb.event.ListenerCallback.invoke(ListenerCallback.java:48)
at org.hibernate.ejb.event.EntityCallbackHandler.callback(EntityCallbackHandler.java:110)
at org.hibernate.ejb.event.EntityCallbackHandler.postCreate(EntityCallbackHandler.java:83)
at org.hibernate.ejb.event.EJB3PostInsertEventListener.onPostInsert(EJB3PostInsertEventListener.java:49)
at org.hibernate.action.internal.EntityInsertAction.postInsert(EntityInsertAction.java:145)
at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:123)
at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:273)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:265)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:186)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:323)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:52)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1081)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:315)
at org.hibernate.engine.transaction.synchronization.internal.SynchronizationCallbackCoordinatorImpl.beforeCompletion(SynchronizationCallbackCoordinatorImpl.java:104)
at org.hibernate.engine.transaction.synchronization.internal.RegisteredSynchronization.beforeCompletion(RegisteredSynchronization.java:53)
at com.sun.enterprise.transaction.JavaEETransactionImpl.commit(JavaEETransactionImpl.java:435)
at com.sun.enterprise.transaction.JavaEETransactionManagerSimplified.commit(JavaEETransactionManagerSimplified.java:852)
at com.sun.ejb.containers.BaseContainer.completeNewTx(BaseContainer.java:5114)
at com.sun.ejb.containers.BaseContainer.postInvokeTx(BaseContainer.java:4879)
at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:2039)
at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:1990)
at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:222)
at com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:88)
at $Proxy121.inserir(Unknown Source)
at com.joaosavio.ContainerTest.test(ContainerTest.java:81)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:35)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:115)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:97)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.maven.surefire.booter.ProviderFactory$ClassLoaderProxy.invoke(ProviderFactory.java:103)
at $Proxy0.invoke(Unknown Source)
at org.apache.maven.surefire.booter.SurefireStarter.invokeProvider(SurefireStarter.java:150)
at org.apache.maven.surefire.booter.SurefireStarter.runSuitesInProcess(SurefireStarter.java:91)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:69)
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: Violation of PRIMARY KEY constraint 'PK__TestEntity__52442E1F'. Cannot insert duplicate key in object 'dbo.TestEntity'.
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1356)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1284)
...
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="simulajava" transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>jdbc/trimpaper</jta-data-source>
<class>com.joaosavio.model.vo.TestEntity</class>
<class>com.joaosavio.model.vo.Auditoria</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect"/>
<property name="hibernate.current_session_context_class" value="jta"/>
<property name="hibernate.session_factory_name" value="java:global/hibernate/SessionFactory"/>
<property name="hibernate.archive.autodetection" value="class"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
</properties>
</persistence-unit>
</persistence>
orm.xml
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"
version="2.0">
<persistence-unit-metadata>
<persistence-unit-defaults>
<entity-listeners>
<entity-listener class="com.joaosavio.event.PersistenceLifeCycleListener">
<pre-persist method-name="prePersist"/>
<post-persist method-name="postPersist"/>
</entity-listener>
</entity-listeners>
</persistence-unit-defaults>
</persistence-unit-metadata>
</entity-mappings>
TestEntity
@Entity
public class TestEntity implements Entidade {
@Id
private Integer id;
private String name;
// sets e gets
@PrePersist
public void preencherId() {
if (getId() == null || getId() == 0) {
Dataset d = DatasetFactory.criarDataset();
Integer i = (Integer) d.fetchJPQLFirstResult("SELECT MAX(te.id) FROM TestEntity te", null);
if (i == null || i < 100) {
setId(100);
} else {
setId(i + 1);
}
}
}
}
Auditoria
@ClasseNaoAuditavel
@Entity
public class Auditoria implements Entidade {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String idEntidade;
//sets e gets
Entidade
public interface Entidade extends Serializable {
Integer getId();
}
Uma gambiarra para resolver o problema é dar um clear no EntityManager. O que estou fazendo de errado?

