提交 7a163bbc authored 作者: noelgrandin's avatar noelgrandin

New feature from Davide Cavestro - allow using custom Java object serialization…

New feature from Davide Cavestro - allow using custom Java object serialization engines on a per-DB basis.
上级 85e74dd1
......@@ -1207,6 +1207,26 @@ This setting can be appended to the database URL: ""jdbc:h2:test;IGNORECASE=TRUE
SET IGNORECASE TRUE
"
"Commands (Other)","SET JAVA_OBJECT_SERIALIZER","
SET JAVA_OBJECT_SERIALIZER
{ null | className }
","
Sets the object used to serialize and deserialize java objects being stored in column of type OTHER.
The serializer class must be public and implement ""org.h2.api.JavaObjectSerializer"".
Inner classes are not supported.
The class must be available in the classpath of the database engine
(when using the server mode, it must be both in the classpath of the server and the client).
This command can only be executed if there are no tables defined.
Admin rights are required to execute this command.
This command commits an open transaction.
This setting is persistent.
This setting can be appended to the database URL: ""jdbc:h2:test;JAVA_OBJECT_SERIALIZER='com.acme.SerializerClassName'""
","
SET JAVA_OBJECT_SERIALIZER 'com.acme.SerializerClassName'
"
"Commands (Other)","SET LOG","
SET LOG int
","
......
......@@ -80,6 +80,8 @@ Advanced
Split File System</a><br />
<a href="#database_upgrade">
Database Upgrade</a><br />
<a href="#java_objects_serialization">
Java Objects Serialization</a><br />
<a href="#limits_limitations">
Limits and Limitations</a><br />
<a href="#glossary_links">
......@@ -1566,6 +1568,32 @@ Please note the old driver did not process the system property <code>"h2.baseDir
so that using this setting is not supported when upgrading.
</p>
<h2 id="java_objects_serialization">Java Objects Serialization</h2>
<p>
Java objects serialization is enabled by default for columns of type <code>OTHER</code>, using standard Java serialization/deserialization semantics.
</p>
<p>
To disable this feature set the system property <code>h2.serializeJavaObject=false</code> (default: true).
</p>
<p>
Serialization and deserialization of java objects is customizable both at system level and at database level providing a <a href="../javadoc/org/h2/api/JavaObjectSerializer.html">JavaObjectSerializer</a> implementation:
<ul>
<li>
At system level set the system property <code>h2.javaObjectSerializer</code> with the
Fully Qualified Name of the <code>JavaObjectSerializer</code> interface implementation.
It will be used over the entire JVM session to (de)serialize java objects being stored in column of type OTHER.
Example <code>h2.javaObjectSerializer=com.acme.SerializerClassName</code>.
</li>
<li>
At database level execute the SQL statement <code>SET JAVA_OBJECT_SERIALIZER 'com.acme.SerializerClassName'</code>
or append <code>;JAVA_OBJECT_SERIALIZER='com.acme.SerializerClassName'</code> to the database URL: <code>jdbc:h2:~/test;JAVA_OBJECT_SERIALIZER='com.acme.SerializerClassName'</code>.
<p>
Please note that this SQL statement can only be executed before any tables are defined.
</p>
</li>
</ul>
</p>
<h2 id="limits_limitations">Limits and Limitations</h2>
<p>
This database has the following known limitations:
......
......@@ -26,6 +26,8 @@ Change Log
e.g. where the one was INT and the other was LONG
</li><li>Bug: Changes to the database structure did not result
in the Session query cache being invalidated.
</li><li>New feature from Davide Cavestro - allow using custom Java object serialization
engines on a per-DB basis.
</li></ul>
<h2>Version 1.3.173 (2013-07-28)</h2>
......
......@@ -159,6 +159,7 @@
90138=Invalid database name: {0}
90139=The public static Java method was not found: {0}
90140=The result set is readonly. You may need to use conn.createStatement(.., ResultSet.CONCUR_UPDATABLE).
90141=Serializer cannot be changed because there is a data table: {0}
HY000=General error: {0}
HY004=Unknown data type: {0}
HYC00=Feature not supported: {0}
......
......@@ -4716,6 +4716,9 @@ public class Parser {
list.toArray(schemaNames);
command.setStringArray(schemaNames);
return command;
} else if (readIf("JAVA_OBJECT_SERIALIZER")) {
readIfEqualOrTo();
return parseSetJavaObjectSerializer();
} else {
if (isToken("LOGSIZE")) {
// HSQLDB compatibility
......@@ -4775,6 +4778,13 @@ public class Parser {
throw DbException.getInvalidValueException("BINARY_COLLATION", name);
}
private Set parseSetJavaObjectSerializer() {
Set command = new Set(session, SetTypes.JAVA_OBJECT_SERIALIZER);
String name = readString();
command.setString(name);
return command;
}
private RunScriptCommand parseRunScript() {
RunScriptCommand command = new RunScriptCommand(session);
read("FROM");
......
......@@ -12,6 +12,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Connection;
import org.h2.api.JavaObjectSerializer;
import org.h2.command.Prepared;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
......@@ -258,4 +259,8 @@ abstract class ScriptBase extends Prepared implements DataHandler {
throw DbException.throwInternalError();
}
@Override
public JavaObjectSerializer getJavaObjectSerializer() {
return session.getDataHandler().getJavaObjectSerializer();
}
}
......@@ -220,6 +220,16 @@ public class Set extends Prepared {
}
break;
}
case SetTypes.JAVA_OBJECT_SERIALIZER: {
session.getUser().checkAdmin();
Table table = database.getFirstUserTable();
if (table != null) {
throw DbException.get(ErrorCode.JAVA_OBJECT_SERIALIZER_CHANGE_WITH_DATA_TABLE, table.getSQL());
}
database.setJavaObjectSerializerFQN (stringValue);
addOrUpdateSetting(name, stringValue, 0);
break;
}
case SetTypes.IGNORECASE:
session.getUser().checkAdmin();
database.setIgnoreCase(getIntValue() == 1);
......
......@@ -200,10 +200,15 @@ public class SetTypes {
public static final int REDO_LOG_BINARY = 37;
/**
* The type of a SET BINARY_COLLATION statement.
* The type of a SET BINARY_COLLATION statement.
*/
public static final int BINARY_COLLATION = 38;
/**
* The type of a SET JAVA_OBJECT_SERIALIZER statement.
*/
public static final int JAVA_OBJECT_SERIALIZER = 39;
private static final ArrayList<String> TYPES = New.arrayList();
private SetTypes() {
......@@ -251,6 +256,7 @@ public class SetTypes {
list.add(QUERY_TIMEOUT, "QUERY_TIMEOUT");
list.add(REDO_LOG_BINARY, "REDO_LOG_BINARY");
list.add(BINARY_COLLATION, "BINARY_COLLATION");
list.add(JAVA_OBJECT_SERIALIZER, "JAVA_OBJECT_SERIALIZER");
}
/**
......
......@@ -1860,8 +1860,18 @@ public class ErrorCode {
*/
public static final int RESULT_SET_READONLY = 90140;
/**
* The error with code <code>90141</code> is thrown when
* trying to change the java object serializer while there was already data in
* the database. The serializer of the database must be set when the
* database is empty.
*/
public static final int JAVA_OBJECT_SERIALIZER_CHANGE_WITH_DATA_TABLE = 90141;
// next are 90006, 90009, 90010, 90011, 90021, 90039,
// 90051, 90056, 90110, 90122, 90141
// 90051, 90056, 90110, 90122, 90142
private ErrorCode() {
// utility class
......
......@@ -12,6 +12,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Properties;
import org.h2.command.dml.SetTypes;
import org.h2.constant.DbSettings;
import org.h2.constant.ErrorCode;
......@@ -602,7 +603,7 @@ public class ConnectionInfo implements Cloneable {
this.name = serverKey;
}
DbSettings getDbSettings() {
public DbSettings getDbSettings() {
DbSettings defaultSettings = DbSettings.getInstance(null);
HashMap<String, String> s = null;
for (Object k : prop.keySet()) {
......
......@@ -15,8 +15,8 @@ import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import org.h2.api.DatabaseEventListener;
import org.h2.api.JavaObjectSerializer;
import org.h2.command.ddl.CreateTableData;
import org.h2.command.dml.SetTypes;
import org.h2.constant.DbSettings;
......@@ -178,6 +178,10 @@ public class Database implements DataHandler {
private MVTableEngine.Store mvStore;
private DbException backgroundException;
private JavaObjectSerializer javaObjectSerializer;
private String javaObjectSerializerFQN;
private volatile boolean javaObjectSerializerInitialized;
public Database(ConnectionInfo ci, String cipher) {
String name = ci.getName();
this.dbSettings = ci.getDbSettings();
......@@ -212,6 +216,8 @@ public class Database implements DataHandler {
}
this.multiVersion = ci.getProperty("MVCC", false);
this.logMode = ci.getProperty("LOG", PageStore.LOG_MODE_SYNC);
this.javaObjectSerializerFQN = ci.getProperty("JAVA_OBJECT_SERIALIZER", null);
boolean closeAtVmShutdown = dbSettings.dbCloseOnExit;
int traceLevelFile = ci.getIntProperty(SetTypes.TRACE_LEVEL_FILE, TraceSystem.DEFAULT_TRACE_LEVEL_FILE);
int traceLevelSystemOut = ci.getIntProperty(SetTypes.TRACE_LEVEL_SYSTEM_OUT,
......@@ -2498,4 +2504,39 @@ public class Database implements DataHandler {
return filePasswordHash;
}
@Override
public JavaObjectSerializer getJavaObjectSerializer() {
initJavaObjectSerializer();
return javaObjectSerializer;
}
private void initJavaObjectSerializer() {
if (javaObjectSerializerInitialized) {
return;
}
synchronized (this) {
if (javaObjectSerializerInitialized) {
return;
}
String serializerFQN = javaObjectSerializerFQN;
if (serializerFQN != null) {
serializerFQN = serializerFQN.trim();
if (!serializerFQN.isEmpty() && !serializerFQN.equals("null")) {
try {
javaObjectSerializer = (JavaObjectSerializer) Utils.loadUserClass(serializerFQN).newInstance();
} catch (Exception e) {
throw DbException.convert(e);
}
}
}
javaObjectSerializerInitialized = true;
}
}
public void setJavaObjectSerializerFQN(String javaObjectSerializerFQN) {
this.javaObjectSerializerFQN = javaObjectSerializerFQN;
synchronized (this) {
javaObjectSerializerInitialized = false;
}
}
}
......@@ -11,6 +11,7 @@ import java.net.Socket;
import java.sql.Connection;
import java.util.ArrayList;
import org.h2.api.DatabaseEventListener;
import org.h2.api.JavaObjectSerializer;
import org.h2.command.CommandInterface;
import org.h2.command.CommandRemote;
import org.h2.command.dml.SetTypes;
......@@ -20,6 +21,7 @@ import org.h2.jdbc.JdbcSQLException;
import org.h2.message.DbException;
import org.h2.message.Trace;
import org.h2.message.TraceSystem;
import org.h2.result.ResultInterface;
import org.h2.store.DataHandler;
import org.h2.store.FileStore;
import org.h2.store.LobStorageFrontend;
......@@ -33,6 +35,7 @@ import org.h2.util.StringUtils;
import org.h2.util.TempFileDeleter;
import org.h2.util.Utils;
import org.h2.value.Transfer;
import org.h2.value.Value;
/**
* The client side part of a session when using the server mode. This object
......@@ -87,6 +90,9 @@ public class SessionRemote extends SessionWithState implements DataHandler {
private boolean cluster;
private TempFileDeleter tempFileDeleter;
private JavaObjectSerializer javaObjectSerializer;
private volatile boolean javaObjectSerializerInitialized;
public SessionRemote(ConnectionInfo ci) {
this.connectionInfo = ci;
}
......@@ -749,4 +755,52 @@ public class SessionRemote extends SessionWithState implements DataHandler {
return 1;
}
@Override
public JavaObjectSerializer getJavaObjectSerializer() {
initJavaObjectSerializer();
return javaObjectSerializer;
}
private void initJavaObjectSerializer() {
if (javaObjectSerializerInitialized) {
return;
}
synchronized (this) {
if (javaObjectSerializerInitialized) {
return;
}
String serializerFQN = readSerializationSettings();
if (serializerFQN != null) {
serializerFQN = serializerFQN.trim();
if (!serializerFQN.isEmpty() && !serializerFQN.equals("null")) {
try {
javaObjectSerializer = (JavaObjectSerializer) Utils.loadUserClass(serializerFQN).newInstance();
} catch (Exception e) {
throw DbException.convert(e);
}
}
}
javaObjectSerializerInitialized = true;
}
}
/**
* Read needed persistent db settings
*/
private String readSerializationSettings () {
String javaObjectSerializerFQN = null;
CommandInterface ci = prepareCommand(
"SELECT * FROM INFORMATION_SCHEMA.SETTINGS "+
" WHERE NAME='JAVA_OBJECT_SERIALIZER'", Integer.MAX_VALUE);
try {
ResultInterface result = ci.executeQuery(0, false);
if (result.next()) {
Value[] row = result.currentRow();
javaObjectSerializerFQN = row[1].getString();
}
} finally {
ci.close();
}
return javaObjectSerializerFQN;
}
}
......@@ -1833,7 +1833,7 @@ public class JdbcConnection extends TraceObject implements Connection {
}
case Value.JAVA_OBJECT:
if (SysProperties.serializeJavaObject) {
o = Utils.deserialize(v.getBytesNoCopy());
o = Utils.deserialize(v.getBytesNoCopy(), session.getDataHandler());
break;
}
default:
......
......@@ -580,7 +580,7 @@ public class ValueDataType implements DataType {
int len = readVarInt(buff);
byte[] b = DataUtils.newBytes(len);
buff.get(b, 0, len);
return ValueJavaObject.getNoCopy(null, b);
return ValueJavaObject.getNoCopy(null, b, handler);
}
case Value.UUID:
return ValueUuid.get(buff.getLong(), buff.getLong());
......
......@@ -159,6 +159,7 @@
90138=Invalid database name: {0}
90139=The public static Java method was not found: {0}
90140=The result set is readonly. You may need to use conn.createStatement(.., ResultSet.CONCUR_UPDATABLE).
90141=Serializer cannot be changed because there is a data table: {0}
HY000=General error: {0}
HY004=Unknown data type: {0}
HYC00=Feature not supported: {0}
......
......@@ -387,6 +387,11 @@ SET IGNORECASE { TRUE | FALSE }
","
If IGNORECASE is enabled, text columns in newly created tables will be
case-insensitive."
"Commands (Other)","SET JAVA_OBJECT_SERIALIZER","
SET JAVA_OBJECT_SERIALIZER
{ null | className }
","
Sets the object used to serialize and deserialize java objects being stored in column of type OTHER."
"Commands (Other)","SET LOG","
SET LOG int
","
......
......@@ -772,7 +772,7 @@ public class Data {
int len = readVarInt();
byte[] b = DataUtils.newBytes(len);
read(b, 0, len);
return ValueJavaObject.getNoCopy(null, b);
return ValueJavaObject.getNoCopy(null, b, handler);
}
case Value.UUID:
return ValueUuid.get(readLong(), readLong());
......
......@@ -7,6 +7,7 @@
package org.h2.store;
import java.sql.Connection;
import org.h2.api.JavaObjectSerializer;
import org.h2.message.DbException;
import org.h2.util.SmallLRUCache;
import org.h2.util.TempFileDeleter;
......@@ -112,4 +113,12 @@ public interface DataHandler {
*/
int readLob(long lobId, byte[] hmac, long offset, byte[] buff, int off, int length);
/**
* Return the serializer to be used for java objects being stored in
* column of type OTHER.
*
* @return the serializer to be used for java objects being stored in
* column of type OTHER
*/
JavaObjectSerializer getJavaObjectSerializer();
}
......@@ -23,6 +23,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.zip.CRC32;
import org.h2.api.JavaObjectSerializer;
import org.h2.compress.CompressLZF;
import org.h2.engine.Constants;
import org.h2.engine.DbObject;
......@@ -1536,4 +1537,8 @@ public class Recover extends Tool implements DataHandler {
throw DbException.throwInternalError();
}
@Override
public JavaObjectSerializer getJavaObjectSerializer() {
return null;
}
}
......@@ -28,6 +28,7 @@ import org.h2.api.JavaObjectSerializer;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.message.DbException;
import org.h2.store.DataHandler;
/**
* This utility class contains miscellaneous functions.
......@@ -331,9 +332,31 @@ public class Utils {
*
* @param obj the object to serialize
* @return the byte array
*
* @deprecated use {@link #serialize(Object, DataHandler)} instead
*/
@Deprecated
public static byte[] serialize(Object obj) {
return serialize(obj, null);
}
/**
* Serialize the object to a byte array, eventually using the serializer
* specified by the connection info.
*
* @param obj the object to serialize
* @param dataHandler provides the object serializer (may be null)
* @return the byte array
*/
public static byte[] serialize(Object obj, DataHandler dataHandler) {
try {
JavaObjectSerializer dbJavaObjectSerializer = null;
if (dataHandler != null) {
dbJavaObjectSerializer = dataHandler.getJavaObjectSerializer();
}
if (dbJavaObjectSerializer != null) {
return dbJavaObjectSerializer.serialize(obj);
}
if (serializer != null) {
return serializer.serialize(obj);
}
......@@ -352,9 +375,32 @@ public class Utils {
* @param data the byte array
* @return the object
* @throws DbException if serialization fails
*
* @deprecated use {@link #deserialize(byte[], DataHandler)} instead
*/
@Deprecated
public static Object deserialize(byte[] data) {
return deserialize(data, null);
}
/**
* De-serialize the byte array to an object, eventually using the serializer
* specified by the connection info.
*
* @param data the byte array
* @param dataHandler provides the object serializer (may be null)
* @return the object
* @throws DbException if serialization fails
*/
public static Object deserialize(byte[] data, DataHandler dataHandler) {
try {
JavaObjectSerializer dbJavaObjectSerializer = null;
if (dataHandler != null) {
dbJavaObjectSerializer = dataHandler.getJavaObjectSerializer();
}
if (dbJavaObjectSerializer != null) {
return dbJavaObjectSerializer.deserialize(data);
}
if (serializer != null) {
return serializer.deserialize(data);
}
......
......@@ -586,10 +586,10 @@ public class DataType {
case Value.JAVA_OBJECT: {
if (SysProperties.serializeJavaObject) {
byte[] buff = rs.getBytes(columnIndex);
v = buff == null ? ValueNull.INSTANCE : ValueJavaObject.getNoCopy(null, buff);
v = buff == null ? ValueNull.INSTANCE : ValueJavaObject.getNoCopy(null, buff, session.getDataHandler());
} else {
Object o = rs.getObject(columnIndex);
v = o == null ? ValueNull.INSTANCE : ValueJavaObject.getNoCopy(o, null);
v = o == null ? ValueNull.INSTANCE : ValueJavaObject.getNoCopy(o, null, session.getDataHandler());
}
break;
}
......@@ -884,7 +884,7 @@ public class DataType {
return ValueNull.INSTANCE;
}
if (type == Value.JAVA_OBJECT) {
return ValueJavaObject.getNoCopy(x, null);
return ValueJavaObject.getNoCopy(x, null, session.getDataHandler());
}
if (x instanceof String) {
return ValueString.get((String) x);
......@@ -957,7 +957,7 @@ public class DataType {
} else if (isGeometry(x)) {
return ValueGeometry.getFromGeometry(x);
} else {
return ValueJavaObject.getNoCopy(x, null);
return ValueJavaObject.getNoCopy(x, null, session.getDataHandler());
}
}
......@@ -1096,7 +1096,8 @@ public class DataType {
return new JdbcClob(conn, v, 0);
}
if (v.getType() == Value.JAVA_OBJECT) {
Object o = SysProperties.serializeJavaObject ? Utils.deserialize(v.getBytes()) : v.getObject();
Object o = SysProperties.serializeJavaObject ? Utils.deserialize(v.getBytes(),
conn.getSession().getDataHandler()) : v.getObject();
if (paramClass.isAssignableFrom(o.getClass())) {
return o;
}
......
......@@ -530,7 +530,7 @@ public class Transfer {
case Value.UUID:
return ValueUuid.get(readLong(), readLong());
case Value.JAVA_OBJECT:
return ValueJavaObject.getNoCopy(null, readBytes());
return ValueJavaObject.getNoCopy(null, readBytes(), session.getDataHandler());
case Value.BOOLEAN:
return ValueBoolean.get(readBoolean());
case Value.BYTE:
......
......@@ -776,7 +776,7 @@ public abstract class Value {
switch(getType()) {
case BYTES:
case BLOB:
return ValueJavaObject.getNoCopy(null, getBytesNoCopy());
return ValueJavaObject.getNoCopy(null, getBytesNoCopy(), getDataHandler());
}
break;
}
......@@ -798,7 +798,7 @@ public abstract class Value {
case BYTES:
return ValueGeometry.get(getBytesNoCopy());
case JAVA_OBJECT:
Object object = Utils.deserialize(getBytesNoCopy());
Object object = Utils.deserialize(getBytesNoCopy(), getDataHandler());
if (DataType.isGeometry(object)) {
return ValueGeometry.getFromGeometry(object);
}
......@@ -844,7 +844,7 @@ public abstract class Value {
case BYTES:
return ValueBytes.getNoCopy(StringUtils.convertHexToBytes(s.trim()));
case JAVA_OBJECT:
return ValueJavaObject.getNoCopy(null, StringUtils.convertHexToBytes(s.trim()));
return ValueJavaObject.getNoCopy(null, StringUtils.convertHexToBytes(s.trim()), getDataHandler());
case STRING:
return ValueString.get(s);
case STRING_IGNORECASE:
......@@ -1099,6 +1099,15 @@ public abstract class Value {
return rs;
}
/**
* Return the data handler for the values that support it
* (actually only Java objects).
* @return the data handler
*/
protected DataHandler getDataHandler() {
return null;
}
/**
* A "binary large object".
*/
......
......@@ -11,6 +11,7 @@ import java.sql.SQLException;
import java.sql.Types;
import org.h2.constant.SysProperties;
import org.h2.store.DataHandler;
import org.h2.util.Utils;
/**
......@@ -18,10 +19,12 @@ import org.h2.util.Utils;
*/
public class ValueJavaObject extends ValueBytes {
private static final ValueJavaObject EMPTY = new ValueJavaObject(Utils.EMPTY_BYTES);
private static final ValueJavaObject EMPTY = new ValueJavaObject(Utils.EMPTY_BYTES, null);
private final DataHandler dataHandler;
protected ValueJavaObject(byte[] v) {
protected ValueJavaObject(byte[] v, DataHandler dataHandler) {
super(v);
this.dataHandler = dataHandler;
}
/**
......@@ -30,20 +33,21 @@ public class ValueJavaObject extends ValueBytes {
*
* @param javaObject the object
* @param b the byte array
* @param dataHandler provides the object serializer
* @return the value
*/
public static ValueJavaObject getNoCopy(Object javaObject, byte[] b) {
public static ValueJavaObject getNoCopy(Object javaObject, byte[] b, DataHandler dataHandler) {
if (b != null && b.length == 0) {
return EMPTY;
}
ValueJavaObject obj;
if (SysProperties.serializeJavaObject) {
if (b == null) {
b = Utils.serialize(javaObject);
b = Utils.serialize(javaObject, dataHandler);
}
obj = new ValueJavaObject(b);
obj = new ValueJavaObject(b, dataHandler);
} else {
obj = new NotSerialized(javaObject, b);
obj = new NotSerialized(javaObject, b, dataHandler);
}
if (b == null || b.length > SysProperties.OBJECT_CACHE_MAX_PER_ELEMENT_SIZE) {
return obj;
......@@ -58,7 +62,7 @@ public class ValueJavaObject extends ValueBytes {
@Override
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
Object obj = Utils.deserialize(getBytesNoCopy());
Object obj = Utils.deserialize(getBytesNoCopy(), getDataHandler());
prep.setObject(parameterIndex, obj, Types.JAVA_OBJECT);
}
......@@ -74,8 +78,8 @@ public class ValueJavaObject extends ValueBytes {
private int displaySize = -1;
NotSerialized(Object javaObject, byte[] v) {
super(v);
NotSerialized(Object javaObject, byte[] v, DataHandler dataHandler) {
super(v, dataHandler);
this.javaObject = javaObject;
}
......@@ -87,7 +91,7 @@ public class ValueJavaObject extends ValueBytes {
@Override
public byte[] getBytesNoCopy() {
if (value == null) {
value = Utils.serialize(javaObject);
value = Utils.serialize(javaObject, null);
}
return value;
}
......@@ -155,7 +159,7 @@ public class ValueJavaObject extends ValueBytes {
@Override
public Object getObject() {
if (javaObject == null) {
javaObject = Utils.deserialize(value);
javaObject = Utils.deserialize(value, getDataHandler());
}
return javaObject;
}
......@@ -193,4 +197,9 @@ public class ValueJavaObject extends ValueBytes {
return this;
}
}
@Override
protected DataHandler getDataHandler() {
return dataHandler;
}
}
......@@ -81,6 +81,7 @@ import org.h2.test.jdbc.TestCancel;
import org.h2.test.jdbc.TestDatabaseEventListener;
import org.h2.test.jdbc.TestDriver;
import org.h2.test.jdbc.TestJavaObject;
import org.h2.test.jdbc.TestJavaObjectSerializer;
import org.h2.test.jdbc.TestLimitUpdates;
import org.h2.test.jdbc.TestLobApi;
import org.h2.test.jdbc.TestManyJdbcObjects;
......@@ -91,6 +92,7 @@ import org.h2.test.jdbc.TestResultSet;
import org.h2.test.jdbc.TestStatement;
import org.h2.test.jdbc.TestTransactionIsolation;
import org.h2.test.jdbc.TestUpdatableResultSet;
import org.h2.test.jdbc.TestUrlJavaObjectSerializer;
import org.h2.test.jdbc.TestZloty;
import org.h2.test.jdbcx.TestConnectionPool;
import org.h2.test.jdbcx.TestDataSource;
......@@ -348,6 +350,8 @@ java org.h2.test.TestAll timer
private Server server;
public String javaObjectSerializer;
/**
* Run all tests.
*
......@@ -648,6 +652,9 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new TestDatabaseEventListener().runTest(this);
new TestDriver().runTest(this);
new TestJavaObject().runTest(this);
new TestJavaObjectSerializer().runTest(this);
new TestUrlJavaObjectSerializer().runTest(this);
new TestLimitUpdates().runTest(this);
new TestLobApi().runTest(this);
new TestManyJdbcObjects().runTest(this);
......
......@@ -320,6 +320,9 @@ public abstract class TestBase {
if (config.nestedJoins) {
url = addOption(url, "NESTED_JOINS", "TRUE");
}
if (config.javaObjectSerializer!=null) {
url = addOption(url, "JAVA_OBJECT_SERIALIZER", config.javaObjectSerializer);
}
return "jdbc:h2:" + url;
}
......
......@@ -1394,7 +1394,7 @@ public class TestLob extends TestBase {
private void testJavaObject() throws SQLException {
deleteDb("lob");
Connection conn = getConnection("lob");
JdbcConnection conn = (JdbcConnection) getConnection("lob");
Statement stat = conn.createStatement();
stat.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, DATA OTHER)");
PreparedStatement prep = conn.prepareStatement("INSERT INTO TEST VALUES(1, ?)");
......@@ -1413,7 +1413,7 @@ public class TestLob extends TestBase {
conn.createStatement().execute("drop table test");
stat.execute("create table test(value other)");
prep = conn.prepareStatement("insert into test values(?)");
prep.setObject(1, Utils.serialize(""));
prep.setObject(1, Utils.serialize("", conn.getSession().getDataHandler()));
prep.execute();
rs = stat.executeQuery("select value from test");
while (rs.next()) {
......
......@@ -19,6 +19,7 @@ import org.h2.util.Utils;
* Tests {@link JavaObjectSerializer}.
*
* @author Sergi Vladykin
* @author Davide Cavestro
*/
public class TestJavaObjectSerializer extends TestBase {
......@@ -39,6 +40,13 @@ public class TestJavaObjectSerializer extends TestBase {
@Override
public void test() throws Exception {
deleteDb("javaSerializer");
testStaticGlobalSerializer();
testDbLevelJavaObjectSerializer();
deleteDb("javaSerializer");
}
public void testStaticGlobalSerializer() throws Exception {
Utils.serializer = new JavaObjectSerializer() {
@Override
public byte[] serialize(Object obj) throws Exception {
......@@ -58,6 +66,7 @@ public class TestJavaObjectSerializer extends TestBase {
try {
deleteDb("javaSerializer");
Connection conn = getConnection("javaSerializer");
Statement stat = conn.createStatement();
stat.execute("create table t(id identity, val other)");
......@@ -74,9 +83,65 @@ public class TestJavaObjectSerializer extends TestBase {
assertEquals(100500, ((Integer) rs.getObject(1)).intValue());
assertEquals(new byte[] { 1, 2, 3 }, rs.getBytes(1));
conn.close();
deleteDb("javaSerializer");
} finally {
Utils.serializer = null;
}
}
/**
* Tests per-db {@link JavaObjectSerializer} when set through the related
* SET command.
*/
public void testDbLevelJavaObjectSerializer() throws Exception {
DbLevelJavaObjectSerializer.testBaseRef = this;
try {
deleteDb("javaSerializer");
Connection conn = getConnection("javaSerializer");
conn.createStatement().execute("SET JAVA_OBJECT_SERIALIZER '"+DbLevelJavaObjectSerializer.class.getName()+"'");
Statement stat = conn.createStatement();
stat.execute("create table t1(id identity, val other)");
PreparedStatement ins = conn.prepareStatement("insert into t1(val) values(?)");
ins.setObject(1, 100500, Types.JAVA_OBJECT);
assertEquals(1, ins.executeUpdate());
Statement s = conn.createStatement();
ResultSet rs = s.executeQuery("select val from t1");
assertTrue(rs.next());
assertEquals(100500, ((Integer) rs.getObject(1)).intValue());
assertEquals(new byte[] { 1, 2, 3 }, rs.getBytes(1));
conn.close();
deleteDb("javaSerializer");
} finally {
DbLevelJavaObjectSerializer.testBaseRef = null;
}
}
public static class DbLevelJavaObjectSerializer implements JavaObjectSerializer {
private static TestBase testBaseRef;
@Override
public byte[] serialize(Object obj) throws Exception {
testBaseRef.assertEquals(100500, ((Integer) obj).intValue());
return new byte[] { 1, 2, 3 };
}
@Override
public Object deserialize(byte[] bytes) throws Exception {
testBaseRef.assertEquals(new byte[] { 1, 2, 3 }, bytes);
return 100500;
}
}
}
/*
* Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.Types;
import org.h2.api.JavaObjectSerializer;
import org.h2.test.TestBase;
/**
* Tests per-db {@link JavaObjectSerializer} when set through the JDBC URL.
*
* @author Davide Cavestro
*/
public class TestUrlJavaObjectSerializer extends TestBase {
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
TestBase test = createCaller().init();
test.config.traceTest = true;
test.config.memory = true;
test.config.networked = true;
test.config.javaObjectSerializer = FakeJavaObjectSerializer.class.getName();
test.config.beforeTest();
test.test();
test.config.afterTest();
}
@Override
public void test() throws Exception {
FakeJavaObjectSerializer.testBaseRef = this;
try {
deleteDb("javaSerializer");
String fqn = FakeJavaObjectSerializer.class.getName();
Connection conn = getConnection("javaSerializer;JAVA_OBJECT_SERIALIZER='"+fqn+"'");
Statement stat = conn.createStatement();
stat.execute("create table t1(id identity, val other)");
PreparedStatement ins = conn.prepareStatement("insert into t1(val) values(?)");
ins.setObject(1, 100500, Types.JAVA_OBJECT);
assertEquals(1, ins.executeUpdate());
Statement s = conn.createStatement();
ResultSet rs = s.executeQuery("select val from t1");
assertTrue(rs.next());
assertEquals(100500, ((Integer) rs.getObject(1)).intValue());
assertEquals(new byte[] { 1, 2, 3 }, rs.getBytes(1));
conn.close();
deleteDb("javaSerializer");
} finally {
FakeJavaObjectSerializer.testBaseRef = null;
}
}
public static class FakeJavaObjectSerializer implements JavaObjectSerializer {
private static TestBase testBaseRef;
@Override
public byte[] serialize(Object obj) throws Exception {
testBaseRef.assertEquals(100500, ((Integer) obj).intValue());
return new byte[] { 1, 2, 3 };
}
@Override
public Object deserialize(byte[] bytes) throws Exception {
testBaseRef.assertEquals(new byte[] { 1, 2, 3 }, bytes);
return 100500;
}
}
}
......@@ -12,6 +12,7 @@ import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import org.h2.api.JavaObjectSerializer;
import org.h2.store.Data;
import org.h2.store.DataHandler;
import org.h2.store.FileStore;
......@@ -167,8 +168,8 @@ public class TestDataPage extends TestBase implements DataHandler {
testValue(ValueTime.get(new Time(0)));
testValue(ValueTimestamp.get(new Timestamp(System.currentTimeMillis())));
testValue(ValueTimestamp.get(new Timestamp(0)));
testValue(ValueJavaObject.getNoCopy(null, new byte[0]));
testValue(ValueJavaObject.getNoCopy(null, new byte[100]));
testValue(ValueJavaObject.getNoCopy(null, new byte[0], this));
testValue(ValueJavaObject.getNoCopy(null, new byte[100], this));
for (int i = 0; i < 300; i++) {
testValue(ValueBytes.getNoCopy(new byte[i]));
}
......@@ -340,4 +341,9 @@ public class TestDataPage extends TestBase implements DataHandler {
return -1;
}
@Override
public JavaObjectSerializer getJavaObjectSerializer() {
return null;
}
}
......@@ -8,6 +8,7 @@ package org.h2.test.unit;
import java.sql.Connection;
import java.util.Random;
import org.h2.api.JavaObjectSerializer;
import org.h2.store.DataHandler;
import org.h2.store.FileStore;
import org.h2.store.LobStorageBackend;
......@@ -194,4 +195,9 @@ public class TestFile extends TestBase implements DataHandler {
return -1;
}
@Override
public JavaObjectSerializer getJavaObjectSerializer() {
return null;
}
}
......@@ -45,7 +45,7 @@ public class TestObjectDeserialization extends TestBase {
usesThreadContextClassLoader = false;
Thread.currentThread().setContextClassLoader(new TestClassLoader());
try {
Utils.deserialize(StringUtils.convertHexToBytes(OBJECT));
Utils.deserialize(StringUtils.convertHexToBytes(OBJECT), null);
fail();
} catch (DbException e) {
// expected
......
......@@ -12,6 +12,7 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Random;
import org.h2.api.JavaObjectSerializer;
import org.h2.store.DataHandler;
import org.h2.store.FileStore;
import org.h2.store.LobStorageBackend;
......@@ -171,4 +172,8 @@ public class TestValueHashMap extends TestBase implements DataHandler {
return -1;
}
@Override
public JavaObjectSerializer getJavaObjectSerializer() {
return null;
}
}
......@@ -14,6 +14,7 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.Random;
import org.h2.api.JavaObjectSerializer;
import org.h2.engine.Constants;
import org.h2.store.DataHandler;
import org.h2.store.FileStore;
......@@ -188,7 +189,7 @@ public class TestValueMemory extends TestBase implements DataHandler {
case Value.RESULT_SET:
return ValueResultSet.get(new SimpleResultSet());
case Value.JAVA_OBJECT:
return ValueJavaObject.getNoCopy(null, randomBytes(random.nextInt(100)));
return ValueJavaObject.getNoCopy(null, randomBytes(random.nextInt(100)), this);
case Value.UUID:
return ValueUuid.get(random.nextLong(), random.nextLong());
case Value.STRING_FIXED:
......@@ -286,4 +287,8 @@ public class TestValueMemory extends TestBase implements DataHandler {
return -1;
}
@Override
public JavaObjectSerializer getJavaObjectSerializer() {
return null;
}
}
......@@ -35,7 +35,7 @@ balance balancing bananas band banking bar barcelona base based baseline basic
basically basis bat batch bbalance bcc bcdfghklmnprstwz bdata bdfghklmnpqrst
bdquo beans became because become becomes becoming been before begin beginning
behalf behave behavior behaviour behind being bel believes belong belongs below
bench benchmark benchmarks beneficial bennet berlini best beta better between
bench benchmark benchmarks beneficial bennet berlini best beta better between beware
beyond bfff bgcolor bid big bigger biggest bigint biginteger billion bin binary
bind biological biondi birth birthday bit bitand bitmap bitor bits bitxor
blank blind blob blobs block blocked blockquote blocks blocksize blog
......@@ -52,7 +52,7 @@ calling calls cally camel can cancel canceled cancels cannot canonical cap
capabilities capability capacity capone caps capture car cardinality care careful
carriage carrier cars cartesian cascade cascading case cases casesensitive
casewhen casqueiro cast cat catalog catalogs catch catcher catches caucho cause
caused causes cayenne cbc ccc ccedil cdata cdd cddl cdup cedil ceil ceiling cell
caused causes cavestro cayenne cbc ccc ccedil cdata cdd cddl cdup cedil ceil ceiling cell
cellpadding cells cellspacing cent center central cert certain certificate
certificates certified certs cet cfg cgi chain chained chaining chair challenge
challenger chance change changed changelog changes changing channel channels char
......@@ -98,11 +98,11 @@ createtable creating creation creatively credential credit criteria critical crl
cross cruncher crypt cryptographic cryptographically cryptoloop css csv csvread
csvwrite cte ctid ctor ctrl ctx cuaz cube cup curation curdate cure curly curren
currency current currently currval cursor cursors curtime curtimestamp curve
curves custom customer customerid customers cut cvs cwd cycle cyclic daemon
curves custom customizable customer customerid customers cut cvs cwd cycle cyclic daemon
daffodil dagger damage damages dangerous darr darwin dash dashes dat data
database databaseaccess databases datalink datanamic datapage datasource
datastore datatype datatypes date dateadd datediff dates datestyle datetime
datetimes datum david day dayname dayofmonth dayofweek dayofyear days dba dbcopy
datetimes datum david davide day dayname dayofmonth dayofweek dayofyear days dba dbcopy
dbcopyplugin dbcp dbev dbid dbmonster dbms dbname dbo dbs dbserv ddl ddlutils
deadlock deadlocks deal dealing deallocate death debug debugging dec december
decimal deck declaration declaratory declare declared decode decoder decodes
......@@ -171,7 +171,7 @@ floating floor florent flower fluent flush flushed flushes flushing fly focus
focusable folder follow followed following follows font foo footer footprint for
forall force forcefully forces forcing foreign forever forge forget forgotten
form format formatdatetime formats formatted formatting formed forms forth
forward found foundation four fourth fowler fox frac fractional frame frameborder
forward found foundation four fourth fowler fox fqn frac fractional frame frameborder
frames frameset framespacing framework frameworks fran france frank frasl free
freed french frequently fresh freshmeat friendly from front frontend fsutil fsync
ftl ftp ftps fukushima fulfill fulfilled fulfils full fulltext fully fulvio fun
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论