Collections can be cached with uncommitted members
--------------------------------------------------
Key: HHH-2192
URL:
http://opensource.atlassian.com/projects/hibernate/browse/HHH-2192
Project: Hibernate3
Type: Bug
Components: core
Reporter: Owen Taylor
org.hibernate.TreeCache.put() does the put into the database with the current transaction
suspended. This works well for entities, because Hibernate knows authorittaively whether
the entity has previously been modified in the current transaction and will never put()
an
entity into the cache if it has already modified it in the current transaction. However,
the same
is not true for collections; there are cases where data from the current transaction can
be
stored into the treecache with a put() as the example below demonstrates:
(Using EJB3 to describe the issue but this is not EJB3 specific, I just don't know the
equivalent
raw Hibernate well)
@Entity
public class Account {
[...];
@OneToMany(mappedBy="account")
@Cache(usage=CacheConcurrencyStrategy.TRANSACTIONAL)
Set<Client> clients;
}
@Entity
public class Client {
[...];
@ManyToOne
@JoinColumn(nullable=false);
Account account;
}
// account.clients is set to a persistent collection, but not yet loaded
Account account = em.find(account_id);
Client client = new Client(account);
// The new client may be inserted into the database at this point
em.persist(client);
// First account.clients is loaded, *including* the newly created client, and
// immediately stored into the tree cache, bypassing the current transaction
// Other threads now have visibility to the collection including the item
// not yet committed to the database
//
// Then it is modified to add the new client which causes an eviction of
// the collections node from the cache *within* the current transaction, but
// that eviction will be rolled back if the transaction is rolled back and
// also isn't propagated to other cluster nodes until commit
account.clients.add(client);
// Now the transaction will be rolled back, so the new client is never committed,
// but the updated collection will still be in the treecache
em.getTransaction().setRollbackOnly()
It's fairly simple to fix the particular case above because we initialize the
collection
inside add(), so we could avoid caching the loaded values, however there are
slightly more artificial cases that are harder to fix, for example:
if (!account.clients.contains(client))
account.clients.add(client);
One general fix would be when evicting a node that we *haven't* previously
read from the cache or modified in the current transaction to bypass the current
transaction when evicting it. That condition is important, since if the tx has a
read or write lock on the node, trying to evict it with the tx suspsended will
dead-lock. (Does JBossCache have a way of querying whether the tx has
a lock on a node?)
Or maybe there is something simpler that I haven't thought of.
P.S. I think this problem is exclusive to TransactionalCache; for ReadWriteCache,
evictions remove the item from the cache without regard to transactions.
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
http://opensource.atlassian.com/projects/hibernate/secure/Administrators....
-
For more information on JIRA, see:
http://www.atlassian.com/software/jira