Skip to content

Commit e1f4bf6

Browse files
committed
HHH-18085 Add test for issue
1 parent 3b00a9f commit e1f4bf6

File tree

1 file changed

+165
-0
lines changed

1 file changed

+165
-0
lines changed
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
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.jpa.query;
8+
9+
import java.util.Set;
10+
11+
import org.hibernate.annotations.Cache;
12+
import org.hibernate.annotations.CacheConcurrencyStrategy;
13+
import org.hibernate.cfg.AvailableSettings;
14+
import org.hibernate.engine.spi.SessionFactoryImplementor;
15+
import org.hibernate.stat.Statistics;
16+
17+
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
18+
import org.hibernate.testing.orm.junit.Jira;
19+
import org.hibernate.testing.orm.junit.Jpa;
20+
import org.hibernate.testing.orm.junit.Setting;
21+
import org.junit.jupiter.api.AfterAll;
22+
import org.junit.jupiter.api.BeforeAll;
23+
import org.junit.jupiter.api.Test;
24+
25+
import jakarta.persistence.Entity;
26+
import jakarta.persistence.EntityManager;
27+
import jakarta.persistence.FetchType;
28+
import jakarta.persistence.Id;
29+
import jakarta.persistence.ManyToOne;
30+
import jakarta.persistence.OneToMany;
31+
import jakarta.persistence.Table;
32+
33+
import static org.assertj.core.api.Assertions.assertThat;
34+
import static org.hibernate.jpa.HibernateHints.HINT_CACHEABLE;
35+
36+
/**
37+
* @author Marco Belladelli
38+
*/
39+
@Jpa( annotatedClasses = {
40+
CachedQueryShallowSharedCollectionTest.Account.class,
41+
CachedQueryShallowSharedCollectionTest.DomainAccount.class
42+
}, properties = {
43+
@Setting( name = AvailableSettings.USE_QUERY_CACHE, value = "true" ),
44+
@Setting( name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "true" ),
45+
@Setting( name = AvailableSettings.QUERY_CACHE_LAYOUT, value = "auto" )
46+
}, generateStatistics = true )
47+
@Jira( "https://hibernate.atlassian.net/browse/HHH-18085" )
48+
public class CachedQueryShallowSharedCollectionTest {
49+
private static final String ACCOUNT_BY_NAME = "from Account where name = :name";
50+
51+
@Test
52+
public void testQueryInSameTransaction(EntityManagerFactoryScope scope) {
53+
final Statistics stats = getStatistics( scope );
54+
stats.clear();
55+
56+
scope.inTransaction( entityManager -> {
57+
// ensure the account is in 2LC and that the query cache is populated
58+
executeQueryByName( entityManager, "test_account" );
59+
} );
60+
61+
assertThat( stats.getQueryCacheHitCount() ).isEqualTo( 0 );
62+
assertThat( stats.getQueryCacheMissCount() ).isEqualTo( 1 );
63+
assertThat( stats.getQueryCachePutCount() ).isEqualTo( 1 );
64+
65+
stats.clear();
66+
67+
scope.inTransaction( entityManager -> {
68+
// execute the query multiple times, ensure the returned account is always the same
69+
Account old = null;
70+
for ( int i = 1; i <= 2; i++ ) {
71+
final Account account = executeQueryByName( entityManager, "test_account" );
72+
assertThat( account.getDomainAccounts() ).hasSize( 2 );
73+
74+
assertThat( stats.getQueryCacheHitCount() ).isEqualTo( i );
75+
assertThat( stats.getQueryCacheMissCount() ).isEqualTo( 0 );
76+
assertThat( stats.getQueryCachePutCount() ).isEqualTo( 0 );
77+
78+
if ( old != null ) {
79+
assertThat( account ).isSameAs( old );
80+
}
81+
old = account;
82+
}
83+
} );
84+
}
85+
86+
private static Account executeQueryByName(
87+
EntityManager entityManager,
88+
@SuppressWarnings( "SameParameterValue" ) String name) {
89+
return entityManager.createQuery( ACCOUNT_BY_NAME, Account.class )
90+
.setParameter( "name", name )
91+
.setHint( HINT_CACHEABLE, true )
92+
.getSingleResult();
93+
}
94+
95+
private static Statistics getStatistics(EntityManagerFactoryScope scope) {
96+
return ( (SessionFactoryImplementor) scope.getEntityManagerFactory() ).getStatistics();
97+
}
98+
99+
@BeforeAll
100+
public void setUp(EntityManagerFactoryScope scope) {
101+
scope.inTransaction( entityManager -> {
102+
final Account account = new Account( 1L, "test_account" );
103+
entityManager.persist( account );
104+
entityManager.persist( new DomainAccount( 1L, account ) );
105+
entityManager.persist( new DomainAccount( 2L, account ) );
106+
} );
107+
}
108+
109+
@AfterAll
110+
public void tearDown(EntityManagerFactoryScope scope) {
111+
scope.inTransaction( entityManager -> {
112+
entityManager.createQuery( "delete from DomainAccount" ).executeUpdate();
113+
entityManager.createQuery( "delete from Account" ).executeUpdate();
114+
} );
115+
}
116+
117+
@Entity( name = "Account" )
118+
@Table( name = "account_table" )
119+
@Cache( usage = CacheConcurrencyStrategy.READ_WRITE )
120+
static class Account {
121+
@Id
122+
private Long id;
123+
124+
private String name;
125+
126+
@OneToMany( fetch = FetchType.LAZY, mappedBy = "account" )
127+
@Cache( usage = CacheConcurrencyStrategy.READ_WRITE )
128+
private Set<DomainAccount> domainAccounts;
129+
130+
public Account() {
131+
}
132+
133+
public Account(Long id, String name) {
134+
this.id = id;
135+
this.name = name;
136+
}
137+
138+
public Set<DomainAccount> getDomainAccounts() {
139+
return domainAccounts;
140+
}
141+
}
142+
143+
@Entity( name = "DomainAccount" )
144+
@Table( name = "domains_account_table" )
145+
@Cache( usage = CacheConcurrencyStrategy.READ_WRITE )
146+
static class DomainAccount {
147+
@Id
148+
public Long id;
149+
150+
@ManyToOne( fetch = FetchType.LAZY )
151+
public Account account;
152+
153+
public DomainAccount() {
154+
}
155+
156+
public DomainAccount(Long id, Account account) {
157+
this.id = id;
158+
this.account = account;
159+
}
160+
161+
public Account getAccount() {
162+
return account;
163+
}
164+
}
165+
}

0 commit comments

Comments
 (0)