使用JPA2和测试重复id

时间:2010-12-16 10:57:58

标签: oracle hibernate spring ora-00001

我现在有点困惑:-S

我正在开发一个使用JPA2,Spring 3.0.5,Hibernate 3.6.0 Final的项目。我们有以下代码(仅相关类)

@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());
    }

}

现在,当我将rollback设置为false运行测试(我知道,这不是一个真正的单元测试)时,我得到以下堆栈跟踪:

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(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

如果我使用回滚,则测试通过,这当然是不正确的。

现在,有一个很好的解决方案吗?

感谢您的帮助

BB 彼得

2 个答案:

答案 0 :(得分:1)

您不能依赖EntityExistsException抛出的persist()

来自javadoc

  

EntityExistsException - 如果是实体   已经存在。 (如果实体已经   存在,EntityExistsException可能   持续操作时抛出   被调用,或者   EntityExistsException 或其他   PersistenceException可能被抛出   刷新或提交时间。

在您的情况下,您将在提交时抛出另一个异常。如果你替换

sessionFactory.getCurrentSession().flush();

em.flush();

你可以在刷新时捕获PersistenceException(我不确定为什么它与SessionFactory的工作方式不同。)

答案 1 :(得分:0)

  

测试是看用户是否有   存储现有ID,   抛出PreexistingEntityException。

检查例外的一般模式是:

  • Junit4:@Test(excpect = ExcpectedException.class)或
  • JUnit3或当简单模式不起作用时:

JUnit3的psydocode

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

我强烈相信,如果您更清楚地编写测试用例,那么您将发现错误。

<强>编辑

在您的情况下,您需要第二个变体,因为测试不能接受第一个用户创建的异常。

@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
    }
}

无论如何:axtavt是对的