|
1 | 1 | package me.zort.sqllib; |
2 | 2 |
|
3 | 3 | import com.google.gson.Gson; |
4 | | -import lombok.AllArgsConstructor; |
5 | | -import lombok.Data; |
6 | | -import lombok.Getter; |
7 | | -import lombok.RequiredArgsConstructor; |
| 4 | +import lombok.*; |
| 5 | +import me.zort.sqllib.api.ObjectMapper; |
8 | 6 | import me.zort.sqllib.api.Query; |
9 | 7 | import me.zort.sqllib.api.StatementFactory; |
10 | 8 | import me.zort.sqllib.api.data.QueryResult; |
|
14 | 12 | import me.zort.sqllib.internal.Defaults; |
15 | 13 | import me.zort.sqllib.internal.annotation.JsonField; |
16 | 14 | import me.zort.sqllib.internal.factory.SQLConnectionFactory; |
| 15 | +import me.zort.sqllib.internal.fieldResolver.ConstructorParameterResolver; |
17 | 16 | import me.zort.sqllib.internal.fieldResolver.LinkedOneFieldResolver; |
18 | 17 | import me.zort.sqllib.internal.impl.DefaultNamingStrategy; |
| 18 | +import me.zort.sqllib.internal.impl.DefaultObjectMapper; |
19 | 19 | import me.zort.sqllib.internal.impl.QueryResultImpl; |
20 | 20 | import me.zort.sqllib.internal.query.*; |
21 | 21 | import me.zort.sqllib.internal.query.part.SetStatement; |
|
37 | 37 | */ |
38 | 38 | public class SQLDatabaseConnectionImpl extends SQLDatabaseConnection { |
39 | 39 |
|
| 40 | + // --***-- Default Constants --***-- |
| 41 | + |
40 | 42 | public static boolean DEFAULT_AUTO_RECONNECT = true; |
41 | 43 | public static boolean DEFAULT_DEBUG = false; |
42 | 44 | public static boolean DEFAULT_LOG_SQL_ERRORS = true; |
43 | 45 | public static NamingStrategy DEFAULT_NAMING_STRATEGY = new DefaultNamingStrategy(); |
44 | 46 | public static Gson DEFAULT_GSON = Defaults.DEFAULT_GSON; |
45 | 47 |
|
| 48 | + // --***-- Options & Utilities --***-- |
| 49 | + |
46 | 50 | @Getter |
47 | 51 | private final SQLDatabaseOptions options; |
48 | | - // Resolvers used after no value is found for the field |
49 | | - // in mapped object as backup. |
50 | | - private final List<FieldValueResolver> backupValueResolvers; |
| 52 | + private transient ObjectMapper objectMapper; |
51 | 53 |
|
52 | 54 | /** |
53 | 55 | * Constructs new instance of this implementation with default |
@@ -78,16 +80,21 @@ public SQLDatabaseConnectionImpl(SQLConnectionFactory connectionFactory, @Nullab |
78 | 80 | ); |
79 | 81 |
|
80 | 82 | this.options = options; |
81 | | - this.backupValueResolvers = Collections.synchronizedList(new ArrayList<>()); |
| 83 | + this.objectMapper = new DefaultObjectMapper(this); |
82 | 84 |
|
83 | 85 | // Default backup value resolvers. |
84 | 86 | registerBackupValueResolver(new LinkedOneFieldResolver()); |
| 87 | + registerBackupValueResolver(new ConstructorParameterResolver()); |
85 | 88 | } |
86 | 89 |
|
87 | | - public void registerBackupValueResolver(@NotNull FieldValueResolver resolver) { |
| 90 | + public void registerBackupValueResolver(@NotNull ObjectMapper.FieldValueResolver resolver) { |
88 | 91 | Objects.requireNonNull(resolver, "Resolver cannot be null!"); |
89 | 92 |
|
90 | | - backupValueResolvers.add(resolver); |
| 93 | + objectMapper.registerBackupValueResolver(resolver); |
| 94 | + } |
| 95 | + |
| 96 | + public void setObjectMapper(@NotNull ObjectMapper objectMapper) { |
| 97 | + this.objectMapper = Objects.requireNonNull(objectMapper, "Object mapper cannot be null!"); |
91 | 98 | } |
92 | 99 |
|
93 | 100 | /** |
@@ -162,7 +169,7 @@ public <T> QueryRowsResult<T> query(Query query, Class<T> typeClass) { |
162 | 169 | QueryRowsResult<Row> resultRows = query(query.getAncestor()); |
163 | 170 | QueryRowsResult<T> result = new QueryRowsResult<>(resultRows.isSuccessful()); |
164 | 171 | for(Row row : resultRows) { |
165 | | - Optional.ofNullable(assignValues(row, typeClass)) |
| 172 | + Optional.ofNullable(objectMapper.assignValues(row, typeClass)) |
166 | 173 | .ifPresent(result::add); |
167 | 174 | } |
168 | 175 | return result; |
@@ -219,95 +226,6 @@ public QueryResult exec(Query query) { |
219 | 226 | } |
220 | 227 | } |
221 | 228 |
|
222 | | - @Nullable |
223 | | - private <T> T assignValues(Row row, Class<T> typeClass) { |
224 | | - T instance = null; |
225 | | - try { |
226 | | - try { |
227 | | - Constructor<T> c = typeClass.getConstructor(); |
228 | | - c.setAccessible(true); |
229 | | - instance = c.newInstance(); |
230 | | - } catch (NoSuchMethodException e) { |
231 | | - for(Constructor<?> c : typeClass.getConstructors()) { |
232 | | - if(c.getParameterCount() == row.size()) { |
233 | | - Parameter[] params = c.getParameters(); |
234 | | - Object[] vals = new Object[c.getParameterCount()]; |
235 | | - for(int i = 0; i < row.size(); i++) { |
236 | | - Parameter param = params[i]; |
237 | | - vals[i] = buildElementValue(param, row); |
238 | | - } |
239 | | - try { |
240 | | - instance = (T) c.newInstance(vals); |
241 | | - } catch(Exception ignored) { |
242 | | - continue; |
243 | | - } |
244 | | - } |
245 | | - } |
246 | | - } |
247 | | - for(Field field : typeClass.getDeclaredFields()) { |
248 | | - |
249 | | - if(Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) { |
250 | | - continue; |
251 | | - } |
252 | | - |
253 | | - try { |
254 | | - field.setAccessible(true); |
255 | | - field.set(instance, buildElementValue(field, row)); |
256 | | - } catch(SecurityException ignored) { |
257 | | - debug(String.format("Field %s on class %s cannot be set accessible!", |
258 | | - field.getName(), |
259 | | - typeClass.getName())); |
260 | | - } catch(Exception ignored) { |
261 | | - continue; |
262 | | - } |
263 | | - } |
264 | | - } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { |
265 | | - debug("Cannot instantinate " + typeClass.getName() + " for assigning attributes from row!"); |
266 | | - e.printStackTrace(); |
267 | | - return null; |
268 | | - } |
269 | | - return instance; |
270 | | - } |
271 | | - |
272 | | - @Nullable |
273 | | - private Object buildElementValue(AnnotatedElement element, Row row) { |
274 | | - String name; |
275 | | - Type type; |
276 | | - if(element instanceof Field) { |
277 | | - name = ((Field) element).getName(); |
278 | | - type = ((Field) element).getGenericType(); |
279 | | - } else if(element instanceof Parameter) { // TODO: Parameter names are arg[a-zA-Z0-9]+, use different strategy. |
280 | | - name = ((Parameter) element).getName(); |
281 | | - type = ((Parameter) element).getType(); |
282 | | - } else { |
283 | | - return null; |
284 | | - } |
285 | | - Object obj = row.get(name); |
286 | | - if(obj == null) { |
287 | | - String converted; |
288 | | - if((obj = row.get(converted = options.getNamingStrategy().fieldNameToColumn(name))) == null) { |
289 | | - |
290 | | - // Now backup resolvers come. |
291 | | - for(FieldValueResolver resolver : backupValueResolvers) { |
292 | | - Object backupValue = resolver.obtainValue(this, element, row, name, converted, type); |
293 | | - if(backupValue != null) { |
294 | | - return backupValue; |
295 | | - } |
296 | | - } |
297 | | - |
298 | | - debug(String.format("Cannot find column for target %s (%s)", name, converted)); |
299 | | - return null; |
300 | | - } |
301 | | - } |
302 | | - if(element.isAnnotationPresent(JsonField.class) && obj instanceof String) { |
303 | | - String jsonString = (String) obj; |
304 | | - Gson gson = options.getGson(); |
305 | | - return gson.fromJson(jsonString, type); |
306 | | - } else { |
307 | | - return obj; |
308 | | - } |
309 | | - } |
310 | | - |
311 | 229 | private boolean handleAutoReconnect() { |
312 | 230 | if(options.isAutoReconnect() && !isConnected()) { |
313 | 231 | debug("Trying to make a new connection with the database!"); |
@@ -390,15 +308,6 @@ public PreparedStatement prepare(Connection connection) throws SQLException { |
390 | 308 | } |
391 | 309 | } |
392 | 310 |
|
393 | | - public interface FieldValueResolver { |
394 | | - Object obtainValue(SQLDatabaseConnectionImpl connection, |
395 | | - AnnotatedElement element, |
396 | | - Row row, |
397 | | - String fieldName, |
398 | | - String convertedName, |
399 | | - Type type); |
400 | | - } |
401 | | - |
402 | 311 | @AllArgsConstructor |
403 | 312 | @Data |
404 | 313 | public static class UnknownValueWrapper { |
|
0 commit comments