Skip to content

Commit 6dbb3a8

Browse files
janariosebersole
authored andcommitted
HHH-10264 - Values weren't cached after persist
HHH-9140 - Allow to look for id outside of context ERROR CollectionCacheInvalidator:145 - org.hibernate.TransientObjectException: The instance was not associated with this session at org.hibernate.internal.SessionImpl.getIdentifier(SessionImpl.java:1511) (cherry picked from commit f0d8fcd) Added property to propagate error in test case Test case without mappedBy org.hibernate.HibernateException: Unable to resolve property: at org.hibernate.tuple.entity.EntityMetamodel.getPropertyIndex(EntityMetamodel.java:926) (cherry picked from commit bf2eb01)
1 parent 92a830a commit 6dbb3a8

File tree

3 files changed

+350
-14
lines changed

3 files changed

+350
-14
lines changed

hibernate-core/src/main/java/org/hibernate/cache/internal/CollectionCacheInvalidator.java

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,14 @@
99
import java.io.Serializable;
1010
import java.util.Set;
1111

12+
import org.hibernate.HibernateException;
13+
import org.hibernate.action.internal.CollectionAction;
14+
import org.hibernate.action.spi.AfterTransactionCompletionProcess;
1215
import org.hibernate.boot.Metadata;
13-
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
16+
import org.hibernate.cache.spi.access.SoftLock;
17+
import org.hibernate.collection.spi.PersistentCollection;
1418
import org.hibernate.engine.spi.SessionFactoryImplementor;
19+
import org.hibernate.engine.spi.SessionImplementor;
1520
import org.hibernate.event.service.spi.EventListenerRegistry;
1621
import org.hibernate.event.spi.EventSource;
1722
import org.hibernate.event.spi.EventType;
@@ -40,6 +45,8 @@
4045
public class CollectionCacheInvalidator
4146
implements Integrator, PostInsertEventListener, PostDeleteEventListener, PostUpdateEventListener {
4247
private static final Logger LOG = Logger.getLogger( CollectionCacheInvalidator.class.getName() );
48+
public static final String PROPAGATE_EXCEPTION = "hibernate.test.auto_evict_collection_cache.propagate_exception";
49+
private boolean propagateException;
4350

4451
@Override
4552
public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactory,
@@ -80,6 +87,8 @@ private void integrate(SessionFactoryServiceRegistry serviceRegistry, SessionFac
8087
// Nothing to do, if caching is disabled
8188
return;
8289
}
90+
propagateException = Boolean.parseBoolean(
91+
sessionFactory.getProperties().getProperty( PROPAGATE_EXCEPTION ) );
8392
EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class );
8493
eventListenerRegistry.appendListeners( EventType.POST_INSERT, this );
8594
eventListenerRegistry.appendListeners( EventType.POST_DELETE, this );
@@ -95,14 +104,14 @@ private void evictCache(Object entity, EntityPersister persister, EventSource se
95104
return;
96105
}
97106
for ( String role : collectionRoles ) {
98-
CollectionPersister collectionPersister = factory.getCollectionPersister( role );
107+
final CollectionPersister collectionPersister = factory.getCollectionPersister( role );
99108
if ( !collectionPersister.hasCache() ) {
100109
// ignore collection if no caching is used
101110
continue;
102111
}
103112
// this is the property this OneToMany relation is mapped by
104113
String mappedBy = collectionPersister.getMappedByProperty();
105-
if ( mappedBy != null ) {
114+
if ( mappedBy != null && !mappedBy.isEmpty() ) {
106115
int i = persister.getEntityMetamodel().getPropertyIndex( mappedBy );
107116
Serializable oldId = null;
108117
if ( oldState != null ) {
@@ -113,7 +122,11 @@ private void evictCache(Object entity, EntityPersister persister, EventSource se
113122
Object ref = persister.getPropertyValue( entity, i );
114123
Serializable id = null;
115124
if ( ref != null ) {
116-
id = session.getIdentifier( ref );
125+
id = session.getContextEntityIdentifier( ref );
126+
if ( id == null ) {
127+
id = session.getSessionFactory().getClassMetadata( ref.getClass() )
128+
.getIdentifier( ref, session );
129+
}
117130
}
118131
// only evict if the related entity has changed
119132
if ( id != null && !id.equals( oldId ) ) {
@@ -125,11 +138,20 @@ private void evictCache(Object entity, EntityPersister persister, EventSource se
125138
}
126139
else {
127140
LOG.debug( "Evict CollectionRegion " + role );
128-
collectionPersister.getCacheAccessStrategy().evictAll();
141+
final SoftLock softLock = collectionPersister.getCacheAccessStrategy().lockRegion();
142+
session.getActionQueue().registerProcess( new AfterTransactionCompletionProcess() {
143+
@Override
144+
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) {
145+
collectionPersister.getCacheAccessStrategy().unlockRegion( softLock );
146+
}
147+
} );
129148
}
130149
}
131150
}
132151
catch ( Exception e ) {
152+
if ( propagateException ) {
153+
throw new IllegalStateException( e );
154+
}
133155
// don't let decaching influence other logic
134156
LOG.error( "", e );
135157
}
@@ -139,13 +161,32 @@ private void evict(Serializable id, CollectionPersister collectionPersister, Eve
139161
if ( LOG.isDebugEnabled() ) {
140162
LOG.debug( "Evict CollectionRegion " + collectionPersister.getRole() + " for id " + id );
141163
}
142-
CollectionRegionAccessStrategy cache = collectionPersister.getCacheAccessStrategy();
143-
Object key = cache.generateCacheKey(
144-
id,
164+
AfterTransactionCompletionProcess afterTransactionProcess = new CollectionEvictCacheAction(
145165
collectionPersister,
146-
session.getFactory(),
147-
session.getTenantIdentifier()
148-
);
149-
cache.evict( key );
166+
null,
167+
id,
168+
session
169+
).lockCache();
170+
session.getActionQueue().registerProcess( afterTransactionProcess );
171+
}
172+
173+
//execute the same process as invalidation with collection operations
174+
private static final class CollectionEvictCacheAction extends CollectionAction {
175+
protected CollectionEvictCacheAction(
176+
CollectionPersister persister,
177+
PersistentCollection collection,
178+
Serializable key,
179+
SessionImplementor session) {
180+
super( persister, collection, key, session );
181+
}
182+
183+
@Override
184+
public void execute() throws HibernateException {
185+
}
186+
187+
public AfterTransactionCompletionProcess lockCache() {
188+
beforeExecutions();
189+
return getAfterTransactionCompletionProcess();
190+
}
150191
}
151192
}

hibernate-core/src/test/java/org/hibernate/test/cache/CollectionCacheEvictionTest.java

Lines changed: 117 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,20 @@
88

99
import org.hibernate.ObjectNotFoundException;
1010
import org.hibernate.Session;
11+
import org.hibernate.cache.internal.CollectionCacheInvalidator;
12+
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
1113
import org.hibernate.cfg.Configuration;
1214
import org.hibernate.cfg.Environment;
13-
14-
import org.junit.Test;
15+
import org.hibernate.engine.spi.SessionImplementor;
16+
import org.hibernate.persister.collection.CollectionPersister;
1517

1618
import org.hibernate.testing.TestForIssue;
1719
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
20+
import org.junit.Test;
1821

1922
import static org.junit.Assert.assertEquals;
23+
import static org.junit.Assert.assertNotNull;
24+
import static org.junit.Assert.assertNull;
2025
import static org.junit.Assert.fail;
2126

2227
/**
@@ -36,6 +41,7 @@ protected void configure(Configuration cfg) {
3641
cfg.setProperty( Environment.USE_SECOND_LEVEL_CACHE, "true" );
3742
cfg.setProperty( Environment.USE_QUERY_CACHE, "true" );
3843
cfg.setProperty( Environment.CACHE_PROVIDER_CONFIG, "true" );
44+
cfg.setProperty( CollectionCacheInvalidator.PROPAGATE_EXCEPTION, "true" );
3945
}
4046

4147
@Override
@@ -68,6 +74,31 @@ protected void cleanupTest() throws Exception {
6874
s.close();
6975
}
7076

77+
@Test
78+
public void testCachedValueAfterEviction() {
79+
CollectionPersister persister = sessionFactory().getCollectionPersister( Company.class.getName() + ".users" );
80+
81+
Session session = openSession();
82+
SessionImplementor sessionImplementor = (SessionImplementor) session;
83+
84+
CollectionRegionAccessStrategy cache = persister.getCacheAccessStrategy();
85+
Object key = cache.generateCacheKey( 1, persister, sessionFactory(), session.getTenantIdentifier() );
86+
Object cachedValue = cache.get( sessionImplementor, key, sessionImplementor.getTimestamp() );
87+
assertNull( cachedValue );
88+
89+
Company company = session.get( Company.class, 1 );
90+
//should add in cache
91+
assertEquals( 1, company.getUsers().size() );
92+
session.close();
93+
94+
session = openSession();
95+
sessionImplementor = (SessionImplementor) session;
96+
key = cache.generateCacheKey( 1, persister, sessionFactory(), session.getTenantIdentifier() );
97+
cachedValue = cache.get( sessionImplementor, key, sessionImplementor.getTimestamp() );
98+
assertNotNull( "Collection wasn't cached", cachedValue );
99+
session.close();
100+
}
101+
71102
@Test
72103
public void testCollectionCacheEvictionInsert() {
73104
Session s = openSession();
@@ -93,6 +124,29 @@ public void testCollectionCacheEvictionInsert() {
93124
s.close();
94125
}
95126

127+
@Test
128+
public void testCollectionCacheEvictionInsertWithEntityOutOfContext() {
129+
Session s = openSession();
130+
Company company = s.get( Company.class, 1 );
131+
assertEquals( 1, company.getUsers().size() );
132+
s.close();
133+
134+
s = openSession();
135+
s.beginTransaction();
136+
137+
User user = new User( 2, company );
138+
s.save( user );
139+
140+
s.getTransaction().commit();
141+
s.close();
142+
143+
s = openSession();
144+
145+
company = s.get( Company.class, 1 );
146+
assertEquals( 2, company.getUsers().size() );
147+
s.close();
148+
}
149+
96150
@Test
97151
public void testCollectionCacheEvictionRemove() {
98152
Session s = openSession();
@@ -121,6 +175,32 @@ public void testCollectionCacheEvictionRemove() {
121175
s.close();
122176
}
123177

178+
@Test
179+
public void testCollectionCacheEvictionRemoveWithEntityOutOfContext() {
180+
Session s = openSession();
181+
Company company = s.get( Company.class, 1 );
182+
assertEquals( 1, company.getUsers().size() );
183+
s.close();
184+
185+
s = openSession();
186+
s.beginTransaction();
187+
s.delete( company.getUsers().get( 0 ) );
188+
189+
s.getTransaction().commit();
190+
s.close();
191+
192+
s = openSession();
193+
194+
company = s.get( Company.class, 1 );
195+
try {
196+
assertEquals( 0, company.getUsers().size() );
197+
}
198+
catch ( ObjectNotFoundException e ) {
199+
fail( "Cached element not found" );
200+
}
201+
s.close();
202+
}
203+
124204
@Test
125205
public void testCollectionCacheEvictionUpdate() {
126206
Session s = openSession();
@@ -157,4 +237,39 @@ public void testCollectionCacheEvictionUpdate() {
157237

158238
s.close();
159239
}
240+
241+
@Test
242+
public void testCollectionCacheEvictionUpdateWithEntityOutOfContext() {
243+
Session s = openSession();
244+
Company company1 = s.get( Company.class, 1 );
245+
Company company2 = s.get( Company.class, 2 );
246+
247+
assertEquals( 1, company1.getUsers().size() );
248+
assertEquals( 0, company2.getUsers().size() );
249+
250+
s.close();
251+
s = openSession();
252+
s.beginTransaction();
253+
254+
User user = s.get( User.class, 1 );
255+
user.setCompany( company2 );
256+
257+
s.getTransaction().commit();
258+
s.close();
259+
260+
s = openSession();
261+
262+
company1 = s.get( Company.class, 1 );
263+
company2 = s.get( Company.class, 2 );
264+
265+
assertEquals( 1, company2.getUsers().size() );
266+
267+
try {
268+
assertEquals( 0, company1.getUsers().size() );
269+
}
270+
catch ( ObjectNotFoundException e ) {
271+
fail( "Cached element not found" );
272+
}
273+
s.close();
274+
}
160275
}

0 commit comments

Comments
 (0)