Skip to content

Commit 7356877

Browse files
committed
Fix statement caching test
1 parent 896c69b commit 7356877

4 files changed

Lines changed: 413 additions & 43 deletions

File tree

Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
package com.vladmihalcea.hpjp.jdbc.caching;
2+
3+
import com.codahale.metrics.MetricRegistry;
4+
import com.codahale.metrics.Slf4jReporter;
5+
import com.codahale.metrics.Timer;
6+
import com.microsoft.sqlserver.jdbc.SQLServerDataSource;
7+
import com.vladmihalcea.hpjp.util.AbstractSQLServerIntegrationTest;
8+
import com.vladmihalcea.hpjp.util.providers.*;
9+
import com.vladmihalcea.hpjp.util.providers.entity.BlogEntityProvider;
10+
import jakarta.persistence.*;
11+
import org.junit.Test;
12+
13+
import javax.sql.DataSource;
14+
import java.sql.PreparedStatement;
15+
import java.sql.ResultSet;
16+
import java.sql.SQLException;
17+
import java.sql.Timestamp;
18+
import java.time.LocalDate;
19+
import java.time.LocalDateTime;
20+
import java.util.List;
21+
import java.util.concurrent.ThreadLocalRandom;
22+
import java.util.concurrent.TimeUnit;
23+
import java.util.stream.LongStream;
24+
25+
import static org.junit.Assert.*;
26+
27+
/**
28+
* @author Vlad Mihalcea
29+
*/
30+
public class SQLServerStatementCacheTest extends AbstractSQLServerIntegrationTest {
31+
32+
public static class CachingSQLServerDataSourceProvider extends SQLServerDataSourceProvider {
33+
private final int cacheSize;
34+
35+
CachingSQLServerDataSourceProvider(int cacheSize) {
36+
this.cacheSize = cacheSize;
37+
}
38+
39+
@Override
40+
public DataSource dataSource() {
41+
SQLServerDataSource dataSource = (SQLServerDataSource) super.dataSource();
42+
dataSource.setStatementPoolingCacheSize(cacheSize);
43+
if (cacheSize > 0) {
44+
dataSource.setDisableStatementPooling(false);
45+
}
46+
return dataSource;
47+
}
48+
49+
@Override
50+
public String toString() {
51+
return "CachingSQLServerDataSourceProvider{" +
52+
"cacheSize=" + cacheSize +
53+
'}';
54+
}
55+
}
56+
57+
public static final String INSERT_POST = "insert into post (title) values (?)";
58+
59+
private MetricRegistry metricRegistry = new MetricRegistry();
60+
61+
private Slf4jReporter logReporter = Slf4jReporter
62+
.forRegistry(metricRegistry)
63+
.outputTo(LOGGER)
64+
.build();
65+
66+
private Timer statementExecutionTimer = metricRegistry.timer("statementExecutionTimer");
67+
68+
private ThreadLocalRandom random = ThreadLocalRandom.current();
69+
70+
private boolean enableStatementCaching = false;
71+
72+
private int cacheSize = enableStatementCaching ? getPostCount() : 0;
73+
74+
@Override
75+
protected CachingSQLServerDataSourceProvider dataSourceProvider() {
76+
return new CachingSQLServerDataSourceProvider(cacheSize);
77+
}
78+
79+
@Override
80+
protected Class<?>[] entities() {
81+
return new Class[] {
82+
Post.class
83+
};
84+
}
85+
86+
protected int getPostCount() {
87+
return 1000;
88+
}
89+
90+
protected int getPostCommentCount() {
91+
return 5;
92+
}
93+
94+
protected long getRunNanos() {
95+
return TimeUnit.SECONDS.toNanos(60);
96+
}
97+
98+
@Override
99+
protected boolean proxyDataSource() {
100+
return false;
101+
}
102+
103+
@Test
104+
public void testInsertStatementCaching() {
105+
long ttlNanos = System.nanoTime() + getRunNanos();
106+
while (System.nanoTime() < ttlNanos) {
107+
doInJDBC(connection -> {
108+
int postCount = getPostCount();
109+
for (int i = 0; i < postCount; i++) {
110+
if (System.nanoTime() > ttlNanos) {
111+
return;
112+
}
113+
long startNanos = System.nanoTime();
114+
if (i % 2 == 0) {
115+
try (
116+
PreparedStatement postStatement = connection.prepareStatement("""
117+
INSERT INTO Posts (
118+
Title,
119+
CreatedOn,
120+
UpdatedOn,
121+
Category,
122+
Tags,
123+
Score
124+
)
125+
VALUES (?, ?, ?, ?, ?, ?)
126+
""")) {
127+
int index;
128+
index = 0;
129+
postStatement.setString(++index, String.format("Post no. %1$d", i));
130+
postStatement.setTimestamp(++index, Timestamp.valueOf(LocalDateTime.now()));
131+
postStatement.setTimestamp(++index, Timestamp.valueOf(LocalDateTime.now()));
132+
postStatement.setString(++index, String.format("Category no. %1$d", i));
133+
postStatement.setString(++index, String.format("Tags no. %1$d", i));
134+
postStatement.setInt(++index, 0);
135+
postStatement.executeUpdate();
136+
statementExecutionTimer.update(System.nanoTime() - startNanos, TimeUnit.NANOSECONDS);
137+
} catch (SQLException e) {
138+
fail(e.getMessage());
139+
}
140+
} else {
141+
try (
142+
PreparedStatement postStatement = connection.prepareStatement("""
143+
INSERT INTO Posts (
144+
Title,
145+
CreatedOn,
146+
Category,
147+
Tags
148+
)
149+
VALUES (?, ?, ?, ?)
150+
""")) {
151+
int index;
152+
index = 0;
153+
postStatement.setString(++index, String.format("Post no. %1$d", i));
154+
postStatement.setTimestamp(++index, Timestamp.valueOf(LocalDateTime.now()));
155+
postStatement.setString(++index, String.format("Category no. %1$d", i));
156+
postStatement.setString(++index, String.format("Tags no. %1$d", i));
157+
postStatement.executeUpdate();
158+
statementExecutionTimer.update(System.nanoTime() - startNanos, TimeUnit.NANOSECONDS);
159+
} catch (SQLException e) {
160+
fail(e.getMessage());
161+
}
162+
}
163+
}
164+
connection.commit();
165+
});
166+
}
167+
LOGGER.info("Running with the statement cache [{}] an a cache size of [{}]", enableStatementCaching, cacheSize);
168+
logReporter.report();
169+
}
170+
171+
@Test
172+
public void testStatementCaching() {
173+
doInJDBC(connection -> {
174+
try (
175+
PreparedStatement postStatement = connection.prepareStatement(INSERT_POST)) {
176+
int postCount = getPostCount();
177+
178+
int index;
179+
180+
for (int i = 0; i < postCount; i++) {
181+
index = 0;
182+
postStatement.setString(++index, String.format("Post no. %1$d", i));
183+
postStatement.setInt(++index, 0);
184+
postStatement.setLong(++index, i);
185+
postStatement.executeUpdate();
186+
}
187+
} catch (SQLException e) {
188+
fail(e.getMessage());
189+
}
190+
});
191+
192+
long ttlNanos = System.nanoTime() + getRunNanos();
193+
while (System.nanoTime() < ttlNanos) {
194+
doInJDBC(connection -> {
195+
for (int i = 0; i < 50; i++) {
196+
if(System.nanoTime() > ttlNanos) {
197+
return;
198+
}
199+
List<Long> ids = LongStream.rangeClosed(1, random.nextLong(2, 10)).boxed().toList();
200+
StringBuilder inClauseBuilder = new StringBuilder();
201+
ids.forEach(aLong -> {
202+
inClauseBuilder.append(inClauseBuilder.isEmpty() ? "(" : ",");
203+
inClauseBuilder.append("?");
204+
});
205+
inClauseBuilder.append(")");
206+
long startNanos = System.nanoTime();
207+
try (PreparedStatement statement = connection.prepareStatement(String.format("""
208+
select p.title
209+
from post p
210+
where p.id in %s
211+
""", inClauseBuilder))) {
212+
for (int j = 0; j < ids.size(); j++) {
213+
statement.setLong(j + 1, ids.get(j));
214+
}
215+
try (ResultSet resultSet = statement.executeQuery()) {
216+
assertTrue(resultSet.next());
217+
}
218+
statementExecutionTimer.update(System.nanoTime() - startNanos, TimeUnit.NANOSECONDS);
219+
} catch (SQLException e) {
220+
fail(e.getMessage());
221+
}
222+
}
223+
});
224+
}
225+
LOGGER.info("Running with the statement cache [{}] an a cache size of [{}]", enableStatementCaching, cacheSize);
226+
logReporter.report();
227+
}
228+
229+
@Test
230+
public void testStatementCachingWithInQueries() {
231+
doInJDBC(connection -> {
232+
try (
233+
PreparedStatement postStatement = connection.prepareStatement(INSERT_POST)) {
234+
int postCount = getPostCount();
235+
236+
int index;
237+
238+
for (int i = 0; i < postCount; i++) {
239+
index = 0;
240+
postStatement.setString(++index, String.format("Post no. %1$d", i));
241+
postStatement.setInt(++index, 0);
242+
postStatement.setLong(++index, i);
243+
postStatement.executeUpdate();
244+
}
245+
} catch (SQLException e) {
246+
fail(e.getMessage());
247+
}
248+
});
249+
250+
doInJPA(entityManager -> {
251+
//Warm-up
252+
for (int i = 0; i < getPostCount(); i++) {
253+
executeInQuery(entityManager);
254+
}
255+
long ttlNanos = System.nanoTime() + getRunNanos();
256+
while (System.nanoTime() < ttlNanos) {
257+
long startNanos = System.nanoTime();
258+
executeInQuery(entityManager);
259+
statementExecutionTimer.update(System.nanoTime() - startNanos, TimeUnit.NANOSECONDS);
260+
}
261+
});
262+
LOGGER.info("Running with the statement cache [{}] an a cache size of [{}]", enableStatementCaching, cacheSize);
263+
logReporter.report();
264+
}
265+
266+
private List<String> executeInQuery(EntityManager entityManager) {
267+
List<Long> ids = LongStream.rangeClosed(1, random.nextLong(2, getPostCount())).boxed().toList();
268+
List<String> titles = entityManager.createQuery("""
269+
select p.title
270+
from Post p
271+
where p.id in (:ids)
272+
""")
273+
.setParameter("ids", ids)
274+
.getResultList();
275+
assertEquals(titles.size(), ids.size());
276+
return titles;
277+
}
278+
279+
@Entity(name = "Post")
280+
@Table(name = "Posts")
281+
public static class Post {
282+
283+
@Id
284+
@GeneratedValue(strategy = GenerationType.IDENTITY)
285+
@Column(name = "PostId")
286+
private Long id;
287+
288+
@Column(name = "Title")
289+
private String title;
290+
291+
@Column(name = "CreatedOn")
292+
private LocalDateTime createdOn;
293+
294+
@Column(name = "UpdatedOn")
295+
private LocalDateTime updatedOn;
296+
297+
@Column(name = "Category")
298+
private String category;
299+
300+
@Column(name = "Tags")
301+
private String tags;
302+
303+
@Column(name = "Score")
304+
private int score;
305+
306+
public Long getId() {
307+
return id;
308+
}
309+
310+
public void setId(Long id) {
311+
this.id = id;
312+
}
313+
314+
public String getTitle() {
315+
return title;
316+
}
317+
318+
public void setTitle(String title) {
319+
this.title = title;
320+
}
321+
}
322+
}

core/src/test/java/com/vladmihalcea/hpjp/jdbc/caching/StatementCachePoolableTest.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,21 +116,21 @@ public StatementCachePoolableTest(DataSourceProvider dataSourceProvider) {
116116
@Parameterized.Parameters
117117
public static Collection<DataSourceProvider[]> rdbmsDataSourceProvider() {
118118
List<DataSourceProvider[]> providers = new ArrayList<>();
119-
providers.add(new DataSourceProvider[]{
119+
/*providers.add(new DataSourceProvider[]{
120120
new CachingOracleDataSourceProvider(1)
121-
});
121+
});*/
122122
providers.add(new DataSourceProvider[]{
123123
new CachingSQLServerDataSourceProvider(1)
124124
});
125-
providers.add(new DataSourceProvider[]{
125+
/*providers.add(new DataSourceProvider[]{
126126
new CachingPostgreSQLDataSourceProvider(1)
127-
});
128-
MySQLDataSourceProvider mySQLCachingDataSourceProvider = new MySQLDataSourceProvider();
127+
});*/
128+
/*MySQLDataSourceProvider mySQLCachingDataSourceProvider = new MySQLDataSourceProvider();
129129
mySQLCachingDataSourceProvider.setUseServerPrepStmts(true);
130130
mySQLCachingDataSourceProvider.setCachePrepStmts(true);
131131
providers.add(new DataSourceProvider[]{
132132
mySQLCachingDataSourceProvider
133-
});
133+
});*/
134134
return providers;
135135
}
136136

0 commit comments

Comments
 (0)