Skip to content

Commit e30e416

Browse files
committed
Add test for issue
1 parent 5dcbdf6 commit e30e416

File tree

1 file changed

+344
-0
lines changed

1 file changed

+344
-0
lines changed
Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.orm.test.cache;
8+
9+
import java.util.Properties;
10+
11+
import org.hibernate.cache.CacheException;
12+
import org.hibernate.cache.cfg.spi.DomainDataRegionBuildingContext;
13+
import org.hibernate.cache.cfg.spi.DomainDataRegionConfig;
14+
import org.hibernate.cache.cfg.spi.EntityDataCachingConfig;
15+
import org.hibernate.cache.internal.DefaultCacheKeysFactory;
16+
import org.hibernate.cache.spi.CacheKeysFactory;
17+
import org.hibernate.cache.spi.DomainDataRegion;
18+
import org.hibernate.cache.spi.access.AccessType;
19+
import org.hibernate.cache.spi.access.EntityDataAccess;
20+
import org.hibernate.cache.spi.access.SoftLock;
21+
import org.hibernate.cache.spi.support.AbstractEntityDataAccess;
22+
import org.hibernate.cache.spi.support.DomainDataRegionImpl;
23+
import org.hibernate.cache.spi.support.DomainDataStorageAccess;
24+
import org.hibernate.cache.spi.support.RegionFactoryTemplate;
25+
import org.hibernate.cfg.AvailableSettings;
26+
import org.hibernate.engine.spi.SharedSessionContractImplementor;
27+
28+
import org.hibernate.testing.cache.CachingRegionFactory;
29+
import org.hibernate.testing.cache.MapStorageAccessImpl;
30+
import org.hibernate.testing.orm.junit.DomainModel;
31+
import org.hibernate.testing.orm.junit.ServiceRegistry;
32+
import org.hibernate.testing.orm.junit.SessionFactory;
33+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
34+
import org.hibernate.testing.orm.junit.Setting;
35+
import org.hibernate.testing.orm.junit.SettingProvider;
36+
import org.junit.jupiter.api.AfterEach;
37+
import org.junit.jupiter.api.BeforeEach;
38+
import org.junit.jupiter.api.Test;
39+
40+
import org.jboss.logging.Logger;
41+
42+
import jakarta.persistence.Cacheable;
43+
import jakarta.persistence.Entity;
44+
import jakarta.persistence.Id;
45+
import jakarta.persistence.Table;
46+
47+
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
48+
49+
/**
50+
* Tests of how sync-spaces for a native query affect caching
51+
*
52+
* @author Samuel Fung
53+
* @author Steve Ebersole
54+
*/
55+
@DomainModel(
56+
annotatedClasses = {
57+
BulkNativeQueryCachingOutsideTransactionTest.Customer.class
58+
}
59+
)
60+
@SessionFactory()
61+
@ServiceRegistry(
62+
settings = {
63+
@Setting(name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "true"),
64+
@Setting(name = AvailableSettings.ALLOW_UPDATE_OUTSIDE_TRANSACTION, value = "true"),
65+
},
66+
settingProviders = @SettingProvider(
67+
settingName = AvailableSettings.CACHE_REGION_FACTORY,
68+
provider = BulkNativeQueryCachingOutsideTransactionTest.CacheRegionFactoryProvider.class
69+
)
70+
)
71+
72+
public class BulkNativeQueryCachingOutsideTransactionTest {
73+
74+
private static final TestCachingRegionFactory CACHING_REGION_FACTORY = new TestCachingRegionFactory();
75+
76+
public static class CacheRegionFactoryProvider implements SettingProvider.Provider<TestCachingRegionFactory> {
77+
@Override
78+
public TestCachingRegionFactory getSetting() {
79+
return CACHING_REGION_FACTORY;
80+
}
81+
}
82+
83+
@BeforeEach
84+
public void before(SessionFactoryScope scope) {
85+
scope.inTransaction(
86+
session ->
87+
session.persist( new Customer( 1, "Samuel" ) )
88+
);
89+
CACHING_REGION_FACTORY.getTestDomainDataRegion().getTestEntityDataAccess().reset();
90+
}
91+
92+
@AfterEach
93+
public void after(SessionFactoryScope scope) {
94+
scope.inTransaction(
95+
session ->
96+
session.createQuery( "delete Customer" ).executeUpdate()
97+
);
98+
CACHING_REGION_FACTORY.getTestDomainDataRegion().getTestEntityDataAccess().reset();
99+
}
100+
101+
@Test
102+
public void testUpdateCachedEntity(SessionFactoryScope scope) {
103+
scope.inTransaction(
104+
session ->
105+
session.createNativeQuery( "update Customer set id = id" ).executeUpdate()
106+
);
107+
assertThat( isLockRegionCalled() )
108+
.as( "EntityDataAccess lockRegion method has not been called" )
109+
.isTrue();
110+
111+
assertThat( isUnlockRegionCalled() )
112+
.as( "EntityDataAccess unlockRegion method has not been called" )
113+
.isTrue();
114+
}
115+
116+
@Test
117+
public void testUpdateCachedOutsideTransaction(SessionFactoryScope scope) {
118+
scope.inSession(
119+
session ->
120+
session.createNativeQuery( "update Customer set id = id" ).executeUpdate()
121+
);
122+
123+
assertThat( isLockRegionCalled() )
124+
.as( "EntityDataAccess lockRegion method has not been called" )
125+
.isTrue();
126+
127+
assertThat( isUnlockRegionCalled() )
128+
.as( "EntityDataAccess unlockRegion method has not been called" )
129+
.isTrue();
130+
}
131+
132+
private static boolean isUnlockRegionCalled() {
133+
return CACHING_REGION_FACTORY.getTestDomainDataRegion()
134+
.getTestEntityDataAccess()
135+
.isUnlockRegionCalled();
136+
}
137+
138+
private static boolean isLockRegionCalled() {
139+
return CACHING_REGION_FACTORY.getTestDomainDataRegion()
140+
.getTestEntityDataAccess()
141+
.isLockRegionCalled();
142+
}
143+
144+
@Entity(name = "Customer")
145+
@Table(name = "Customer")
146+
@Cacheable
147+
public static class Customer {
148+
@Id
149+
private int id;
150+
151+
private String name;
152+
153+
public Customer() {
154+
}
155+
156+
public Customer(int id, String name) {
157+
this.id = id;
158+
this.name = name;
159+
}
160+
161+
public int getId() {
162+
return id;
163+
}
164+
165+
public void setId(int id) {
166+
this.id = id;
167+
}
168+
169+
public String getName() {
170+
return name;
171+
}
172+
173+
public void setName(String name) {
174+
this.name = name;
175+
}
176+
}
177+
178+
public static class TestCachingRegionFactory extends CachingRegionFactory {
179+
private static final Logger LOG = Logger.getLogger( org.hibernate.testing.cache.CachingRegionFactory.class.getName() );
180+
181+
public static final String DEFAULT_ACCESSTYPE = "DefaultAccessType";
182+
private final CacheKeysFactory cacheKeysFactory;
183+
private TestDomainDataRegion testDomainDataRegion;
184+
185+
public TestCachingRegionFactory() {
186+
this( DefaultCacheKeysFactory.INSTANCE, null );
187+
}
188+
189+
public TestCachingRegionFactory(CacheKeysFactory cacheKeysFactory) {
190+
this( cacheKeysFactory, null );
191+
}
192+
193+
public TestCachingRegionFactory(Properties properties) {
194+
this( DefaultCacheKeysFactory.INSTANCE, properties );
195+
}
196+
197+
public TestCachingRegionFactory(CacheKeysFactory cacheKeysFactory, Properties properties) {
198+
LOG.warn( "org.hibernate.testing.cache.CachingRegionFactory should be only used for testing." );
199+
this.cacheKeysFactory = cacheKeysFactory;
200+
}
201+
202+
@Override
203+
public DomainDataRegion buildDomainDataRegion(
204+
DomainDataRegionConfig regionConfig, DomainDataRegionBuildingContext buildingContext) {
205+
if ( testDomainDataRegion == null ) {
206+
testDomainDataRegion = new TestDomainDataRegion(
207+
regionConfig,
208+
this,
209+
new MapStorageAccessImpl(),
210+
cacheKeysFactory,
211+
buildingContext
212+
);
213+
}
214+
return testDomainDataRegion;
215+
}
216+
217+
@Override
218+
protected void releaseFromUse() {
219+
testDomainDataRegion = null;
220+
}
221+
222+
public TestDomainDataRegion getTestDomainDataRegion() {
223+
return testDomainDataRegion;
224+
}
225+
}
226+
227+
public static class TestDomainDataRegion extends DomainDataRegionImpl {
228+
229+
TestEntityDataAccess testEntityDataAccess;
230+
231+
public TestDomainDataRegion(
232+
DomainDataRegionConfig regionConfig,
233+
RegionFactoryTemplate regionFactory,
234+
DomainDataStorageAccess domainDataStorageAccess,
235+
CacheKeysFactory defaultKeysFactory,
236+
DomainDataRegionBuildingContext buildingContext) {
237+
super( regionConfig, regionFactory, domainDataStorageAccess, defaultKeysFactory, buildingContext );
238+
}
239+
240+
@Override
241+
public EntityDataAccess generateEntityAccess(EntityDataCachingConfig entityAccessConfig) {
242+
if ( testEntityDataAccess == null ) {
243+
testEntityDataAccess = new TestEntityDataAccess(
244+
this,
245+
getEffectiveKeysFactory(),
246+
getCacheStorageAccess()
247+
);
248+
}
249+
return testEntityDataAccess;
250+
}
251+
252+
public TestEntityDataAccess getTestEntityDataAccess() {
253+
return testEntityDataAccess;
254+
}
255+
256+
@Override
257+
public void destroy() throws CacheException {
258+
testEntityDataAccess = null;
259+
}
260+
}
261+
262+
public static class TestEntityDataAccess extends AbstractEntityDataAccess {
263+
264+
private boolean isUnlockRegionCalled = false;
265+
private boolean lockRegionCalled = false;
266+
267+
public TestEntityDataAccess(
268+
DomainDataRegion region,
269+
CacheKeysFactory cacheKeysFactory,
270+
DomainDataStorageAccess storageAccess) {
271+
super( region, cacheKeysFactory, storageAccess );
272+
}
273+
274+
@Override
275+
public boolean insert(SharedSessionContractImplementor session, Object key, Object value, Object version) {
276+
return false;
277+
}
278+
279+
@Override
280+
public boolean afterInsert(SharedSessionContractImplementor session, Object key, Object value, Object version) {
281+
return false;
282+
}
283+
284+
@Override
285+
public boolean update(
286+
SharedSessionContractImplementor session,
287+
Object key,
288+
Object value,
289+
Object currentVersion,
290+
Object previousVersion) {
291+
return false;
292+
}
293+
294+
@Override
295+
public boolean afterUpdate(
296+
SharedSessionContractImplementor session,
297+
Object key,
298+
Object value,
299+
Object currentVersion,
300+
Object previousVersion,
301+
SoftLock lock) {
302+
return false;
303+
}
304+
305+
@Override
306+
public AccessType getAccessType() {
307+
return null;
308+
}
309+
310+
@Override
311+
public SoftLock lockRegion() {
312+
lockRegionCalled = true;
313+
return super.lockRegion();
314+
}
315+
316+
@Override
317+
public void unlockRegion(SoftLock lock) {
318+
super.unlockRegion( lock );
319+
isUnlockRegionCalled = true;
320+
}
321+
322+
@Override
323+
public void destroy() {
324+
super.destroy();
325+
isUnlockRegionCalled = false;
326+
}
327+
328+
public boolean isUnlockRegionCalled() {
329+
return isUnlockRegionCalled;
330+
}
331+
332+
public boolean isLockRegionCalled() {
333+
return lockRegionCalled;
334+
}
335+
336+
public void reset() {
337+
this.isUnlockRegionCalled = false;
338+
this.lockRegionCalled = false;
339+
}
340+
341+
}
342+
343+
344+
}

0 commit comments

Comments
 (0)