1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.jadira.usertype.spi.shared;
17
18 import static org.jadira.usertype.spi.utils.reflection.ArrayUtils.copyOf;
19
20 import java.io.Serializable;
21 import java.sql.PreparedStatement;
22 import java.sql.ResultSet;
23 import java.sql.SQLException;
24 import java.util.HashMap;
25 import java.util.Map;
26
27 import org.hibernate.HibernateException;
28 import org.hibernate.engine.spi.SharedSessionContractImplementor;
29 import org.hibernate.type.Type;
30 import org.hibernate.usertype.CompositeUserType;
31 import org.jadira.usertype.spi.utils.reflection.TypeHelper;
32
33 public abstract class AbstractMultiColumnUserType<T> extends AbstractUserType implements CompositeUserType, Serializable {
34
35 private static final long serialVersionUID = -8258683760413283329L;
36
37 private int[] sqlTypes;
38
39 private Type[] hibernateTypes;
40
41
42 private String[] defaultPropertyNames;
43
44 public AbstractMultiColumnUserType() {
45
46 initialise();
47 }
48
49 protected final void initialise() {
50
51 initialiseSqlTypes();
52 initialiseHibernateTypes();
53 initialiseDefaultPropertyNames();
54 }
55
56 private void initialiseDefaultPropertyNames() {
57 Map<String, Integer> nameCount = new HashMap<String, Integer>();
58
59 defaultPropertyNames = new String[getColumnMappers().length];
60 for (int i = 0; i < defaultPropertyNames.length; i++) {
61 String className = hibernateTypes[i].getClass().getSimpleName();
62 if (className.endsWith("Type")) {
63 className = className.substring(0, className.length() - 4);
64 }
65
66 String name = className.toLowerCase();
67 final Integer count;
68 if (nameCount.containsKey(name)) {
69 Integer oldCount = nameCount.get(name);
70 count = oldCount.intValue() + 1;
71 defaultPropertyNames[i] = name + count;
72 } else {
73 count = 1;
74 defaultPropertyNames[i] = name;
75 }
76 nameCount.put(name, count);
77 }
78 }
79
80 @SuppressWarnings({ "unchecked", "rawtypes" })
81 private void initialiseHibernateTypes() {
82 hibernateTypes = new Type[getColumnMappers().length];
83 for (int i = 0; i < hibernateTypes.length; i++) {
84 hibernateTypes[i] = new ColumnMapperSingleColumnTypeAdapter(getColumnMappers()[i]);
85 }
86 }
87
88 private void initialiseSqlTypes() {
89 sqlTypes = new int[getColumnMappers().length];
90 for (int i = 0; i < sqlTypes.length; i++) {
91 sqlTypes[i] = getColumnMappers()[i].getSqlType();
92 }
93 }
94
95 public int[] sqlTypes() {
96 return copyOf(sqlTypes);
97 }
98
99 @SuppressWarnings("unchecked")
100 @Override
101 public Class<T> returnedClass() {
102 return (Class<T>) TypeHelper.getTypeArguments(AbstractMultiColumnUserType.class, getClass()).get(0);
103 }
104
105 protected abstract ColumnMapper<?, ?>[] getColumnMappers();
106
107 @SuppressWarnings({ "rawtypes", "unchecked" })
108 @Override
109 public T nullSafeGet(ResultSet resultSet, String[] strings, SharedSessionContractImplementor session, Object object) throws SQLException {
110
111 beforeNullSafeOperation(session);
112
113 final SharedSessionContractImplementor mySession = doWrapSession(session);
114
115 try {
116 Object[] convertedColumns = new Object[getColumnMappers().length];
117
118 for (int getIndex = 0; getIndex < getColumnMappers().length; getIndex++) {
119 ColumnMapper nextMapper = getColumnMappers()[getIndex];
120
121 final Object converted = nextMapper.getHibernateType().nullSafeGet(resultSet, strings[getIndex], mySession, object);
122
123 if (converted != null) {
124 convertedColumns[getIndex] = nextMapper.fromNonNullValue(converted);
125 }
126 }
127
128 for (int i = 0; i < convertedColumns.length; i++) {
129 if (convertedColumns[i] != null) {
130 return fromConvertedColumns(convertedColumns);
131 }
132 }
133
134 return null;
135
136 } finally {
137 afterNullSafeOperation(session);
138 }
139 }
140
141 protected abstract T fromConvertedColumns(Object[] convertedColumns);
142
143 protected abstract Object[] toConvertedColumns(T value);
144
145 @SuppressWarnings("unchecked")
146 @Override
147 public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index, SharedSessionContractImplementor session) throws SQLException {
148
149 beforeNullSafeOperation(session);
150
151 final SharedSessionContractImplementor mySession = doWrapSession(session);
152
153 try {
154 final Object[] valuesToSet = new Object[getColumnMappers().length];
155
156 if (value != null) {
157
158 final T myValue = (T) value;
159 Object[] convertedColumns = toConvertedColumns(myValue);
160
161 for (int cIdx = 0; cIdx < valuesToSet.length; cIdx++) {
162
163 @SuppressWarnings("rawtypes") ColumnMapper nextMapper = getColumnMappers()[cIdx];
164 valuesToSet[cIdx] = nextMapper.toNonNullValue(convertedColumns[cIdx]);
165 }
166 }
167
168 for (int setIndex = 0; setIndex < valuesToSet.length; setIndex++) {
169
170 @SuppressWarnings("rawtypes") ColumnMapper nextMapper = getColumnMappers()[setIndex];
171
172
173 nextMapper.getHibernateType().nullSafeSet(preparedStatement, valuesToSet[setIndex], index + setIndex, mySession);
174 }
175
176 } finally {
177 afterNullSafeOperation(session);
178 }
179 }
180
181 protected SharedSessionContractImplementor doWrapSession(SharedSessionContractImplementor session) {
182 return session;
183 }
184
185 @Override
186 public String[] getPropertyNames() {
187 return defaultPropertyNames;
188 }
189
190 @Override
191 public Type[] getPropertyTypes() {
192 return copyOf(hibernateTypes);
193 }
194
195 @Override
196 public Object getPropertyValue(Object component, int property) throws HibernateException {
197
198 if (!returnedClass().isAssignableFrom(component.getClass())) {
199 throw new HibernateException("getPropertyValue called with incorrect class: {" + component.getClass() + "}");
200 }
201 @SuppressWarnings("unchecked") Object[] cols = toConvertedColumns((T) component);
202 return cols[property];
203 }
204
205 @Override
206 public void setPropertyValue(Object component, int property, Object value) throws HibernateException {
207 throw new HibernateException("Called setPropertyValue on an immutable type {" + component.getClass() + "}");
208 }
209
210 @Override
211 public Serializable disassemble(Object value, SharedSessionContractImplementor session) throws HibernateException {
212 return super.disassemble(value);
213 }
214
215 @Override
216 public Object assemble(Serializable cached, SharedSessionContractImplementor session, Object owner) throws HibernateException {
217 return super.assemble(cached, owner);
218 }
219
220 @Override
221 public Object replace(Object original, Object target, SharedSessionContractImplementor session, Object owner) throws HibernateException {
222 return super.replace(original, target, owner);
223 }
224 }