Skip to content

Commit d22b1c6

Browse files
committed
optimize when BaseExecutor#createCacheKey get parameter property value create same MeteObject multiple times
1 parent 7edcfa4 commit d22b1c6

File tree

3 files changed

+267
-3
lines changed

3 files changed

+267
-3
lines changed

src/main/java/org/apache/ibatis/executor/BaseExecutor.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBo
207207
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
208208
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
209209
// mimic DefaultParameterHandler logic
210+
MetaObject metaObject = null;
210211
for (ParameterMapping parameterMapping : parameterMappings) {
211212
if (parameterMapping.getMode() != ParameterMode.OUT) {
212213
Object value;
@@ -218,7 +219,9 @@ public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBo
218219
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
219220
value = parameterObject;
220221
} else {
221-
MetaObject metaObject = configuration.newMetaObject(parameterObject);
222+
if (metaObject == null) {
223+
metaObject = configuration.newMetaObject(parameterObject);
224+
}
222225
value = metaObject.getValue(propertyName);
223226
}
224227
cacheKey.update(value);

src/test/java/org/apache/ibatis/executor/BaseExecutorTest.java

+130
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import static org.junit.jupiter.api.Assertions.assertNull;
2121
import static org.junit.jupiter.api.Assertions.assertTrue;
2222

23+
import java.util.ArrayList;
2324
import java.util.HashMap;
2425
import java.util.List;
2526
import java.util.Map;
@@ -29,15 +30,22 @@
2930
import javax.sql.DataSource;
3031

3132
import org.apache.ibatis.BaseDataTest;
33+
import org.apache.ibatis.builder.StaticSqlSource;
34+
import org.apache.ibatis.cache.CacheKey;
3235
import org.apache.ibatis.domain.blog.Author;
3336
import org.apache.ibatis.domain.blog.Blog;
3437
import org.apache.ibatis.domain.blog.Post;
3538
import org.apache.ibatis.domain.blog.Section;
39+
import org.apache.ibatis.mapping.BoundSql;
3640
import org.apache.ibatis.mapping.MappedStatement;
41+
import org.apache.ibatis.mapping.ParameterMapping;
42+
import org.apache.ibatis.mapping.SqlCommandType;
3743
import org.apache.ibatis.session.Configuration;
3844
import org.apache.ibatis.session.RowBounds;
3945
import org.apache.ibatis.transaction.Transaction;
4046
import org.apache.ibatis.transaction.jdbc.JdbcTransaction;
47+
import org.apache.ibatis.type.JdbcType;
48+
import org.apache.ibatis.type.TypeHandlerRegistry;
4149
import org.junit.jupiter.api.BeforeAll;
4250
import org.junit.jupiter.api.Test;
4351

@@ -470,6 +478,128 @@ void shouldClearDeferredLoads() throws Exception {
470478
}
471479
}
472480

481+
@Test
482+
void testCreateCacheKeyWithAdditionalParameter() {
483+
TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
484+
485+
MappedStatement mappedStatement = new MappedStatement.Builder(config, "testSelect", new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).build();
486+
487+
Object parameterObject = 1;
488+
489+
BoundSql boundSql = new BoundSql(config, "some select statement", new ArrayList<ParameterMapping>() {
490+
{
491+
add(new ParameterMapping.Builder(config, "id", registry.getTypeHandler(int.class)).build());
492+
}
493+
}, parameterObject) {
494+
{
495+
setAdditionalParameter("id", 2);
496+
}
497+
};
498+
499+
Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
500+
CacheKey cacheKey = executor.createCacheKey(mappedStatement, parameterObject, RowBounds.DEFAULT, boundSql);
501+
502+
CacheKey expected = new CacheKey();
503+
expected.update(mappedStatement.getId());
504+
expected.update(RowBounds.DEFAULT.getOffset());
505+
expected.update(RowBounds.DEFAULT.getLimit());
506+
expected.update(boundSql.getSql());
507+
expected.update(2);
508+
509+
assertEquals(expected, cacheKey);
510+
}
511+
512+
@Test
513+
void testCreateCacheKeyWithNull() {
514+
TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
515+
516+
MappedStatement mappedStatement = new MappedStatement.Builder(config, "testSelect", new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).build();
517+
518+
Object parameterObject = null;
519+
520+
BoundSql boundSql = new BoundSql(config, "some select statement", new ArrayList<ParameterMapping>() {
521+
{
522+
add(new ParameterMapping.Builder(config, "id", registry.getTypeHandler(int.class)).build());
523+
}
524+
}, parameterObject);
525+
526+
Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
527+
CacheKey cacheKey = executor.createCacheKey(mappedStatement, parameterObject, RowBounds.DEFAULT, boundSql);
528+
529+
CacheKey expected = new CacheKey();
530+
expected.update(mappedStatement.getId());
531+
expected.update(RowBounds.DEFAULT.getOffset());
532+
expected.update(RowBounds.DEFAULT.getLimit());
533+
expected.update(boundSql.getSql());
534+
expected.update(null);
535+
536+
assertEquals(expected, cacheKey);
537+
}
538+
539+
@Test
540+
void testCreateCacheKeyWithTypeHandler() {
541+
TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
542+
543+
MappedStatement mappedStatement = new MappedStatement.Builder(config, "testSelect", new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).build();
544+
545+
Object parameterObject = 1;
546+
547+
BoundSql boundSql = new BoundSql(config, "some select statement", new ArrayList<ParameterMapping>() {
548+
{
549+
add(new ParameterMapping.Builder(config, "id", registry.getTypeHandler(int.class)).build());
550+
}
551+
}, parameterObject);
552+
553+
Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
554+
CacheKey cacheKey = executor.createCacheKey(mappedStatement, parameterObject, RowBounds.DEFAULT, boundSql);
555+
556+
CacheKey expected = new CacheKey();
557+
expected.update(mappedStatement.getId());
558+
expected.update(RowBounds.DEFAULT.getOffset());
559+
expected.update(RowBounds.DEFAULT.getLimit());
560+
expected.update(boundSql.getSql());
561+
expected.update(1);
562+
563+
assertEquals(expected, cacheKey);
564+
}
565+
566+
@Test
567+
void testCreateCacheKeyWithMetaObject() {
568+
TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
569+
570+
MappedStatement mappedStatement = new MappedStatement.Builder(config, "testSelect", new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).build();
571+
572+
Author parameterObject = new Author(-1, "cbegin", "******", "[email protected]", "N/A", Section.NEWS);
573+
574+
BoundSql boundSql = new BoundSql(config, "some select statement", new ArrayList<ParameterMapping>() {
575+
{
576+
add(new ParameterMapping.Builder(config, "id", registry.getTypeHandler(int.class)).build());
577+
add(new ParameterMapping.Builder(config, "username", registry.getTypeHandler(String.class)).build());
578+
add(new ParameterMapping.Builder(config, "password", registry.getTypeHandler(String.class)).build());
579+
add(new ParameterMapping.Builder(config, "email", registry.getTypeHandler(String.class)).build());
580+
add(new ParameterMapping.Builder(config, "bio", registry.getTypeHandler(String.class)).jdbcType(JdbcType.VARCHAR).build());
581+
add(new ParameterMapping.Builder(config, "favouriteSection", registry.getTypeHandler(Section.class)).jdbcType(JdbcType.VARCHAR).build());
582+
}
583+
}, parameterObject);
584+
585+
Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
586+
CacheKey cacheKey = executor.createCacheKey(mappedStatement, parameterObject, RowBounds.DEFAULT, boundSql);
587+
588+
CacheKey expected = new CacheKey();
589+
expected.update(mappedStatement.getId());
590+
expected.update(RowBounds.DEFAULT.getOffset());
591+
expected.update(RowBounds.DEFAULT.getLimit());
592+
expected.update(boundSql.getSql());
593+
expected.update(parameterObject.getId());
594+
expected.update(parameterObject.getUsername());
595+
expected.update(parameterObject.getPassword());
596+
expected.update(parameterObject.getEmail());
597+
expected.update(parameterObject.getBio());
598+
expected.update(parameterObject.getFavouriteSection());
599+
600+
assertEquals(expected, cacheKey);
601+
}
602+
473603
protected Executor createExecutor(Transaction transaction) {
474604
return new SimpleExecutor(config, transaction);
475605
}

src/test/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandlerTest.java

+133-2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@
3939
import org.apache.ibatis.mapping.ResultMap;
4040
import org.apache.ibatis.mapping.ResultMapping;
4141
import org.apache.ibatis.mapping.SqlCommandType;
42+
import org.apache.ibatis.reflection.DefaultReflectorFactory;
43+
import org.apache.ibatis.reflection.MetaObject;
44+
import org.apache.ibatis.reflection.ReflectorFactory;
45+
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
46+
import org.apache.ibatis.reflection.factory.ObjectFactory;
47+
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
48+
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
49+
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
4250
import org.apache.ibatis.session.Configuration;
4351
import org.apache.ibatis.type.JdbcType;
4452
import org.apache.ibatis.type.TypeException;
@@ -103,16 +111,93 @@ MappedStatement getMappedStatement() {
103111
}
104112

105113
@Test
106-
void testParameterObjectMetaObjectGetValue() {
114+
void testParameterObjectGetPropertyValueWithAdditionalParameter() throws SQLException {
107115
Configuration config = new Configuration();
108116
TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
109117

110118
MappedStatement mappedStatement = new MappedStatement.Builder(config, "testSelect", new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).build();
111119

112-
Author parameterObject = mock(Author.class);
120+
Object parameterObject = 1;
121+
122+
BoundSql boundSql = new BoundSql(config, "some select statement", new ArrayList<ParameterMapping>() {
123+
{
124+
add(new ParameterMapping.Builder(config, "id", registry.getTypeHandler(int.class)).build());
125+
}
126+
}, parameterObject) {
127+
{
128+
setAdditionalParameter("id", 2);
129+
}
130+
};
131+
132+
DefaultParameterHandler defaultParameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
133+
134+
PreparedStatement ps = mock(PreparedStatement.class);
135+
136+
defaultParameterHandler.setParameters(ps);
137+
138+
verify(ps, times(1)).setInt(1, 2);
139+
}
140+
141+
@Test
142+
void testParameterObjectGetPropertyValueWithNull() throws SQLException {
143+
Configuration config = new Configuration();
144+
TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
145+
146+
MappedStatement mappedStatement = new MappedStatement.Builder(config, "testSelect", new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).build();
147+
148+
Object parameterObject = null;
149+
150+
BoundSql boundSql = new BoundSql(config, "some select statement", new ArrayList<ParameterMapping>() {
151+
{
152+
add(new ParameterMapping.Builder(config, "id", registry.getTypeHandler(int.class)).build());
153+
}
154+
}, parameterObject);
155+
156+
DefaultParameterHandler defaultParameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
157+
158+
PreparedStatement ps = mock(PreparedStatement.class);
159+
160+
defaultParameterHandler.setParameters(ps);
161+
162+
verify(ps, times(1)).setNull(1, config.getJdbcTypeForNull().TYPE_CODE);
163+
}
164+
165+
@Test
166+
void testParameterObjectGetPropertyValueWithTypeHandler() throws SQLException {
167+
Configuration config = new Configuration();
168+
TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
169+
170+
MappedStatement mappedStatement = new MappedStatement.Builder(config, "testSelect", new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).build();
171+
172+
Object parameterObject = 1;
113173

114174
BoundSql boundSql = new BoundSql(config, "some select statement", new ArrayList<ParameterMapping>() {
115175
{
176+
add(new ParameterMapping.Builder(config, "id", registry.getTypeHandler(int.class)).build());
177+
}
178+
}, parameterObject);
179+
180+
DefaultParameterHandler defaultParameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
181+
182+
PreparedStatement ps = mock(PreparedStatement.class);
183+
184+
defaultParameterHandler.setParameters(ps);
185+
186+
verify(ps, times(1)).setInt(1, (Integer) parameterObject);
187+
}
188+
189+
@Test
190+
void testParameterObjectGetPropertyValueWithMetaObject() throws SQLException {
191+
Configuration config = new Configuration();
192+
TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
193+
194+
MappedStatement mappedStatement = new MappedStatement.Builder(config, "testSelect", new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).build();
195+
196+
Author parameterObject = new Author(-1, "cbegin", "******", "[email protected]", "N/A", Section.NEWS);
197+
198+
BoundSql boundSql = new BoundSql(config, "some select statement", new ArrayList<ParameterMapping>() {
199+
{
200+
add(new ParameterMapping.Builder(config, "id", registry.getTypeHandler(int.class)).build());
116201
add(new ParameterMapping.Builder(config, "username", registry.getTypeHandler(String.class)).build());
117202
add(new ParameterMapping.Builder(config, "password", registry.getTypeHandler(String.class)).build());
118203
add(new ParameterMapping.Builder(config, "email", registry.getTypeHandler(String.class)).build());
@@ -127,10 +212,56 @@ void testParameterObjectMetaObjectGetValue() {
127212

128213
defaultParameterHandler.setParameters(ps);
129214

215+
verify(ps, times(1)).setInt(1, parameterObject.getId());
216+
verify(ps, times(1)).setString(2, parameterObject.getUsername());
217+
verify(ps, times(1)).setString(3, parameterObject.getPassword());
218+
verify(ps, times(1)).setString(4, parameterObject.getEmail());
219+
verify(ps, times(1)).setString(5, parameterObject.getBio());
220+
verify(ps, times(1)).setObject(6, parameterObject.getFavouriteSection().name(), JdbcType.VARCHAR.TYPE_CODE);
221+
}
222+
223+
@Test
224+
void testParameterObjectGetPropertyValueWithMetaObjectAndCreateOnce() {
225+
Author parameterObject = mock(Author.class);
226+
227+
Configuration mockConfig = mock(Configuration.class);
228+
229+
final ObjectFactory objectFactory = new DefaultObjectFactory();
230+
final ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
231+
final ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
232+
233+
when(mockConfig.getTypeHandlerRegistry()).thenReturn(new TypeHandlerRegistry(mockConfig));
234+
when(mockConfig.getDefaultScriptingLanguageInstance()).thenReturn(new XMLLanguageDriver());
235+
when(mockConfig.newMetaObject(parameterObject)).thenReturn(MetaObject.forObject(parameterObject, objectFactory, objectWrapperFactory, reflectorFactory));
236+
237+
TypeHandlerRegistry registry = mockConfig.getTypeHandlerRegistry();
238+
239+
MappedStatement mappedStatement = new MappedStatement.Builder(mockConfig, "testSelect", new StaticSqlSource(mockConfig, "some select statement"), SqlCommandType.SELECT).build();
240+
241+
BoundSql boundSql = new BoundSql(mockConfig, "some select statement", new ArrayList<ParameterMapping>() {
242+
{
243+
add(new ParameterMapping.Builder(mockConfig, "id", registry.getTypeHandler(int.class)).jdbcType(JdbcType.INTEGER).build());
244+
add(new ParameterMapping.Builder(mockConfig, "username", registry.getTypeHandler(String.class)).jdbcType(JdbcType.VARCHAR).build());
245+
add(new ParameterMapping.Builder(mockConfig, "password", registry.getTypeHandler(String.class)).jdbcType(JdbcType.VARCHAR).build());
246+
add(new ParameterMapping.Builder(mockConfig, "email", registry.getTypeHandler(String.class)).jdbcType(JdbcType.VARCHAR).build());
247+
add(new ParameterMapping.Builder(mockConfig, "bio", registry.getTypeHandler(String.class)).jdbcType(JdbcType.VARCHAR).build());
248+
add(new ParameterMapping.Builder(mockConfig, "favouriteSection", registry.getTypeHandler(Section.class)).jdbcType(JdbcType.VARCHAR).build());
249+
}
250+
}, parameterObject);
251+
252+
DefaultParameterHandler defaultParameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
253+
254+
PreparedStatement ps = mock(PreparedStatement.class);
255+
256+
defaultParameterHandler.setParameters(ps);
257+
258+
verify(parameterObject, times(1)).getId();
130259
verify(parameterObject, times(1)).getUsername();
131260
verify(parameterObject, times(1)).getPassword();
132261
verify(parameterObject, times(1)).getEmail();
133262
verify(parameterObject, times(1)).getBio();
134263
verify(parameterObject, times(1)).getFavouriteSection();
264+
265+
verify(mockConfig, times(1)).newMetaObject(parameterObject);
135266
}
136267
}

0 commit comments

Comments
 (0)