I have a @Transactional service performing a persist action into an oracle DB. If i run a persist breaking a unique violation i get the expected rollbackException:ConstraintException.
The problem is that any subsequent request (even if not breaking the unique constraint) to persist throws the same exception.
It seems like JPA is not clearing the object to persist out of its transaction manager? Am i even close? I need a little explaination.
Repo:
@Repository
public class UserRepository {
@PersistenceContext(type=PersistenceContextType.EXTENDED)
private EntityManager em;
public User findUserById(long id){
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<User> query = builder.createQuery(User.class);
Root<User> root = query.from(User.class);
Predicate whereClause = builder.equal(root.get(User_.userId), id);
return em.createQuery(query.where(whereClause)).getSingleResult();
}
public User findUserByCredentials(String credentials){
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<User> query = builder.createQuery(User.class);
Root<User> root = query.from(User.class);
Predicate whereClause = builder.equal(root.get(User_.credentials), credentials);
return em.createQuery(query.where(whereClause)).getSingleResult();
}
public void registerUser(User user){
em.persist(user);
}
}
ServiceImpl:
@Transactional(readOnly=true)
@Service("userService")
public class UserServiceImpl implements UserService {
@Resource
private UserRepository userRepository;
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User findUserById(long id) {
return userRepository.findUserById(id);
}
public User findUserByCredentials(String credentials){
return userRepository.findUserByCredentials(credentials);
}
@Transactional(readOnly=false)
public void registerUser(User user){
userRepository.registerUser(user);
}
}
error throw in endpoint:
@PayloadRoot(localPart="RegisterUserRequest", namespace = "http://www.missingwire.com/schemas/User")
public RegisterUserResponseDocument registerUser(RegisterUserRequestDocument requestDoc){
RegisterUserRequest request = requestDoc.getRegisterUserRequest();
User user = new User();
user.setCredentials(request.getCredentials());
user.setPassword(request.getPassword());
user.setHonorRating(BigDecimal.valueOf(STARTING_USER_HONOR_RATING));
user.setAccountActive(true);
user.setDateCreated(new Date());
user.setVerified(false);
UserProfile userProfile = new UserProfile();
userProfile.setEmailAddress(request.getEmail());
userProfile.setFirstName(request.getFirstName());
userProfile.setLastName(request.getLastName());
userProfile.setDateCreated(new Date());
userProfile.setUser(user);
user.setUserProfile(userProfile);
**userService.registerUser(user);** //HERE IS THE EXCEPTION THROW
RegisterUserResponseDocument responseDoc = RegisterUserResponseDocument.Factory.newInstance();
RegisterUserResponse response = responseDoc.addNewRegisterUserResponse();
UserType userType = response.addNewUser();
userType.setAccountActive(user.getAccountActive());
userType.setCredentials(user.getCredentials());
userType.setDateCreated(DateConverter.convertDateToXML(user.getDateCreated()));
userType.setUserId(user.getUserId());
userType.setVerified(user.getVerified());
return responseDoc;
}
Exception:
org.springframework.orm.jpa.JpaSystemException: Error while committing
the transaction; nested exception is
javax.persistence.RollbackException: Error while committing the
transaction at
org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:311)
at
org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:102)
at
org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerSynchronization.convertException(ExtendedEntityManagerCreator.java:501)
at
org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerSynchronization.afterCommit(ExtendedEntityManagerCreator.java:481)
at
org.springframework.transaction.support.TransactionSynchronizationUtils.invokeAfterCommit(TransactionSynchronizationUtils.java:133)
at
org.springframework.transaction.support.TransactionSynchronizationUtils.triggerAfterCommit(TransactionSynchronizationUtils.java:121)
at
org.springframework.transaction.support.AbstractPlatformTransactionManager.triggerAfterCommit(AbstractPlatformTransactionManager.java:953)
at
org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:796)
at
org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
at
org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
at
org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at $Proxy37.registerUser(Unknown Source) at
com.missingwire.achieve.soa.endpoint.UserEndpoint.registerUser(UserEndpoint.java:76)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616) at
org.springframework.ws.server.endpoint.MethodEndpoint.invoke(MethodEndpoint.java:132)
at
org.springframework.ws.server.endpoint.adapter.MarshallingMethodEndpointAdapter.invokeInternal(MarshallingMethodEndpointAdapter.java:140)
at
org.springframework.ws.server.endpoint.adapter.AbstractMethodEndpointAdapter.invoke(AbstractMethodEndpointAdapter.java:53)
at
org.springframework.ws.server.MessageDispatcher.dispatch(MessageDispatcher.java:231)
at
org.springframework.ws.server.MessageDispatcher.receive(MessageDispatcher.java:172)
at
org.springframework.ws.transport.support.WebServiceMessageReceiverObjectSupport.handleConnection(WebServiceMessageReceiverObjectSupport.java:88)
at
org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter.handle(WebServiceMessageReceiverHandlerAdapter.java:57)
at
org.springframework.ws.transport.http.MessageDispatcherServlet.doService(MessageDispatcherServlet.java:221)
at
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:669)
at
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:585)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:637) at
javax.servlet.http.HttpServlet.service(HttpServlet.java:717) at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
at
org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:859)
at
org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:602)
at
org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
at java.lang.Thread.run(Thread.java:679) Caused by:
javax.persistence.RollbackException: Error while committing the
transaction at
org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:93) at
org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerSynchronization.afterCommit(ExtendedEntityManagerCreator.java:478)
… 39 more Caused by: javax.persistence.PersistenceException:
org.hibernate.exception.ConstraintViolationException: Could not
execute JDBC batch update at
org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1235)
at
org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1168)
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:81)
… 40 more Caused by:
org.hibernate.exception.ConstraintViolationException: Could not
execute JDBC batch update at
org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:96)
at
org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
at
org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
at
org.hibernate.jdbc.AbstractBatcher.prepareStatement(AbstractBatcher.java:114)
at
org.hibernate.jdbc.AbstractBatcher.prepareStatement(AbstractBatcher.java:109)
at
org.hibernate.jdbc.AbstractBatcher.prepareBatchStatement(AbstractBatcher.java:244)
at
org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2395)
at
org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2858)
at
org.hibernate.action.EntityInsertAction.execute(EntityInsertAction.java:79)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:267) at
org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:259)
at
org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:178)
at
org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at
org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1206) at
org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:375) at
org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:137)
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:76)
… 40 more Caused by: java.sql.BatchUpdateException: ORA-00001:
unique constraint (ACHIEVE.SYS_C0016488) violatedat
oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:10070)
at
oracle.jdbc.driver.OracleStatementWrapper.executeBatch(OracleStatementWrapper.java:213)
at
org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
at
org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)
You’re injecting an entity manager for an extended persistence context. That means that the lifetime of the persistence context is not tied to the lifetime of the transaction: it stays open until you close it explicitely.
Since you got a RollbackException, the persistence context is in a dirty, inconsistent state, and the only thing you can do is close it immediately.
If the persistence context was a transactional one, it would be closed automatically. But since you’re using an extended context, it’s up to you to close it explicitely.
Make sure to read and understand the following section of the Spring documentation: