Skip to content

Commit f191db7

Browse files
authored
Merge pull request #33 from ZorTik/development
Schema synchronization improvements & tests
2 parents c553603 + 46e9bde commit f191db7

14 files changed

Lines changed: 192 additions & 42 deletions

File tree

api/src/main/java/me/zort/sqllib/api/data/QueryResult.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,17 @@ public interface QueryResult {
1313
@Nullable
1414
String getRejectMessage();
1515

16+
static QueryResult successful() {
17+
return new QueryResult() {
18+
@Override
19+
public boolean isSuccessful() {
20+
return true;
21+
}
22+
@Override
23+
public @Nullable String getRejectMessage() {
24+
return null;
25+
}
26+
};
27+
}
28+
1629
}

api/src/main/java/me/zort/sqllib/api/mapping/MappingProxyInstance.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
public interface MappingProxyInstance<T> extends InvocationHandler {
1010

11-
T getProxyInstance();
11+
Class<T> getTypeClass();
1212
List<TableSchema> getTableSchemas(NamingStrategy namingStrategy, boolean sqLite);
1313

1414
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package me.zort.sqllib.api.model;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Getter;
5+
6+
@AllArgsConstructor
7+
@Getter
8+
public class ColumnDefinition {
9+
10+
private final String name;
11+
private final String type;
12+
13+
@Override
14+
public String toString() {
15+
return name + " " + type;
16+
}
17+
18+
}

api/src/main/java/me/zort/sqllib/api/model/SchemaSynchronizer.java

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

3+
import me.zort.sqllib.api.SQLConnection;
4+
import me.zort.sqllib.api.data.QueryResult;
5+
36
/**
47
* Synchronized (updates) table schema (column definitions) in provided source.
58
*
69
* @param <S> The target source
710
*/
8-
public interface SchemaSynchronizer<S> {
11+
public interface SchemaSynchronizer<S extends SQLConnection> {
912

1013
/**
1114
* Table synchronization logic in provided source.
@@ -16,6 +19,6 @@ public interface SchemaSynchronizer<S> {
1619
* @param from The template schema
1720
* @param to The schema to be <u>updated</u> to match 'from' schema
1821
*/
19-
void synchronize(S source, TableSchema from, TableSchema to);
22+
QueryResult synchronize(S source, TableSchema from, TableSchema to);
2023

2124
}

api/src/main/java/me/zort/sqllib/api/model/TableSchema.java

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,39 @@
11
package me.zort.sqllib.api.model;
22

3+
import me.zort.sqllib.util.Pair;
4+
35
public class TableSchema {
46

57
private final String table;
6-
private final String[] definitions;
8+
private final ColumnDefinition[] definitions;
79

8-
public TableSchema(String table, String[] definitions) {
10+
public TableSchema(String table, ColumnDefinition[] definitions) {
911
this.table = table;
1012
this.definitions = definitions;
1113
}
1214

13-
public String getDefinition(int index) {
15+
public ColumnDefinition getDefinitionDetails(int index) {
1416
return definitions[index];
1517
}
1618

19+
public String getDefinitionName(int index) {
20+
return getDefinitionDetails(index).getName();
21+
}
22+
23+
public String getDefinitionType(int index) {
24+
return getDefinitionDetails(index).getType();
25+
}
26+
27+
public String getDefinition(int index) {
28+
ColumnDefinition details = getDefinitionDetails(index);
29+
return details.getName() + " " + details.getType();
30+
}
31+
1732
public String[] getDefinitions() {
33+
String[] definitions = new String[this.definitions.length];
34+
for (int i = 0; i < definitions.length; i++) {
35+
definitions[i] = getDefinition(i);
36+
}
1837
return definitions;
1938
}
2039

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

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,15 @@ public SQLDatabaseConnection(final @NotNull SQLConnectionFactory connectionFacto
5656
* @param mappingFactory Mapping factory to use.
5757
*/
5858
public abstract void setProxyMapping(final @NotNull StatementMappingFactory mappingFactory);
59+
60+
/**
61+
* Sets the schema synchronizer to use with synchronizeModel methods.
62+
*
63+
* @param synchronizer Schema synchronizer to use
64+
*/
5965
@ApiStatus.Experimental
6066
public abstract void setSchemaSynchronizer(SchemaSynchronizer<SQLDatabaseConnection> synchronizer);
67+
6168
@ApiStatus.Experimental
6269
public abstract StatementMappingRegistry getMappingRegistry();
6370

@@ -101,12 +108,40 @@ public SQLDatabaseConnection(final @NotNull SQLConnectionFactory connectionFacto
101108
public abstract <T> T createProxy(Class<T> mappingInterface);
102109
public abstract <T> T createProxy(Class<T> mappingInterface, @NotNull StatementMappingOptions options);
103110
public abstract boolean buildEntitySchema(String tableName, Class<?> entityClass);
111+
112+
/**
113+
* Synchronizes proxy mapping models with database.<br>
114+
* <i>Warning:</i> This tries to update all tables that are part of mapping
115+
* proxies. Should be used carefully.
116+
*
117+
* @return True if synchronization was successful
118+
*/
104119
@ApiStatus.Experimental
105-
public abstract void synchronizeModel();
120+
public abstract boolean synchronizeModel();
121+
122+
/**
123+
* Synchronizes provided entity schema with the database
124+
* In other words, it tries to update database schema (table) to match
125+
* the provided entity schema.
126+
*
127+
* @param entitySchema Entity schema
128+
* @param table Table name
129+
* @return True if synchronization was successful
130+
*/
106131
@ApiStatus.Experimental
107-
public abstract void synchronizeModel(TableSchema entitySchema, String table);
132+
public abstract boolean synchronizeModel(TableSchema entitySchema, String table);
133+
/**
134+
* Synchronizes provided entity class with the database
135+
* In other words, it tries to update database schema (table) to match
136+
* the provided entity schema. This method uses synchronizeModel method
137+
* with schema generated from the provided entity class.
138+
*
139+
* @param entity The entity (model) class
140+
* @param table Table name
141+
* @return True if synchronization was successful
142+
*/
108143
@ApiStatus.Experimental
109-
public abstract void synchronizeModel(Class<?> entity, String table);
144+
public abstract boolean synchronizeModel(Class<?> entity, String table);
110145

111146
/**
112147
* Performs new query and returns the result. This result is never null.

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

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -176,28 +176,59 @@ public void enableCaching(CacheManager cacheManager) {
176176
this.cacheManager = cacheManager;
177177
}
178178

179+
/**
180+
* Synchronizes proxy mapping models with database.<br>
181+
* <i>Warning:</i> This tries to update all tables that are part of mapping
182+
* proxies. Should be used carefully.
183+
*
184+
* @return True if synchronization was successful
185+
*/
179186
@ApiStatus.Experimental
180187
@Override
181-
public void synchronizeModel() {
182-
mappingRegistry.getProxyInstances()
188+
public boolean synchronizeModel() {
189+
return mappingRegistry.getProxyInstances()
183190
.stream().flatMap(i -> i.getTableSchemas(
184191
getOptions().getNamingStrategy(),
185192
this instanceof SQLiteDatabaseConnectionImpl).stream())
186-
.forEach(schema -> synchronizeModel(schema, schema.getTable()));
193+
.allMatch(schema -> synchronizeModel(schema, schema.getTable()));
187194
}
188195

196+
/**
197+
* Synchronizes provided entity schema with the database
198+
* In other words, it tries to update database schema (table) to match
199+
* the provided entity schema.
200+
*
201+
* @param entitySchema Entity schema
202+
* @param table Table name
203+
* @return True if synchronization was successful
204+
*/
189205
@ApiStatus.Experimental
190206
@Override
191-
public void synchronizeModel(TableSchema entitySchema, String table) {
192-
getSchemaSynchronizer().synchronize(this, entitySchema, getSchemaBuilder(table).buildTableSchema());
207+
public boolean synchronizeModel(TableSchema entitySchema, String table) {
208+
return getSchemaSynchronizer().synchronize(this, entitySchema, getSchemaBuilder(table).buildTableSchema()).isSuccessful();
193209
}
194210

211+
/**
212+
* Synchronizes provided entity class with the database
213+
* In other words, it tries to update database schema (table) to match
214+
* the provided entity schema. This method uses synchronizeModel method
215+
* with schema generated from the provided entity class.
216+
*
217+
* @param entity The entity (model) class
218+
* @param table Table name
219+
* @return True if synchronization was successful
220+
*/
195221
@ApiStatus.Experimental
196222
@Override
197-
public void synchronizeModel(Class<?> entity, String table) {
198-
synchronizeModel(new EntitySchemaBuilder(table, entity, getOptions().getNamingStrategy(), this instanceof SQLiteDatabaseConnectionImpl).buildTableSchema(), table);
223+
public boolean synchronizeModel(Class<?> entity, String table) {
224+
return synchronizeModel(new EntitySchemaBuilder(table, entity, getOptions().getNamingStrategy(), this instanceof SQLiteDatabaseConnectionImpl).buildTableSchema(), table);
199225
}
200226

227+
/**
228+
* Sets the schema synchronizer to use with synchronizeModel methods.
229+
*
230+
* @param synchronizer Schema synchronizer to use
231+
*/
201232
@ApiStatus.Experimental
202233
@Override
203234
public void setSchemaSynchronizer(SchemaSynchronizer<SQLDatabaseConnection> synchronizer) {
@@ -279,15 +310,16 @@ public final <T> T createProxy(final @NotNull Class<T> mappingInterface, final @
279310
Objects.requireNonNull(options, "Options cannot be null!");
280311

281312
AtomicReference<MappingProxyInstance<T>> instanceReference = new AtomicReference<>();
282-
instanceReference.set(new ProxyInstanceImpl<>((T) Proxy.newProxyInstance(mappingInterface.getClassLoader(),
283-
new Class[]{mappingInterface}, (proxy, method, args) -> instanceReference.get().invoke(proxy, method, args)),
313+
T rawInstance = (T) Proxy.newProxyInstance(mappingInterface.getClassLoader(),
314+
new Class[]{mappingInterface}, (proxy, method, args) -> instanceReference.get().invoke(proxy, method, args));
315+
instanceReference.set(new ProxyInstanceImpl<>(mappingInterface,
284316
options,
285317
mappingFactory.strategy(mappingInterface, this),
286318
mappingFactory.resultAdapter()));
287319

288320
MappingProxyInstance<T> proxyInstanceWrapper = instanceReference.get();
289321
mappingRegistry.registerProxy(proxyInstanceWrapper);
290-
return proxyInstanceWrapper.getProxyInstance();
322+
return rawInstance;
291323
}
292324

293325
@ApiStatus.Experimental

core/src/main/java/me/zort/sqllib/mapping/MappingRegistryImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public MappingRegistryImpl(SQLDatabaseConnectionImpl connection, SchemaSynchroni
2929

3030
@Override
3131
public void registerProxy(MappingProxyInstance<?> proxyInstance) {
32-
proxyWrappers.put(proxyInstance.getProxyInstance().getClass(), proxyInstance);
32+
proxyWrappers.put(proxyInstance.getTypeClass(), proxyInstance);
3333
}
3434

3535
@Override

core/src/main/java/me/zort/sqllib/mapping/ProxyInstanceImpl.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,18 @@
2121
@Getter
2222
public class ProxyInstanceImpl<T> implements MappingProxyInstance<T> {
2323

24-
private final T proxyInstance;
24+
private final Class<T> typeClass;
2525
private final StatementMappingOptions options;
2626
private final StatementMappingStrategy<T> statementMapping;
2727
private final StatementMappingResultAdapter mappingResultAdapter;
2828

2929
private final List<Method> pendingMethods = new CopyOnWriteArrayList<>();
3030

31-
public ProxyInstanceImpl(T proxyInstance,
31+
public ProxyInstanceImpl(Class<T> typeClass,
3232
StatementMappingOptions options,
3333
StatementMappingStrategy<T> statementMappingStrategy,
3434
StatementMappingResultAdapter mappingResultAdapter) {
35-
this.proxyInstance = proxyInstance;
35+
this.typeClass = typeClass;
3636
this.options = options;
3737
this.statementMapping = statementMappingStrategy;
3838
this.mappingResultAdapter = mappingResultAdapter;
@@ -61,10 +61,13 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
6161
@Override
6262
public List<TableSchema> getTableSchemas(NamingStrategy namingStrategy, boolean sqLite) {
6363
List<TableSchema> schemaList = new ArrayList<>();
64-
for (Method method : proxyInstance.getClass().getDeclaredMethods()) {
64+
for (Method method : getTypeClass().getDeclaredMethods()) {
6565
Class<?> resultType = mappingResultAdapter.retrieveResultType(method);
66-
String table = Table.Util.getFromContext(method, null);
67-
schemaList.add(new EntitySchemaBuilder(table, resultType, namingStrategy, sqLite).buildTableSchema());
66+
67+
if (!QueryResult.class.isAssignableFrom(resultType) && statementMapping.isMappingMethod(method)) {
68+
String table = options.getTable() != null ? options.getTable() : Table.Util.getFromContext(method, null);
69+
schemaList.add(new EntitySchemaBuilder(table, resultType, namingStrategy, sqLite).buildTableSchema());
70+
}
6871
}
6972
return schemaList;
7073
}

core/src/main/java/me/zort/sqllib/mapping/annotation/Table.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public static String getFromContext(Method method, @Nullable ParameterPair[] par
2626
} else if(method.getDeclaringClass().isAnnotationPresent(Table.class)) {
2727
return mapper.assignValues(method.getDeclaringClass().getAnnotation(Table.class).value());
2828
} else {
29-
throw new SQLMappingException("Method " + method.getName() + " requires @Table annotation", method, null);
29+
throw new SQLMappingException("Method " + method.getName() + " in class " + method.getDeclaringClass().getSimpleName() + " requires @Table annotation", method, null);
3030
}
3131
}
3232
}

0 commit comments

Comments
 (0)