not serialized ValueJavaObject

上级 8ff160f7
......@@ -366,6 +366,32 @@ public class SysProperties {
public static final boolean USE_THREAD_CONTEXT_CLASS_LOADER =
Utils.getProperty("h2.useThreadContextClassLoader", false);
/**
* System property <code>h2.serializeJavaObject</code>
* (default: true).<br />
* <b>If true</b>, values of type OTHER will be stored in serialized form
* and have the semantics of binary data for all operations (such as sorting and conversion to string).
* <br />
* <b>If false</b>, the objects will be serialized only for I/O operations and few other special cases
* (for example when someone tries to get the value in binary form or when comparing objects
* other way than in binary form is impossible).
* <br />
* If the object implements the Comparable interface, the method compareTo
* will be used for sorting (but only if objects being compared have a common comparable
* super type). Otherwise the objects will be compared by types, and if they are the same by hashCode, and
* if the hash codes are equal, but objects are not, the serialized forms (the byte arrays) are compared.
* <br />
* The string representation of the values use the toString method of object.
* <br />
* In client-server mode, the server must have all required classes in the class path.
* On the client side, this setting is required to be disabled as well, to have correct string representation
* and display size.
* <br />
* In embedded mode, no data copying occurs, so the user has to make defensive copy himself before storing,
* to make sure that value object is immutable and will not be modified later.
*/
public static final boolean SERIALIZE_JAVA_OBJECT = Utils.getProperty("h2.serializeJavaObject", true);
private static final String H2_BASE_DIR = "h2.baseDir";
private SysProperties() {
......
......@@ -1756,8 +1756,10 @@ public class JdbcConnection extends TraceObject implements Connection {
break;
}
case Value.JAVA_OBJECT:
o = Utils.deserialize(v.getBytesNoCopy());
break;
if (SysProperties.SERIALIZE_JAVA_OBJECT) {
o = Utils.deserialize(v.getBytesNoCopy());
break;
}
default:
o = v.getObject();
}
......
......@@ -759,7 +759,7 @@ public class Data {
int len = readVarInt();
byte[] b = Utils.newBytes(len);
read(b, 0, len);
return ValueJavaObject.getNoCopy(b);
return ValueJavaObject.getNoCopy(null, b);
}
case Value.UUID:
return ValueUuid.get(readLong(), readLong());
......
......@@ -237,7 +237,7 @@ public class Utils {
}
int len = b.length;
if (len == 0) {
return b;
return EMPTY_BYTES;
}
byte[] copy = new byte[len];
System.arraycopy(b, 0, copy, 0, len);
......@@ -403,6 +403,28 @@ public class Utils {
return new long[len];
}
/**
* Checks if given classes have common Comparable superclass.
*/
public static boolean haveCommonComparableSuperclass(Class<?> c1, Class<?> c2) {
if (c1 == c2 || c1.isAssignableFrom(c2) || c2.isAssignableFrom(c1)) {
return true;
}
Class<?> top1;
do {
top1 = c1;
c1 = c1.getSuperclass();
} while (Comparable.class.isAssignableFrom(c1));
Class<?> top2;
do {
top2 = c2;
c2 = c2.getSuperclass();
} while (Comparable.class.isAssignableFrom(c2));
return top1 == top2;
}
/**
* Load a class, but check if it is allowed to load this class first. To
* perform access rights checking, the system property h2.allowedClasses
......
......@@ -23,6 +23,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.UUID;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.engine.SessionInterface;
import org.h2.jdbc.JdbcBlob;
import org.h2.jdbc.JdbcClob;
......@@ -577,8 +578,13 @@ public class DataType {
break;
}
case Value.JAVA_OBJECT: {
byte[] buff = rs.getBytes(columnIndex);
v = buff == null ? (Value) ValueNull.INSTANCE : ValueJavaObject.getNoCopy(buff);
if (SysProperties.SERIALIZE_JAVA_OBJECT) {
byte[] buff = rs.getBytes(columnIndex);
v = buff == null ? ValueNull.INSTANCE : ValueJavaObject.getNoCopy(null, buff);
} else {
Object o = rs.getObject(columnIndex);
v = o == null ? ValueNull.INSTANCE : ValueJavaObject.getNoCopy(o, null);
}
break;
}
case Value.ARRAY: {
......@@ -861,7 +867,7 @@ public class DataType {
return ValueNull.INSTANCE;
}
if (type == Value.JAVA_OBJECT) {
return ValueJavaObject.getNoCopy(Utils.serialize(x));
return ValueJavaObject.getNoCopy(x, null);
}
if (x instanceof String) {
return ValueString.get((String) x);
......@@ -932,7 +938,7 @@ public class DataType {
} else if (x instanceof Character) {
return ValueStringFixed.get(((Character) x).toString());
} else {
return ValueJavaObject.getNoCopy(Utils.serialize(x));
return ValueJavaObject.getNoCopy(x, null);
}
}
......@@ -1059,7 +1065,7 @@ public class DataType {
return new JdbcClob(conn, v, 0);
}
if (v.getType() == Value.JAVA_OBJECT) {
Object o = Utils.deserialize(v.getBytes());
Object o = SysProperties.SERIALIZE_JAVA_OBJECT ? Utils.deserialize(v.getBytes()) : v.getObject();
if (paramClass.isAssignableFrom(o.getClass())) {
return o;
}
......
......@@ -527,7 +527,7 @@ public class Transfer {
case Value.UUID:
return ValueUuid.get(readLong(), readLong());
case Value.JAVA_OBJECT:
return ValueJavaObject.getNoCopy(readBytes());
return ValueJavaObject.getNoCopy(null, readBytes());
case Value.BOOLEAN:
return ValueBoolean.get(readBoolean());
case Value.BYTE:
......
......@@ -761,7 +761,7 @@ public abstract class Value {
switch(getType()) {
case BYTES:
case BLOB:
return ValueJavaObject.getNoCopy(getBytesNoCopy());
return ValueJavaObject.getNoCopy(null, getBytesNoCopy());
}
break;
}
......@@ -820,7 +820,7 @@ public abstract class Value {
case BYTES:
return ValueBytes.getNoCopy(StringUtils.convertHexToBytes(s.trim()));
case JAVA_OBJECT:
return ValueJavaObject.getNoCopy(StringUtils.convertHexToBytes(s.trim()));
return ValueJavaObject.getNoCopy(null, StringUtils.convertHexToBytes(s.trim()));
case STRING:
return ValueString.get(s);
case STRING_IGNORECASE:
......
......@@ -21,8 +21,8 @@ public class ValueBytes extends Value {
private static final ValueBytes EMPTY = new ValueBytes(Utils.EMPTY_BYTES);
private final byte[] value;
private int hash;
protected byte[] value;
protected int hash;
protected ValueBytes(byte[] v) {
this.value = v;
......@@ -66,7 +66,7 @@ public class ValueBytes extends Value {
}
public String getSQL() {
return "X'" + getString() + "'";
return "X'" + StringUtils.convertBytesToHex(getBytesNoCopy()) + "'";
}
public byte[] getBytesNoCopy() {
......@@ -74,7 +74,7 @@ public class ValueBytes extends Value {
}
public byte[] getBytes() {
return Utils.cloneByteArray(value);
return Utils.cloneByteArray(getBytesNoCopy());
}
protected int compareSecure(Value v, CompareMode mode) {
......
......@@ -28,15 +28,24 @@ public class ValueJavaObject extends ValueBytes {
* Get or create a java object value for the given byte array.
* Do not clone the data.
*
* @param javaObject the object
* @param b the byte array
* @return the value
*/
public static ValueJavaObject getNoCopy(byte[] b) {
if (b.length == 0) {
public static ValueJavaObject getNoCopy(Object javaObject, byte[] b) {
if (b != null && b.length == 0) {
return EMPTY;
}
ValueJavaObject obj = new ValueJavaObject(b);
if (b.length > SysProperties.OBJECT_CACHE_MAX_PER_ELEMENT_SIZE) {
ValueJavaObject obj;
if (SysProperties.SERIALIZE_JAVA_OBJECT) {
if (b == null) {
b = Utils.serialize(javaObject);
}
obj = new ValueJavaObject(b);
} else {
obj = new NotSerialized(javaObject, b);
}
if (b == null || b.length > SysProperties.OBJECT_CACHE_MAX_PER_ELEMENT_SIZE) {
return obj;
}
return (ValueJavaObject) Value.cache(obj);
......@@ -51,4 +60,133 @@ public class ValueJavaObject extends ValueBytes {
prep.setObject(parameterIndex, obj, Types.JAVA_OBJECT);
}
/**
* Value which serializes java object only for I/O operations.
* Used when property {@link SysProperties#SERIALIZE_JAVA_OBJECT} is disabled.
*
* @author Sergi Vladykin
*/
private static class NotSerialized extends ValueJavaObject {
private Object javaObject;
private int displaySize = -1;
NotSerialized(Object javaObject, byte[] v) {
super(v);
this.javaObject = javaObject;
}
@Override
public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
prep.setObject(parameterIndex, getObject(), Types.JAVA_OBJECT);
}
@Override
public byte[] getBytesNoCopy() {
if (value == null) {
value = Utils.serialize(javaObject);
}
return value;
}
@Override
protected int compareSecure(Value v, CompareMode mode) {
Object o1 = getObject();
Object o2 = v.getObject();
boolean o1Comparable = o1 instanceof Comparable;
boolean o2Comparable = o2 instanceof Comparable;
if (o1Comparable && o2Comparable &&
Utils.haveCommonComparableSuperclass(o1.getClass(), o2.getClass())) {
return ((Comparable) o1).compareTo(o2);
}
// group by types
if (o1.getClass() != o2.getClass()) {
if (o1Comparable != o2Comparable) {
return o1Comparable ? -1 : 1;
}
return o1.getClass().getName().compareTo(o2.getClass().getName());
}
// compare hash codes
int h1 = hashCode();
int h2 = v.hashCode();
if (h1 == h2) {
if (o1.equals(o2)) {
return 0;
}
return Utils.compareNotNull(getBytesNoCopy(), v.getBytesNoCopy());
}
return h1 > h2 ? 1 : -1;
}
@Override
public String getString() {
String str = getObject().toString();
if (displaySize == -1) {
displaySize = str.length();
}
return str;
}
@Override
public long getPrecision() {
return 0;
}
@Override
public int hashCode() {
if (hash == 0) {
hash = getObject().hashCode();
}
return hash;
}
@Override
public Object getObject() {
if (javaObject == null) {
javaObject = Utils.deserialize(value);
}
return javaObject;
}
@Override
public int getDisplaySize() {
if (displaySize == -1) {
displaySize = getString().length();
}
return displaySize;
}
@Override
public int getMemory() {
if (value == null) {
return DataType.getDataType(getType()).memory;
}
int mem = super.getMemory();
if (javaObject != null) {
mem *= 2;
}
return mem;
}
@Override
public boolean equals(Object other) {
if (!(other instanceof NotSerialized)) {
return false;
}
return getObject().equals(((NotSerialized) other).getObject());
}
@Override
public Value convertPrecision(long precision, boolean force) {
return this;
}
}
}
/*
* Copyright 2004-2011 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.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Arrays;
import java.util.UUID;
import org.h2.test.TestAll;
import org.h2.test.TestBase;
/**
* Tests java object values when {@link SysProperties#SERIALIZE_JAVA_OBJECT} property is disabled.
*
* @author Sergi Vladykin
*/
public class TestJavaObject extends TestBase {
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
System.setProperty("h2.serializeJavaObject", "false");
TestAll conf = new TestAll();
conf.traceTest = true;
conf.memory = true;
// conf.networked = true;
TestBase.createCaller().init(conf).test();
}
@Override
public void test() throws Exception {
trace("Test Java Object");
startServerIfRequired();
doTest(Arrays.asList(UUID.randomUUID(), null), Arrays.asList(UUID.randomUUID(), UUID.randomUUID()), true);
doTest(new Timestamp(System.currentTimeMillis()), new Timestamp(System.currentTimeMillis() + 10000), false);
doTest(200, 100, false);
doTest(200, 100L, true);
doTest(new Date(System.currentTimeMillis() + 1000), new Date(System.currentTimeMillis()), false);
doTest(new java.util.Date(System.currentTimeMillis() + 1000), new java.util.Date(System.currentTimeMillis()), false);
doTest(new Time(System.currentTimeMillis() + 1000), new Date(System.currentTimeMillis()), false);
doTest(new Time(System.currentTimeMillis() + 1000), new Timestamp(System.currentTimeMillis()), false);
}
private void doTest(Object o1, Object o2, boolean hash) throws SQLException {
deleteDb("javaObject");
Connection conn = getConnection("javaObject");
Statement stmt = conn.createStatement();
stmt.execute("create table t(id identity, val other)");
PreparedStatement ins = conn.prepareStatement("insert into t(val) values(?)");
ins.setObject(1, o1, Types.JAVA_OBJECT);
assertEquals(1, ins.executeUpdate());
ins.setObject(1, o2, Types.JAVA_OBJECT);
assertEquals(1, ins.executeUpdate());
ResultSet rs = stmt.executeQuery("select val from t order by val limit 1");
assertTrue(rs.next());
Object x;
if (hash) {
if (o1.getClass() != o2.getClass()) {
x = o1.getClass().getName().compareTo(o2.getClass().getName()) < 0 ? o1 : o2;
} else {
assertFalse(o1.hashCode() == o2.hashCode());
x = o1.hashCode() < o2.hashCode() ? o1 : o2;
}
} else {
int cmp = ((Comparable) o1).compareTo(o2);
assertFalse(cmp == 0);
x = cmp < 0 ? o1 : o2;
}
assertEquals(x.toString(), rs.getString(1));
Object y = rs.getObject(1);
assertTrue(x.equals(y));
assertFalse(rs.next());
rs.close();
PreparedStatement slct = conn.prepareStatement("select id from t where val = ?");
slct.setObject(1, o1, Types.JAVA_OBJECT);
rs = slct.executeQuery();
assertTrue(rs.next());
assertEquals(1, rs.getInt(1));
assertFalse(rs.next());
rs.close();
slct.setObject(1, o2, Types.JAVA_OBJECT);
rs = slct.executeQuery();
assertTrue(rs.next());
assertEquals(2, rs.getInt(1));
assertFalse(rs.next());
rs.close();
stmt.close();
slct.close();
conn.close();
deleteDb("javaObject");
trace("ok: " + o1.getClass().getName() + " vs " + o2.getClass().getName());
}
}
......@@ -166,8 +166,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(new byte[0]));
testValue(ValueJavaObject.getNoCopy(new byte[100]));
testValue(ValueJavaObject.getNoCopy(null, new byte[0]));
testValue(ValueJavaObject.getNoCopy(null, new byte[100]));
for (int i = 0; i < 300; i++) {
testValue(ValueBytes.getNoCopy(new byte[i]));
}
......
......@@ -8,6 +8,9 @@ package org.h2.test.unit;
import java.io.File;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.Random;
import org.h2.test.TestBase;
import org.h2.util.Utils;
......@@ -101,6 +104,12 @@ public class TestUtils extends TestBase {
Utils.callStaticMethod("java.lang.String.valueOf", "a");
Utils.callStaticMethod("java.awt.AWTKeyStroke.getAWTKeyStroke",
'x', java.awt.event.InputEvent.SHIFT_DOWN_MASK);
// Common comparable superclass
assertFalse(Utils.haveCommonComparableSuperclass(Integer.class, Long.class));
assertTrue(Utils.haveCommonComparableSuperclass(Integer.class, Integer.class));
assertTrue(Utils.haveCommonComparableSuperclass(Timestamp.class, Date.class));
assertFalse(Utils.haveCommonComparableSuperclass(ArrayList.class, Long.class));
assertFalse(Utils.haveCommonComparableSuperclass(Integer.class, ArrayList.class));
}
}
......@@ -180,7 +180,7 @@ public class TestValueMemory extends TestBase implements DataHandler {
case Value.RESULT_SET:
return ValueResultSet.get(new SimpleResultSet());
case Value.JAVA_OBJECT:
return ValueJavaObject.getNoCopy(randomBytes(random.nextInt(100)));
return ValueJavaObject.getNoCopy(null, randomBytes(random.nextInt(100)));
case Value.UUID:
return ValueUuid.get(random.nextLong(), random.nextLong());
case Value.STRING_FIXED:
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论