Skip to content

Commit 7a6ea17

Browse files
authored
Merge pull request #45 from ZorTik/development
Development
2 parents 77e0280 + e257c8c commit 7a6ea17

7 files changed

Lines changed: 131 additions & 69 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package me.zort.sqllib.api;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Getter;
5+
6+
import java.util.concurrent.atomic.AtomicReference;
7+
8+
@AllArgsConstructor
9+
@Getter
10+
public class DefsVals {
11+
private final String[] defs;
12+
private final AtomicReference<Object>[] vals;
13+
}

asql-api/src/main/java/me/zort/sqllib/api/ObjectMapper.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44
import org.jetbrains.annotations.NotNull;
55

66
import java.lang.reflect.AnnotatedElement;
7+
import java.lang.reflect.Field;
78
import java.lang.reflect.Type;
89

910
public interface ObjectMapper {
1011

1112
void registerBackupValueResolver(@NotNull FieldValueResolver resolver);
13+
void registerAdapter(@NotNull Class<?> typeClass, @NotNull TypeAdapter<?> adapter);
1214

13-
<T> T assignValues(Row row, Class<T> typeClass);
15+
<T> T deserializeValues(Row row, Class<T> typeClass);
16+
DefsVals serializeValues(Object obj);
1417

1518
interface FieldValueResolver {
1619
Object obtainValue(SQLConnection connection,
@@ -20,4 +23,30 @@ Object obtainValue(SQLConnection connection,
2023
String convertedName,
2124
Type type);
2225
}
26+
27+
interface TypeAdapter<T> { // TODO: Tests
28+
/**
29+
* Deserializes value from database to Java value.
30+
* This value is commonly set to field value in corresponding
31+
* object. Don't set the value to the element manually!
32+
*
33+
* @param element Element to deserialize value for.
34+
* @param row Row to deserialize value from.
35+
* @param raw Raw value to deserialize.
36+
* @return Deserialized value that will be set to the element.
37+
*/
38+
T deserialize(AnnotatedElement element, Row row, Object raw);
39+
40+
/**
41+
* Serializes value to be inserted into database.
42+
*
43+
* @param element Element to serialize value for.
44+
* @param value Value to serialize.
45+
* This value should be suitable to the database type,
46+
* so you should always check if the value corresponds to
47+
* the column type, etc.
48+
* @return The serialized value that will be inserted into database.
49+
*/
50+
Object serialize(AnnotatedElement element, Object value);
51+
}
2352
}

asql-core/src/main/java/me/zort/sqllib/SQLDatabaseConnection.java

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
package me.zort.sqllib;
22

3-
import lombok.AllArgsConstructor;
43
import lombok.Getter;
4+
import me.zort.sqllib.api.DefsVals;
5+
import me.zort.sqllib.api.ObjectMapper;
56
import me.zort.sqllib.api.Query;
67
import me.zort.sqllib.api.SQLConnection;
78
import me.zort.sqllib.api.cache.CacheManager;
89
import me.zort.sqllib.api.data.QueryResult;
910
import me.zort.sqllib.api.data.QueryRowsResult;
1011
import me.zort.sqllib.api.data.Row;
11-
import me.zort.sqllib.api.mapping.StatementMappingFactory;
12-
import me.zort.sqllib.api.mapping.StatementMappingOptions;
1312
import me.zort.sqllib.api.model.SchemaSynchronizer;
1413
import me.zort.sqllib.api.model.TableSchema;
1514
import me.zort.sqllib.api.model.TableSchemaBuilder;
@@ -71,6 +70,15 @@ public SQLDatabaseConnection(final @NotNull SQLConnectionFactory connectionFacto
7170
@ApiStatus.Experimental
7271
public abstract void setSchemaSynchronizer(SchemaSynchronizer<SQLDatabaseConnection> synchronizer);
7372

73+
/**
74+
* Sets the object mapper to use.
75+
* Object mapper maps queries to objects, as specified in {@link SQLDatabaseConnection#query(Query, Class)}.
76+
*
77+
* @param objectMapper Object mapper to use.
78+
*/
79+
public abstract void setObjectMapper(final @NotNull ObjectMapper objectMapper);
80+
public abstract ObjectMapper getObjectMapper();
81+
7482
public abstract boolean buildEntitySchema(String tableName, Class<?> entityClass);
7583

7684
/**
@@ -167,8 +175,6 @@ public SQLDatabaseConnection(final @NotNull SQLConnectionFactory connectionFacto
167175

168176
public abstract boolean isTransactionActive();
169177

170-
protected abstract DefsVals buildDefsVals(Object obj);
171-
172178
public abstract boolean isLogSqlErrors();
173179

174180
public abstract boolean isDebug();
@@ -220,12 +226,14 @@ public SQLDatabaseConnection cacheFor(long millis) {
220226
}
221227

222228
public UpsertQuery save(final @NotNull String table, final @NotNull Object obj) {
223-
if (buildDefsVals(obj) == null) throw new IllegalArgumentException("Cannot create save query! (defsVals == null)");
229+
if (getObjectMapper().serializeValues(obj) == null) {
230+
throw new IllegalArgumentException("Cannot create save query! (defsVals == null)");
231+
}
224232
return save(obj).table(table);
225233
}
226234

227235
public UpsertQuery save(final @NotNull Object obj) {
228-
DefsVals defsVals = buildDefsVals(obj);
236+
DefsVals defsVals = getObjectMapper().serializeValues(obj);
229237
if (defsVals == null) return null;
230238
String[] defs = defsVals.getDefs();
231239
AtomicReference<Object>[] vals = defsVals.getVals();
@@ -242,7 +250,7 @@ public UpsertQuery save(final @NotNull Object obj) {
242250
}
243251

244252
public QueryResult insert(final @NotNull String table, final @NotNull Object obj) {
245-
DefsVals defsVals = buildDefsVals(obj);
253+
DefsVals defsVals = getObjectMapper().serializeValues(obj);
246254
if (defsVals == null) return new QueryResultImpl(false);
247255

248256
InsertQuery query = insert().into(table, defsVals.getDefs());
@@ -343,13 +351,6 @@ public interface CodeObserver {
343351
void onNotified(int code);
344352
}
345353

346-
@AllArgsConstructor
347-
@Getter
348-
protected static class DefsVals {
349-
private final String[] defs;
350-
private final AtomicReference<Object>[] vals;
351-
}
352-
353354
public static final class Code {
354355
public static final int CONNECTED = 1;
355356
public static final int CLOSED = 2;

asql-core/src/main/java/me/zort/sqllib/SQLDatabaseConnectionImpl.java

Lines changed: 3 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import me.zort.sqllib.api.model.TableSchemaBuilder;
1919
import me.zort.sqllib.api.options.NamingStrategy;
2020
import me.zort.sqllib.internal.Defaults;
21-
import me.zort.sqllib.internal.annotation.JsonField;
2221
import me.zort.sqllib.internal.factory.SQLConnectionFactory;
2322
import me.zort.sqllib.internal.fieldResolver.ConstructorParameterResolver;
2423
import me.zort.sqllib.internal.fieldResolver.LinkedOneFieldResolver;
@@ -29,19 +28,13 @@
2928
import me.zort.sqllib.model.builder.EntitySchemaBuilder;
3029
import me.zort.sqllib.pool.PooledSQLDatabaseConnection;
3130
import me.zort.sqllib.transaction.Transaction;
32-
import me.zort.sqllib.util.Validator;
3331
import org.jetbrains.annotations.ApiStatus;
3432
import org.jetbrains.annotations.NotNull;
3533
import org.jetbrains.annotations.Nullable;
3634

37-
import java.lang.reflect.Field;
38-
import java.lang.reflect.Modifier;
3935
import java.sql.*;
40-
import java.util.HashMap;
41-
import java.util.Map;
4236
import java.util.Objects;
4337
import java.util.Optional;
44-
import java.util.concurrent.atomic.AtomicReference;
4538
import java.util.logging.Logger;
4639

4740
/**
@@ -72,6 +65,7 @@ static SQLDatabaseOptions defaultOptions() {
7265

7366
@Getter
7467
private final ISQLDatabaseOptions options;
68+
@Getter
7569
private transient ObjectMapper objectMapper;
7670
private transient CacheManager cacheManager;
7771
@Setter
@@ -130,6 +124,7 @@ public void registerBackupValueResolver(final @NotNull ObjectMapper.FieldValueRe
130124
*
131125
* @param objectMapper Object mapper to use.
132126
*/
127+
@Override
133128
public void setObjectMapper(final @NotNull ObjectMapper objectMapper) {
134129
this.objectMapper = Objects.requireNonNull(objectMapper, "Object mapper cannot be null!");
135130
}
@@ -254,7 +249,7 @@ public <T> QueryRowsResult<T> query(final @NotNull Query query, final @NotNull C
254249
QueryRowsResult<T> result = new QueryRowsResult<>(resultRows.isSuccessful());
255250

256251
for (Row row : resultRows) {
257-
Optional.ofNullable(objectMapper.assignValues(row, typeClass))
252+
Optional.ofNullable(objectMapper.deserializeValues(row, typeClass))
258253
.ifPresent(result::add);
259254
}
260255
return result;
@@ -360,48 +355,6 @@ public QueryResult exec(final @NotNull String query) {
360355
}
361356
}
362357

363-
@SuppressWarnings("unchecked")
364-
@Nullable
365-
protected final DefsVals buildDefsVals(Object obj) {
366-
Objects.requireNonNull(obj);
367-
368-
Class<?> aClass = obj.getClass();
369-
370-
Map<String, Object> fields = new HashMap<>();
371-
for (Field field : aClass.getDeclaredFields()) {
372-
373-
if (Modifier.isTransient(field.getModifiers())) {
374-
// Transient fields are ignored.
375-
continue;
376-
}
377-
378-
try {
379-
field.setAccessible(true);
380-
Object o = field.get(obj);
381-
if (field.isAnnotationPresent(JsonField.class)) {
382-
o = options.getGson().toJson(o);
383-
} else if (Validator.validateAutoIncrement(field) && field.get(obj) == null) {
384-
// If field is PrimaryKey and autoIncrement true and is null,
385-
// We will skip this to use auto increment strategy on SQL server.
386-
continue;
387-
}
388-
fields.put(options.getNamingStrategy().fieldNameToColumn(field.getName()), o);
389-
} catch (IllegalAccessException e) {
390-
e.printStackTrace();
391-
return null;
392-
}
393-
}
394-
// I make entry array for indexing safety.
395-
Map.Entry<String, Object>[] entryArray = fields.entrySet().toArray(new Map.Entry[0]);
396-
String[] defs = new String[entryArray.length];
397-
AtomicReference<Object>[] vals = new AtomicReference[entryArray.length];
398-
for (int i = 0; i < entryArray.length; i++) {
399-
defs[i] = entryArray[i].getKey();
400-
vals[i] = new AtomicReference<>(entryArray[i].getValue());
401-
}
402-
return new DefsVals(defs, vals);
403-
}
404-
405358
@ApiStatus.Experimental
406359
@SneakyThrows(SQLException.class)
407360
public final Transaction beginTransaction() {

asql-core/src/main/java/me/zort/sqllib/SQLiteDatabaseConnection.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package me.zort.sqllib;
22

3+
import me.zort.sqllib.api.DefsVals;
34
import me.zort.sqllib.api.ISQLDatabaseOptions;
45
import me.zort.sqllib.api.Query;
56
import me.zort.sqllib.api.data.QueryResult;
@@ -65,7 +66,7 @@ private void setup() {
6566
@NotNull
6667
@Override
6768
public final UpsertQuery save(@NotNull String table, @NotNull Object obj) {
68-
DefsVals defsVals = buildDefsVals(obj);
69+
DefsVals defsVals = getObjectMapper().serializeValues(obj);
6970
if (defsVals == null) throw new IllegalArgumentException("Cannot create save query! (defsVals == null)");
7071
String[] defs = defsVals.getDefs();
7172
AtomicReference<Object>[] vals = defsVals.getVals();

asql-core/src/main/java/me/zort/sqllib/internal/impl/DefaultObjectMapper.java

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,36 @@
44
import lombok.AccessLevel;
55
import lombok.Getter;
66
import me.zort.sqllib.SQLDatabaseConnectionImpl;
7+
import me.zort.sqllib.api.DefsVals;
8+
import me.zort.sqllib.api.ISQLDatabaseOptions;
79
import me.zort.sqllib.api.ObjectMapper;
810
import me.zort.sqllib.api.data.Row;
911
import me.zort.sqllib.internal.annotation.JsonField;
12+
import me.zort.sqllib.util.Validator;
1013
import org.jetbrains.annotations.NotNull;
1114
import org.jetbrains.annotations.Nullable;
1215

1316
import java.lang.reflect.*;
17+
import java.util.HashMap;
1418
import java.util.List;
19+
import java.util.Map;
20+
import java.util.Objects;
21+
import java.util.concurrent.ConcurrentHashMap;
1522
import java.util.concurrent.CopyOnWriteArrayList;
23+
import java.util.concurrent.atomic.AtomicReference;
1624

1725
public class DefaultObjectMapper implements ObjectMapper {
1826

1927
// Resolvers used after no value is found for the field
2028
// in mapped object as backup.
2129
@Getter(AccessLevel.PROTECTED)
2230
private final List<ObjectMapper.FieldValueResolver> backupValueResolvers;
31+
private final Map<Class<?>, ObjectMapper.TypeAdapter<?>> typeAdapters;
2332
private final SQLDatabaseConnectionImpl connectionWrapper;
2433

2534
public DefaultObjectMapper(SQLDatabaseConnectionImpl connectionWrapper) {
2635
this.backupValueResolvers = new CopyOnWriteArrayList<>();
36+
this.typeAdapters = new ConcurrentHashMap<>();
2737
this.connectionWrapper = connectionWrapper;
2838
}
2939

@@ -32,8 +42,13 @@ public void registerBackupValueResolver(@NotNull FieldValueResolver resolver) {
3242
this.backupValueResolvers.add(resolver);
3343
}
3444

45+
@Override
46+
public void registerAdapter(@NotNull Class<?> typeClass, @NotNull TypeAdapter<?> adapter) {
47+
this.typeAdapters.put(typeClass, adapter);
48+
}
49+
3550
@Nullable
36-
public <T> T assignValues(Row row, Class<T> typeClass) {
51+
public <T> T deserializeValues(Row row, Class<T> typeClass) {
3752
T instance = null;
3853
try {
3954
try {
@@ -113,6 +128,11 @@ private Object buildElementValue(AnnotatedElement element, Row row) {
113128
debug(String.format("Cannot find column for class %s target %s (%s)", declaringClass.getName(), name, converted));
114129
return null;
115130
}
131+
} else {
132+
TypeAdapter<?> typeAdapter = typeAdapters.get(type.getClass());
133+
if (typeAdapter != null) {
134+
return typeAdapter.deserialize(element, row, obj);
135+
}
116136
}
117137
if (element.isAnnotationPresent(JsonField.class) && obj instanceof String) {
118138
String jsonString = (String) obj;
@@ -123,6 +143,51 @@ private Object buildElementValue(AnnotatedElement element, Row row) {
123143
}
124144
}
125145

146+
@Override
147+
public DefsVals serializeValues(Object obj) {
148+
Objects.requireNonNull(obj);
149+
150+
Class<?> aClass = obj.getClass();
151+
152+
Map<String, Object> fields = new HashMap<>();
153+
for (Field field : aClass.getDeclaredFields()) {
154+
155+
if (Modifier.isTransient(field.getModifiers())) {
156+
// Transient fields are ignored.
157+
continue;
158+
}
159+
160+
ISQLDatabaseOptions options = connectionWrapper.getOptions();
161+
162+
try {
163+
field.setAccessible(true);
164+
Object o = field.get(obj);
165+
if (typeAdapters.containsKey(field.getType())) {
166+
o = typeAdapters.get(field.getType()).serialize(field, o);
167+
} else if (field.isAnnotationPresent(JsonField.class)) {
168+
o = options.getGson().toJson(o);
169+
} else if (Validator.validateAutoIncrement(field) && field.get(obj) == null) {
170+
// If field is PrimaryKey and autoIncrement true and is null,
171+
// We will skip this to use auto increment strategy on SQL server.
172+
continue;
173+
}
174+
fields.put(options.getNamingStrategy().fieldNameToColumn(field.getName()), o);
175+
} catch (IllegalAccessException e) {
176+
e.printStackTrace();
177+
return null;
178+
}
179+
}
180+
// I make entry array for indexing safety.
181+
Map.Entry<String, Object>[] entryArray = fields.entrySet().toArray(new Map.Entry[0]);
182+
String[] defs = new String[entryArray.length];
183+
AtomicReference<Object>[] vals = new AtomicReference[entryArray.length];
184+
for (int i = 0; i < entryArray.length; i++) {
185+
defs[i] = entryArray[i].getKey();
186+
vals[i] = new AtomicReference<>(entryArray[i].getValue());
187+
}
188+
return new DefsVals(defs, vals);
189+
}
190+
126191
private void debug(String message) {
127192
connectionWrapper.debug(message);
128193
}

asql-core/src/main/java/me/zort/sqllib/mapping/annotation/Where.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public static WhereStatement<?> build(Conditional<?> parent, Where annotation, P
4242
case BT:
4343
case LT:
4444
try {
45-
int number = Integer.parseInt(value);
45+
long number = Long.parseLong(value);
4646
if (condition.type().equals(Condition.Type.BT)) {
4747
where.bt(condition.column(), number);
4848
} else {

0 commit comments

Comments
 (0)