Browse Source

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
pull/137/merge
Oliver Gierke 13 years ago committed by Chris Beams
parent
commit
3fb3b7d67a
  1. 32
      spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java
  2. 17
      spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/DatabasePopulatorTests.java
  3. 2013
      spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/db-test-data-huge.sql

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

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -43,6 +43,7 @@ import org.springframework.util.StringUtils; @@ -43,6 +43,7 @@ import org.springframework.util.StringUtils;
* @author Dave Syer
* @author Juergen Hoeller
* @author Chris Beams
* @author Oliver Gierke
* @since 3.0
*/
public class ResourceDatabasePopulator implements DatabasePopulator {
@ -265,7 +266,7 @@ public class ResourceDatabasePopulator implements DatabasePopulator { @@ -265,7 +266,7 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
if (content[i] == '\'') {
inLiteral = !inLiteral;
}
if (!inLiteral && script.substring(i).startsWith(delim)) {
if (!inLiteral && startsWithDelimiter(script, i, delim)) {
return true;
}
}
@ -273,8 +274,29 @@ public class ResourceDatabasePopulator implements DatabasePopulator { @@ -273,8 +274,29 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
}
/**
* Split an SQL script into separate statements delimited with the provided delimiter character.
* Each individual statement will be added to the provided <code>List</code>.
* Return whether the substring of a given source {@link String} starting at the
* given index starts with the given delimiter.
*
* @param source the source {@link String} to inspect
* @param startIndex the index to look for the delimiter
* @param delim the delimiter to look for
*/
private boolean startsWithDelimiter(String source, int startIndex, String delim) {
int endIndex = startIndex + delim.length();
if (source.length() < endIndex) {
// String is too short to contain the delimiter
return false;
}
return source.substring(startIndex, endIndex).equals(delim);
}
/**
* Split an SQL script into separate statements delimited with the provided delimiter
* character. Each individual statement will be added to the provided {@code List}.
*
* @param script the SQL script
* @param delim character delimiting each statement (typically a ';' character)
* @param statements the List that will contain the individual statements
@ -301,7 +323,7 @@ public class ResourceDatabasePopulator implements DatabasePopulator { @@ -301,7 +323,7 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
inLiteral = !inLiteral;
}
if (!inLiteral) {
if (script.substring(i).startsWith(delim)) {
if (startsWithDelimiter(script, i, delim)) {
if (sb.length() > 0) {
statements.add(sb.toString());
sb = new StringBuilder();

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

@ -238,4 +238,21 @@ public class DatabasePopulatorTests { @@ -238,4 +238,21 @@ public class DatabasePopulatorTests {
EasyMock.verify(populator);
}
/**
* @see SPR-9781
*/
@Test(timeout = 1000)
public void executesHugeScriptInReasonableTime() throws SQLException {
databasePopulator.addScript(resourceLoader.getResource("db-schema.sql"));
databasePopulator.addScript(resourceLoader.getResource("db-test-data-huge.sql"));
Connection connection = db.getConnection();
try {
databasePopulator.populate(connection);
} finally {
connection.close();
}
}
}

2013
spring-jdbc/src/test/resources/org/springframework/jdbc/datasource/init/db-test-data-huge.sql

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save