开发者

Duplicate id using JPA2 and testing

开发者 https://www.devze.com 2023-01-30 17:07 出处:网络
I\'m a bit confused right now :-S I\'m working on a project that uses JPA2, Spring 3.0.5, Hibernate 3.6.0 Final. We have the following code (only relevant classes)

I'm a bit confused right now :-S

I'm working on a project that uses JPA2, Spring 3.0.5, Hibernate 3.6.0 Final. We have the following code (only relevant classes)

@Entity
public class User extends AbstractEntity implements Serializable {
    @Id
    @Column(name = "ID", nullable = false, insertable = true, updatable = true, length = 36)
    protected String id;

    @NotNull
    @Size(min = 1, max = 30)
    @Column(name = "NAME", length = 30, nullable = false)
    private String name;

    protected User() {
        id = java.util.UUID.randomUUID().toString();
    }

    @Override
    public boolean equals(Object object) {
        if (!(object instanceof User)) {
            return false;
        }
        User other = (User) object;
        if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }


}


@Repository("userDao")
public class UserDaoImpl implements UserDao {

    @PersistenceContext
    private EntityManager em;

    public void create(User user) throws PreexistingEntityException, Exception {
        try {
            em.persist(user);

        } catch (EntityExistsException ex) {
            logger.error("User " + user + " already exists.", ex);
            throw new PreexistingEntityException("User " + user + " already exists.", ex);
        } catch (Exception ex) {
            logger.error("Exception occurred:", ex);
            throw ex;
        }
    }
}



@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/testDaoContext.xml" })
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
@Transactional
public class UserDaoTest {

    private UserDao userDao;

    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Test
    public void testInsertUserExistingID() {
        User user = User.valueOf("1");
        user.setFirstname("DUMMY");
        user.setName("CRASH");
        logger.debug(user);
        try {
            userDao.create(user);
            sessionFactory.getCurrentSession().flush();
        } catch (PreexistingEntityException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        logger.debug("id = " + user.getId());

        User retrieved = userDao.find(user.getId());
        Assert.assertEquals(user.getId(), retrieved.getId());
        Assert.assertEquals("DUMMY", retrieved.getFirstname());
        Assert.assertEquals("CRASH", retrieved.getName());
    }

}

Now, when I run the test (I know, it's not a real unit test) with rollback set to false, I get the following stacktrace:

org.springframework.dao.DataIntegrityViolationException: Could not execute JDBC batch update; SQL [insert into PV_UMDB.USERS (CREATION_DT, CREATION_USR, MODIFICATION_USR, MODIFICATION_DT, VERSION, BIRTHDAY, EMAIL, FAX, FIRSTNAME, INTERNAL, MOBILE, NAME, PHONE, PICTURE, STAFF_NO, STAFF_NO_KBC, ID) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)]; constraint [PV_UMDB.USERS_PK]; nested exception is org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
    at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:637)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible开发者_JAVA百科(HibernateJpaDialect.java:102)
    at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:471)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
    at org.springframework.test.context.transaction.TransactionalTestExecutionListener$TransactionContext.endTransaction(TransactionalTestExecutionListener.java:515)
    at org.springframework.test.context.transaction.TransactionalTestExecutionListener.endTransaction(TransactionalTestExecutionListener.java:290)
    at org.springframework.test.context.transaction.TransactionalTestExecutionListener.afterTestMethod(TransactionalTestExecutionListener.java:183)
    at org.springframework.test.context.TestContextManager.afterTestMethod(TestContextManager.java:406)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:90)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)
    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.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:46)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
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.engine.ActionQueue.executeActions(ActionQueue.java:268)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:184)
    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:1216)
    at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:383)
    at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:133)
    at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:76)
    at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:467)
    ... 25 more
Caused by: java.sql.BatchUpdateException: ORA-00001: unique constraint (PV_UMDB.USERS_PK) violated

    at oracle.jdbc.driver.DatabaseError.throwBatchUpdateException(DatabaseError.java:343)
    at oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:10768)
    at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
    at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)
    ... 34 more

If I use rollback, then the test passes, which of course is incorrect.

Now, is there a good solution?

Thanks for your help

BB Peter


You cannot rely on EntityExistsException thrown by persist().

From the javadoc:

EntityExistsException - if the entity already exists. (If the entity already exists, the EntityExistsException may be thrown when the persist operation is invoked, or the EntityExistsException or another PersistenceException may be thrown at flush or commit time.)

In your case, you get another exception thrown at the commit time. If you replace

sessionFactory.getCurrentSession().flush();

with

em.flush();

you can catch a PersistenceException thrown at the flush time (I'm not sure why it doesn't work the same way with SessionFactory).


The test is to see if a User with an existing ID is stored, the PreexistingEntityException is thrown.

The general pattern for checking a exception is:

  • Junit4: @Test(excpect=ExcpectedException.class), or
  • JUnit3 or when the easy pattern is not working:

psydocode for JUnit3

try { 
  invokeExceptionThrowingMethod();
  fail("ExceptionX expected");
} catch(ExcpectedException e) {
  //expected - do nothing
}

I strongly belive, that if you write your test case more clear, than you will find the bug.

edited

In your case, you need the second variant, because the test must not accept an exception from the first user creation.

@Test
public void testInsertUserExistingID()         
       //(See my comment to your question about throwing Exception)
       throws Exception{
    User user = User.valueOf("1");
    user.setFirstname("DUMMY");
    user.setName("CRASH");

    try {
        userDao.create(user);
        sessionFactory.getCurrentSession().flush();
        fail("PreexistingEntityException expected");
    } catch (PreexistingEntityException e) {
        //Thats what is execpected
    }
}

Anyway: axtavt is right

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号