|
import java.io.ByteArrayInputStream; |
|
import java.io.ByteArrayOutputStream; |
|
import java.io.File; |
|
import java.io.ObjectInputStream; |
|
import java.io.ObjectOutputStream; |
|
import java.sql.Connection; |
|
import java.sql.DriverManager; |
|
import java.sql.PreparedStatement; |
|
import java.sql.ResultSet; |
|
import java.sql.SQLException; |
|
import java.sql.Statement; |
|
import java.util.ArrayList; |
|
import java.util.Calendar; |
|
import java.util.GregorianCalendar; |
|
import java.util.InputMismatchException; |
|
import java.util.LinkedHashMap; |
|
import java.util.LinkedList; |
|
import java.util.List; |
|
import java.util.Map; |
|
import java.util.Map.Entry; |
|
import java.util.Scanner; |
|
|
|
import org.h2.api.JavaObjectSerializer; |
|
import org.jboss.serial.io.JBossObjectInputStream; |
|
import org.jboss.serial.io.JBossObjectOutputStream; |
|
|
|
import com.esotericsoftware.kryo.Kryo; |
|
import com.esotericsoftware.kryo.io.Input; |
|
import com.esotericsoftware.kryo.io.Output; |
|
|
|
public class Main { |
|
|
|
private static final int STEP = 10; |
|
private static final int SAMPLES_COUNT = 10; |
|
private static final int MAX_ROWS = 10000; |
|
private static final int CALENDAR_COUNT = 50; |
|
private String url; |
|
|
|
public static void main (final String[] args) throws Exception { |
|
final File dbDir = new File (System.getProperty ("java.io.tmpdir"), "h2perftest"); |
|
// executeUserDriven (dbDIr); |
|
acquireStats (dbDir); |
|
} |
|
|
|
private static void acquireStats (final File dbDir) throws Exception { |
|
final int samplesCount = SAMPLES_COUNT; |
|
final Map<String, List<Long>> stats = new LinkedHashMap<String, List<Long>> (); |
|
final int maxRows = MAX_ROWS; |
|
for (int rowCount = 1; rowCount <= maxRows; rowCount = rowCount * STEP) { |
|
List<Long> rowStats = new LinkedList<Long> (); |
|
stats.put (Integer.toString (rowCount), rowStats); |
|
for (int sampleNum = 0; sampleNum < samplesCount; sampleNum++) { |
|
Main main = new Main (); |
|
long time = main.work (rowCount, dbDir); |
|
rowStats.add (time); |
|
} |
|
} |
|
for (final Entry<String, List<Long>> statsEntry : stats.entrySet ()) { |
|
String label = statsEntry.getKey (); |
|
double sum = 0; |
|
|
|
final List<Long> data = statsEntry.getValue (); |
|
for (Long time : data) { |
|
sum += time; |
|
} |
|
System.out.println ("[" + System.getProperty ("h2.javaObjectSerializer") + "] execution average time: " |
|
+ (sum / data.size ()) + "ms for " + label + " rows (" + samplesCount + " executions)"); |
|
} |
|
} |
|
|
|
private static void executeUserDriven (final File dbDir) throws Exception { |
|
final Scanner scanner = new Scanner (System.in); |
|
while (askRowCount (scanner)) { |
|
try { |
|
|
|
final int rowCount = scanner.nextInt (); |
|
long time = new Main ().work (rowCount, dbDir); |
|
System.out.println ("Execution time: " + time + "ms for " + rowCount + " rows"); |
|
|
|
} catch (final InputMismatchException e) { |
|
final String cmd = scanner.nextLine (); |
|
if ("q".equalsIgnoreCase (cmd)) { |
|
return; |
|
} |
|
} |
|
} |
|
} |
|
|
|
private static boolean askRowCount (final Scanner scanner) { |
|
System.out.println ("Please provide the number of rows to insert, then hit ENTER:"); |
|
return scanner.hasNextInt (); |
|
} |
|
|
|
private long work (final int rowCount, final File dbDir) throws Exception { |
|
final long startTime = System.currentTimeMillis (); |
|
|
|
dbDir.mkdirs (); |
|
dbDir.deleteOnExit (); |
|
|
|
Class.forName ("org.h2.Driver"); |
|
url = "jdbc:h2:" + dbDir.getCanonicalPath () + "/data"; |
|
|
|
withDb (new JdbcTemplate () { |
|
@Override |
|
public void execute (final Connection c) throws SQLException { |
|
final Statement statement = c.createStatement (); |
|
try { |
|
statement |
|
.execute ("CREATE TABLE IF NOT EXISTS mydata (id LONG PRIMARY KEY AUTO_INCREMENT, data OTHER );"); |
|
statement.execute ("TRUNCATE TABLE mydata;"); |
|
} finally { |
|
statement.close (); |
|
} |
|
|
|
} |
|
}); |
|
|
|
final List<List<Calendar>> originalData = new ArrayList<List<Calendar>> (); |
|
|
|
withDb (new JdbcTemplate () { |
|
@Override |
|
public void execute (final Connection c) throws SQLException { |
|
final PreparedStatement stmt = c.prepareStatement ("INSERT INTO mydata(data) values (?)"); |
|
try { |
|
final int calendarCount = CALENDAR_COUNT; |
|
for (int row = 0; row < rowCount; row++) { |
|
final List<Calendar> data = new LinkedList<Calendar> (); |
|
originalData.add (data); |
|
for (int i = 0; i < calendarCount; i++) { |
|
final Calendar calendar = new GregorianCalendar (row, 0, 0, 0, 0, i); |
|
data.add (calendar); |
|
} |
|
stmt.setObject (1, data); |
|
stmt.addBatch (); |
|
} |
|
stmt.executeBatch (); |
|
} finally { |
|
stmt.close (); |
|
} |
|
|
|
} |
|
}); |
|
|
|
withDb (new JdbcTemplate () { |
|
@Override |
|
public void execute (final Connection c) throws SQLException { |
|
final PreparedStatement stmt = c.prepareStatement ("SELECT * FROM mydata"); |
|
try { |
|
final ResultSet rs = stmt.executeQuery (); |
|
try { |
|
final List<List<Calendar>> actualData = new ArrayList<List<Calendar>> (); |
|
while (rs.next ()) { |
|
// final Object idValue = rs.getObject (1); |
|
final Object dataValue = rs.getObject (2); |
|
// System.out.print (idValue); |
|
// System.out.print ("\t\t"); |
|
// System.out.println (dataValue); |
|
|
|
actualData.add ((List<Calendar>) dataValue); |
|
} |
|
|
|
/* |
|
* data consistency check |
|
*/ |
|
if (!actualData.equals (originalData)) { |
|
throw new RuntimeException ("data corruption! originalData: " + originalData |
|
+ "\nactualData: " + actualData); |
|
} |
|
} finally { |
|
rs.close (); |
|
} |
|
} finally { |
|
stmt.close (); |
|
} |
|
|
|
} |
|
}); |
|
|
|
return System.currentTimeMillis () - startTime; |
|
|
|
} |
|
|
|
private void withDb (final JdbcTemplate jdbcTemplate) throws SQLException { |
|
final Connection c = DriverManager.getConnection (url, "sa", "sa"); |
|
|
|
try { |
|
jdbcTemplate.execute (c); |
|
} finally { |
|
c.close (); |
|
} |
|
|
|
} |
|
|
|
static interface JdbcTemplate { |
|
void execute (Connection c) throws SQLException; |
|
} |
|
|
|
public static class KryoSerializer implements JavaObjectSerializer { |
|
|
|
final Kryo kryo = new Kryo (); |
|
|
|
@Override |
|
public byte[] serialize (final Object obj) throws Exception { |
|
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream (); |
|
final Output output = new Output (byteArrayOutputStream); |
|
try { |
|
kryo.writeClassAndObject (output, obj); |
|
} finally { |
|
output.close (); |
|
} |
|
return byteArrayOutputStream.toByteArray (); |
|
} |
|
|
|
@Override |
|
public Object deserialize (final byte[] bytes) throws Exception { |
|
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream (bytes); |
|
final Input input = new Input (byteArrayInputStream); |
|
try { |
|
return kryo.readClassAndObject (input); |
|
} finally { |
|
input.close (); |
|
} |
|
} |
|
|
|
} |
|
|
|
public static class JBossSerializer implements JavaObjectSerializer { |
|
|
|
@Override |
|
public byte[] serialize (final Object obj) throws Exception { |
|
final ByteArrayOutputStream baos = new ByteArrayOutputStream (); |
|
try { |
|
final ObjectOutputStream oos = new JBossObjectOutputStream (baos); |
|
try { |
|
oos.writeObject (obj); |
|
oos.flush (); |
|
} finally { |
|
oos.close (); |
|
} |
|
} finally { |
|
baos.close (); |
|
} |
|
return baos.toByteArray (); |
|
} |
|
|
|
@Override |
|
public Object deserialize (final byte[] bytes) throws Exception { |
|
final ByteArrayInputStream bais = new ByteArrayInputStream (bytes); |
|
try { |
|
final ObjectInputStream ois = new JBossObjectInputStream (bais); |
|
try { |
|
return ois.readObject (); |
|
} finally { |
|
ois.close (); |
|
} |
|
} finally { |
|
bais.close (); |
|
} |
|
} |
|
|
|
} |
|
} |