1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.value; |
7 | |
8 | import java.sql.PreparedStatement; |
9 | import java.sql.SQLException; |
10 | import java.sql.Types; |
11 | |
12 | import org.h2.engine.SysProperties; |
13 | import org.h2.store.DataHandler; |
14 | import org.h2.util.JdbcUtils; |
15 | import org.h2.util.Utils; |
16 | |
17 | /** |
18 | * Implementation of the OBJECT data type. |
19 | */ |
20 | public class ValueJavaObject extends ValueBytes { |
21 | |
22 | private static final ValueJavaObject EMPTY = |
23 | new ValueJavaObject(Utils.EMPTY_BYTES, null); |
24 | private final DataHandler dataHandler; |
25 | |
26 | protected ValueJavaObject(byte[] v, DataHandler dataHandler) { |
27 | super(v); |
28 | this.dataHandler = dataHandler; |
29 | } |
30 | |
31 | /** |
32 | * Get or create a java object value for the given byte array. |
33 | * Do not clone the data. |
34 | * |
35 | * @param javaObject the object |
36 | * @param b the byte array |
37 | * @param dataHandler provides the object serializer |
38 | * @return the value |
39 | */ |
40 | public static ValueJavaObject getNoCopy(Object javaObject, byte[] b, |
41 | DataHandler dataHandler) { |
42 | if (b != null && b.length == 0) { |
43 | return EMPTY; |
44 | } |
45 | ValueJavaObject obj; |
46 | if (SysProperties.serializeJavaObject) { |
47 | if (b == null) { |
48 | b = JdbcUtils.serialize(javaObject, dataHandler); |
49 | } |
50 | obj = new ValueJavaObject(b, dataHandler); |
51 | } else { |
52 | obj = new NotSerialized(javaObject, b, dataHandler); |
53 | } |
54 | if (b == null || b.length > SysProperties.OBJECT_CACHE_MAX_PER_ELEMENT_SIZE) { |
55 | return obj; |
56 | } |
57 | return (ValueJavaObject) Value.cache(obj); |
58 | } |
59 | |
60 | @Override |
61 | public int getType() { |
62 | return Value.JAVA_OBJECT; |
63 | } |
64 | |
65 | @Override |
66 | public void set(PreparedStatement prep, int parameterIndex) |
67 | throws SQLException { |
68 | Object obj = JdbcUtils.deserialize(getBytesNoCopy(), getDataHandler()); |
69 | prep.setObject(parameterIndex, obj, Types.JAVA_OBJECT); |
70 | } |
71 | |
72 | /** |
73 | * Value which serializes java object only for I/O operations. |
74 | * Used when property {@link SysProperties#serializeJavaObject} is disabled. |
75 | * |
76 | * @author Sergi Vladykin |
77 | */ |
78 | private static class NotSerialized extends ValueJavaObject { |
79 | |
80 | private Object javaObject; |
81 | |
82 | private int displaySize = -1; |
83 | |
84 | NotSerialized(Object javaObject, byte[] v, DataHandler dataHandler) { |
85 | super(v, dataHandler); |
86 | this.javaObject = javaObject; |
87 | } |
88 | |
89 | @Override |
90 | public void set(PreparedStatement prep, int parameterIndex) |
91 | throws SQLException { |
92 | prep.setObject(parameterIndex, getObject(), Types.JAVA_OBJECT); |
93 | } |
94 | |
95 | @Override |
96 | public byte[] getBytesNoCopy() { |
97 | if (value == null) { |
98 | value = JdbcUtils.serialize(javaObject, null); |
99 | } |
100 | return value; |
101 | } |
102 | |
103 | @Override |
104 | protected int compareSecure(Value v, CompareMode mode) { |
105 | Object o1 = getObject(); |
106 | Object o2 = v.getObject(); |
107 | |
108 | boolean o1Comparable = o1 instanceof Comparable; |
109 | boolean o2Comparable = o2 instanceof Comparable; |
110 | |
111 | if (o1Comparable && o2Comparable && |
112 | Utils.haveCommonComparableSuperclass(o1.getClass(), o2.getClass())) { |
113 | @SuppressWarnings("unchecked") |
114 | Comparable<Object> c1 = (Comparable<Object>) o1; |
115 | return c1.compareTo(o2); |
116 | } |
117 | |
118 | // group by types |
119 | if (o1.getClass() != o2.getClass()) { |
120 | if (o1Comparable != o2Comparable) { |
121 | return o1Comparable ? -1 : 1; |
122 | } |
123 | return o1.getClass().getName().compareTo(o2.getClass().getName()); |
124 | } |
125 | |
126 | // compare hash codes |
127 | int h1 = hashCode(); |
128 | int h2 = v.hashCode(); |
129 | |
130 | if (h1 == h2) { |
131 | if (o1.equals(o2)) { |
132 | return 0; |
133 | } |
134 | |
135 | return Utils.compareNotNullSigned(getBytesNoCopy(), v.getBytesNoCopy()); |
136 | } |
137 | |
138 | return h1 > h2 ? 1 : -1; |
139 | } |
140 | |
141 | @Override |
142 | public String getString() { |
143 | String str = getObject().toString(); |
144 | if (displaySize == -1) { |
145 | displaySize = str.length(); |
146 | } |
147 | return str; |
148 | } |
149 | |
150 | @Override |
151 | public long getPrecision() { |
152 | return 0; |
153 | } |
154 | |
155 | @Override |
156 | public int hashCode() { |
157 | if (hash == 0) { |
158 | hash = getObject().hashCode(); |
159 | } |
160 | return hash; |
161 | } |
162 | |
163 | @Override |
164 | public Object getObject() { |
165 | if (javaObject == null) { |
166 | javaObject = JdbcUtils.deserialize(value, getDataHandler()); |
167 | } |
168 | return javaObject; |
169 | } |
170 | |
171 | @Override |
172 | public int getDisplaySize() { |
173 | if (displaySize == -1) { |
174 | displaySize = getString().length(); |
175 | } |
176 | return displaySize; |
177 | } |
178 | |
179 | @Override |
180 | public int getMemory() { |
181 | if (value == null) { |
182 | return DataType.getDataType(getType()).memory; |
183 | } |
184 | int mem = super.getMemory(); |
185 | if (javaObject != null) { |
186 | mem *= 2; |
187 | } |
188 | return mem; |
189 | } |
190 | |
191 | @Override |
192 | public boolean equals(Object other) { |
193 | if (!(other instanceof NotSerialized)) { |
194 | return false; |
195 | } |
196 | return getObject().equals(((NotSerialized) other).getObject()); |
197 | } |
198 | |
199 | @Override |
200 | public Value convertPrecision(long precision, boolean force) { |
201 | return this; |
202 | } |
203 | } |
204 | |
205 | @Override |
206 | protected DataHandler getDataHandler() { |
207 | return dataHandler; |
208 | } |
209 | } |