Skip to content

Commit 3fb3b7d

Browse files
odrotbohmcbeams
authored andcommitted
Work around JDK7 String#substring performance regression
String#substring has become significantly slower as of JDK 1.7.0_06 [1], such that there are performance degradations by a factor of 100-1000 in ResourceDatabasePopulator, especially for large SQL files. This commit works around this problem by minimizing the substring scope to the least amount possible to prevent unnecessary internal copying of strings (which seems to cause the issue). [1]: http://mail.openjdk.java.net/pipermail/core-libs-dev/2012-May/010257.html Issue: SPR-9781
1 parent dfe0530 commit 3fb3b7d

File tree

3 files changed

+2057
-5
lines changed

3 files changed

+2057
-5
lines changed

spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2011 the original author or authors.
2+
* Copyright 2002-2012 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -43,6 +43,7 @@
4343
* @author Dave Syer
4444
* @author Juergen Hoeller
4545
* @author Chris Beams
46+
* @author Oliver Gierke
4647
* @since 3.0
4748
*/
4849
public class ResourceDatabasePopulator implements DatabasePopulator {
@@ -265,16 +266,37 @@ private boolean containsSqlScriptDelimiters(String script, String delim) {
265266
if (content[i] == '\'') {
266267
inLiteral = !inLiteral;
267268
}
268-
if (!inLiteral && script.substring(i).startsWith(delim)) {
269+
if (!inLiteral && startsWithDelimiter(script, i, delim)) {
269270
return true;
270271
}
271272
}
272273
return false;
273274
}
274275

275276
/**
276-
* Split an SQL script into separate statements delimited with the provided delimiter character.
277-
* Each individual statement will be added to the provided <code>List</code>.
277+
* Return whether the substring of a given source {@link String} starting at the
278+
* given index starts with the given delimiter.
279+
*
280+
* @param source the source {@link String} to inspect
281+
* @param startIndex the index to look for the delimiter
282+
* @param delim the delimiter to look for
283+
*/
284+
private boolean startsWithDelimiter(String source, int startIndex, String delim) {
285+
286+
int endIndex = startIndex + delim.length();
287+
288+
if (source.length() < endIndex) {
289+
// String is too short to contain the delimiter
290+
return false;
291+
}
292+
293+
return source.substring(startIndex, endIndex).equals(delim);
294+
}
295+
296+
/**
297+
* Split an SQL script into separate statements delimited with the provided delimiter
298+
* character. Each individual statement will be added to the provided {@code List}.
299+
*
278300
* @param script the SQL script
279301
* @param delim character delimiting each statement (typically a ';' character)
280302
* @param statements the List that will contain the individual statements
@@ -301,7 +323,7 @@ private void splitSqlScript(String script, String delim, List<String> statements
301323
inLiteral = !inLiteral;
302324
}
303325
if (!inLiteral) {
304-
if (script.substring(i).startsWith(delim)) {
326+
if (startsWithDelimiter(script, i, delim)) {
305327
if (sb.length() > 0) {
306328
statements.add(sb.toString());
307329
sb = new StringBuilder();

spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/DatabasePopulatorTests.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,4 +238,21 @@ public void usesBoundConnectionIfAvailable() throws SQLException {
238238

239239
EasyMock.verify(populator);
240240
}
241+
242+
/**
243+
* @see SPR-9781
244+
*/
245+
@Test(timeout = 1000)
246+
public void executesHugeScriptInReasonableTime() throws SQLException {
247+
248+
databasePopulator.addScript(resourceLoader.getResource("db-schema.sql"));
249+
databasePopulator.addScript(resourceLoader.getResource("db-test-data-huge.sql"));
250+
251+
Connection connection = db.getConnection();
252+
try {
253+
databasePopulator.populate(connection);
254+
} finally {
255+
connection.close();
256+
}
257+
}
241258
}

0 commit comments

Comments
 (0)