Q:

Hibernate getOrCreate pattern for multi threaded application

I've searched for a while, but haven't found an example to what I'm trying to do.

We have an API that will be under heavy use.  One of the operations is to create a new Client domain object.  The name of each Client is unique.
In the following code, we do a read for the Client by name.  If it doesn't exist, we try to create it.  As 2 threads could potentially be trying to create the same Client at the same time, we catch a ConstraintException and then do another lookup, incase another thread got in before us:
@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = false, isolation = Isolation.READ_COMMITTED)
public Client getOrCreate(String name) {
    DetachedCriteria query = DetachedCriteria.forClass(Client.class).add(Restrictions.eq("name", name));

    Client client = entityDao.findSingle(Client.class, query);

    if (client == null) {
        client = new Client();
        client.setName(name);
        try {
            entityDao.save(client);
        } catch (ConstraintViolationException e) {
            client = entityService.findSingle(Client.class, query);
        }
    }
    return client;
}

Hibernate complains with this code, since in the exception we're trying to use a session that was involved in an exception:
org.hibernate.AssertionFailure: null id in com.mydomain.Client entry (don't flush the Session after an exception occurs)

Is there a standard pattern or approach to accomplish what I'm trying to do in Hibernate?

A:

You just need to move the catch and retry out of the transaction, in order to use a new transaction (and session):
Client client;
try {
    client = clientService.getOrCreate(name);
}
catch (ConstraintViolationException e) {
    client = clientService.getOrCreate(name);
}

and
@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = false, isolation = Isolation.READ_COMMITTED)
public Client getOrCreate(String name) {
    DetachedCriteria query =
        DetachedCriteria.forClass(Client.class).add(Restrictions.eq("name", name));

    Client client = entityDao.findSingle(Client.class, query);

    if (client == null) {
        client = new Client();
        client.setName(name);
        entityDao.save(client);
    }
    return client;
}

