views:

175

answers:

1

I'm trying to test Hibernate mappings, specifically a unique constraint. My POJO is mapped as follows:

<property name="name" type="string" unique="true" not-null="true" />

What I want to do is to test that I can't persist two entities with the same name:

public class ExpertiseAreaDAOTest{
    private ExpertiseAreaDAO ead;
    static Connection c;
    private static SessionFactory sessionFactory;

    static {
        Configuration config = new Configuration().configure("resources/exp/hibernate-test.cfg.xml");
        sessionFactory = config.buildSessionFactory();
    }

    @Test(expected=ConstraintViolationException.class)
    public void testPersistTwoExpertiseAreasWithTheSameNameIsNotAllowed(){
        ExpertiseArea ea = new ExpertiseArea("Design");
        ExpertiseArea otherEA = new ExpertiseArea("Design");

        ead.setSession(getSessionFactory().getCurrentSession());
        ead.getSession().beginTransaction();
        ead.makePersistent(ea);
        ead.makePersistent(otherEA);
        ead.getSession().getTransaction().commit(); 
    }

    public static SessionFactory getSessionFactory(){
        return sessionFactory;
    }
}
//Other methods ommited

On commiting the current transaction, I can see in the logs that a ConstraintViolationException is thrown:

16:08:47,571 DEBUG SQL:111 - insert into ExpertiseArea (VERSION, name, id) values (?, ?, ?)
Hibernate: insert into ExpertiseArea (VERSION, name, id) values (?, ?, ?)
16:08:47,571 DEBUG SQL:111 - insert into ExpertiseArea (VERSION, name, id) values (?, ?, ?)
Hibernate: insert into ExpertiseArea (VERSION, name, id) values (?, ?, ?)
16:08:47,572  WARN JDBCExceptionReporter:100 - SQL Error: -104, SQLState: 23505
16:08:47,572 ERROR JDBCExceptionReporter:101 - integrity constraint violation: unique constraint or index violation; SYS_CT_10036 table: EXPERTISEAREA
16:08:47,573 ERROR AbstractFlushingEventListener:324 - Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update

So I would expect the test to pass, since the expected ConstraintViolationException is thrown. However, the test never completes (neither pass nor fails) and I have to manually kill the test runner.

What's the correct way to test this?

+2  A: 

Shouldn't you run the creates in two transactions, expecting the first one to succeed and the second to fail?

public void testPersistTwoExpertiseAreasWithTheSameNameIsNotAllowed(){
    ExpertiseArea ea = new ExpertiseArea("Design");
    ExpertiseArea otherEA = new ExpertiseArea("Design");

    SessionFactory sf = getSessionFactory();
    Session session = sf.openSession();
    Transaction txn = session.beginTransaction();
    try {
        ead.setSession(session);
        ead.makePersistent(ea);
        // no ExpertiseArea by this name in DB, this should commit
        txn.commit(); 
    } catch (HibernateException ex) {
        txn.rollback();
        fail("Unexpected exception " + ex);
    } finally {
        session.close();
        ead.setSession(null);
    }
    session = sf.openSession();
    txn = session.beginTransaction();
    try {
        ead.setSession(session);
        ead.makePersistent(otherEA);
        // this should fail 'cos there is already an ExpertiseArea with
        // the same name in DB
        txn.commit();
        fail("Expected constraint violation exception");
    } finally {
        session.close();
        ead.setSession(null);
    }
}
binil
I tried that, but get exactly the same result.
Cesar
Did you try manually creating the `Session` in the test?
binil
Yes, actually the session is created internally in the test. Or what do you mean by manually creating the Session? I edited my question to reflect that.
Cesar
What I meant was the sequence: open a session, begin a txn, save the entity, commit txn, close the session, open a new session, begin a txn, save the second (duplicate) entity, commit the txn (which should fail), and close the session. I thought you were using a single thread-local session for the duration of the test.
binil
@Cesar, I have edited the post to indicate what I mean clearly. It uses an older JUnit style though.
binil
binil, thanks a lot for your answer! The test finally passes, I took your example and modified it to make it run properly in JUnit 4. Now I know it had to do with session management, but I'm not sure why it didn't work before.
Cesar
@Cesar, Glad to be of help! In your original code, if you do a ead.getSession().flush() after the first commit, does the test run?
binil
@binil It doesn't hang, but hibernate complains for trying to flush after the commit since the session is now closed. Flushing before committing makes the test hang again.
Cesar
@Cesar, weird. In your original code, you were not closing the `Session` in the test; I am guessing that you were closing it in a `tearDown` method. Beats me why it should complain about a closed `Session`.
binil