|
13 | 13 | import me.zort.sqllib.api.data.QueryResult; |
14 | 14 | import me.zort.sqllib.api.data.QueryRowsResult; |
15 | 15 | import me.zort.sqllib.api.data.Row; |
| 16 | +import me.zort.sqllib.api.mapping.MappingProxyInstance; |
16 | 17 | import me.zort.sqllib.api.mapping.StatementMappingFactory; |
17 | 18 | import me.zort.sqllib.api.mapping.StatementMappingOptions; |
18 | | -import me.zort.sqllib.api.mapping.StatementMappingResultAdapter; |
19 | | -import me.zort.sqllib.api.mapping.StatementMappingStrategy; |
| 19 | +import me.zort.sqllib.api.mapping.StatementMappingRegistry; |
| 20 | +import me.zort.sqllib.api.model.SchemaSynchronizer; |
| 21 | +import me.zort.sqllib.api.model.TableSchema; |
| 22 | +import me.zort.sqllib.api.model.TableSchemaBuilder; |
20 | 23 | import me.zort.sqllib.api.options.NamingStrategy; |
21 | 24 | import me.zort.sqllib.internal.Defaults; |
22 | 25 | import me.zort.sqllib.internal.annotation.JsonField; |
|
26 | 29 | import me.zort.sqllib.internal.impl.DefaultObjectMapper; |
27 | 30 | import me.zort.sqllib.internal.impl.QueryResultImpl; |
28 | 31 | import me.zort.sqllib.mapping.DefaultStatementMappingFactory; |
| 32 | +import me.zort.sqllib.mapping.MappingRegistryImpl; |
| 33 | +import me.zort.sqllib.mapping.ProxyInstanceImpl; |
| 34 | +import me.zort.sqllib.model.DatabaseSchemaBuilder; |
| 35 | +import me.zort.sqllib.model.EntitySchemaBuilder; |
| 36 | +import me.zort.sqllib.model.SQLSchemaSynchronizer; |
29 | 37 | import me.zort.sqllib.pool.PooledSQLDatabaseConnection; |
30 | 38 | import me.zort.sqllib.transaction.Transaction; |
31 | 39 | import me.zort.sqllib.util.Validator; |
|
34 | 42 | import org.jetbrains.annotations.Nullable; |
35 | 43 |
|
36 | 44 | import java.lang.reflect.Field; |
37 | | -import java.lang.reflect.Method; |
38 | 45 | import java.lang.reflect.Modifier; |
39 | 46 | import java.lang.reflect.Proxy; |
40 | 47 | import java.sql.*; |
41 | 48 | import java.util.*; |
42 | 49 | import java.util.concurrent.CopyOnWriteArrayList; |
| 50 | +import java.util.concurrent.atomic.AtomicReference; |
43 | 51 | import java.util.logging.Logger; |
44 | 52 |
|
45 | 53 | import static me.zort.sqllib.util.ExceptionsUtility.runCatching; |
@@ -72,6 +80,7 @@ public class SQLDatabaseConnectionImpl extends PooledSQLDatabaseConnection { |
72 | 80 | @Getter |
73 | 81 | private final ISQLDatabaseOptions options; |
74 | 82 | private final transient List<ErrorStateObserver> errorStateHandlers; |
| 83 | + private final transient MappingRegistryImpl mappingRegistry; |
75 | 84 | private transient StatementMappingFactory mappingFactory; |
76 | 85 | private transient ObjectMapper objectMapper; |
77 | 86 | private transient CacheManager cacheManager; |
@@ -105,6 +114,7 @@ public SQLDatabaseConnectionImpl(final @NotNull SQLConnectionFactory connectionF |
105 | 114 | this.errorStateHandlers = new CopyOnWriteArrayList<>(); |
106 | 115 | this.transaction = null; |
107 | 116 | this.logger = Logger.getGlobal(); |
| 117 | + this.mappingRegistry = new MappingRegistryImpl(this, new SQLSchemaSynchronizer()); |
108 | 118 |
|
109 | 119 | enableCaching(CacheManager.noCache()); |
110 | 120 |
|
@@ -166,6 +176,46 @@ public void enableCaching(CacheManager cacheManager) { |
166 | 176 | this.cacheManager = cacheManager; |
167 | 177 | } |
168 | 178 |
|
| 179 | + @ApiStatus.Experimental |
| 180 | + @Override |
| 181 | + public void synchronizeModel() { |
| 182 | + mappingRegistry.getProxyInstances() |
| 183 | + .stream().flatMap(i -> i.getTableSchemas( |
| 184 | + getOptions().getNamingStrategy(), |
| 185 | + this instanceof SQLiteDatabaseConnectionImpl).stream()) |
| 186 | + .forEach(schema -> synchronizeModel(schema, schema.getTable())); |
| 187 | + } |
| 188 | + |
| 189 | + @ApiStatus.Experimental |
| 190 | + @Override |
| 191 | + public void synchronizeModel(TableSchema entitySchema, String table) { |
| 192 | + getSchemaSynchronizer().synchronize(this, entitySchema, getSchemaBuilder(table).buildTableSchema()); |
| 193 | + } |
| 194 | + |
| 195 | + @ApiStatus.Experimental |
| 196 | + @Override |
| 197 | + public void synchronizeModel(Class<?> entity, String table) { |
| 198 | + synchronizeModel(new EntitySchemaBuilder(table, entity, getOptions().getNamingStrategy(), this instanceof SQLiteDatabaseConnectionImpl).buildTableSchema(), table); |
| 199 | + } |
| 200 | + |
| 201 | + @ApiStatus.Experimental |
| 202 | + @Override |
| 203 | + public void setSchemaSynchronizer(SchemaSynchronizer<SQLDatabaseConnection> synchronizer) { |
| 204 | + mappingRegistry.setSynchronizer(synchronizer); |
| 205 | + } |
| 206 | + |
| 207 | + @ApiStatus.Experimental |
| 208 | + @Override |
| 209 | + public SchemaSynchronizer<SQLDatabaseConnection> getSchemaSynchronizer() { |
| 210 | + return mappingRegistry.getSynchronizer(); |
| 211 | + } |
| 212 | + |
| 213 | + @ApiStatus.Experimental |
| 214 | + @Override |
| 215 | + public StatementMappingRegistry getMappingRegistry() { |
| 216 | + return mappingRegistry; |
| 217 | + } |
| 218 | + |
169 | 219 | /** |
170 | 220 | * Constructs a mapping proxy based on provided interface. |
171 | 221 | * The interface should follow rules for creating mapping repositories |
@@ -228,37 +278,23 @@ public final <T> T createProxy(final @NotNull Class<T> mappingInterface, final @ |
228 | 278 | Objects.requireNonNull(mappingInterface, "Mapping interface cannot be null!"); |
229 | 279 | Objects.requireNonNull(options, "Options cannot be null!"); |
230 | 280 |
|
231 | | - StatementMappingStrategy<T> statementMapping = mappingFactory.strategy(mappingInterface, this); |
232 | | - StatementMappingResultAdapter mappingResultAdapter = mappingFactory.resultAdapter(); |
233 | | - List<Method> pendingMethods = new CopyOnWriteArrayList<>(); |
| 281 | + 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)), |
| 284 | + options, |
| 285 | + mappingFactory.strategy(mappingInterface, this), |
| 286 | + mappingFactory.resultAdapter())); |
234 | 287 |
|
235 | | - return (T) Proxy.newProxyInstance(mappingInterface.getClassLoader(), |
236 | | - new Class[]{mappingInterface}, (proxy, method, args) -> { |
237 | | - |
238 | | - // Allow invokation from interfaces or abstract classes only. |
239 | | - Class<?> declaringClass = method.getDeclaringClass(); |
240 | | - if ((declaringClass.isInterface() || Modifier.isAbstract(declaringClass.getModifiers())) |
241 | | - && statementMapping.isMappingMethod(method)) { |
242 | | - // Prepare and execute query based on invoked method. |
243 | | - QueryResult result = statementMapping.executeQuery(options, method, args, mappingResultAdapter.retrieveResultType(method)); |
244 | | - // Adapt QueryResult to method return type. |
245 | | - return mappingResultAdapter.adaptResult(method, result); |
246 | | - } |
247 | | - |
248 | | - // Default methods are invoked normally. |
249 | | - if (declaringClass.isInterface() && method.isDefault()) { |
250 | | - return JVM.getJVM().invokeDefault(declaringClass, proxy, method, args); |
251 | | - } |
252 | | - |
253 | | - throw new UnsupportedOperationException("Method " + method.getName() + " is not supported by this mapping repository!"); |
254 | | - }); |
| 288 | + MappingProxyInstance<T> proxyInstanceWrapper = instanceReference.get(); |
| 289 | + mappingRegistry.registerProxy(proxyInstanceWrapper); |
| 290 | + return proxyInstanceWrapper.getProxyInstance(); |
255 | 291 | } |
256 | 292 |
|
257 | 293 | @ApiStatus.Experimental |
258 | 294 | public final boolean buildEntitySchema(final @NotNull String tableName, final @NotNull Class<?> entityClass) { |
259 | 295 | Objects.requireNonNull(entityClass, "Entity class cannot be null!"); |
260 | 296 |
|
261 | | - TableSchemaBuilder converter = new TableSchemaBuilder(this, tableName, entityClass); |
| 297 | + EntitySchemaBuilder converter = new EntitySchemaBuilder(tableName, entityClass, options.getNamingStrategy(), this instanceof SQLiteDatabaseConnectionImpl); |
262 | 298 | String query = converter.buildTableQuery(); |
263 | 299 |
|
264 | 300 | return exec(() -> query).isSuccessful(); |
@@ -518,6 +554,17 @@ public final boolean isTransactionActive() { |
518 | 554 | return transaction != null && transaction.isActive(); |
519 | 555 | } |
520 | 556 |
|
| 557 | + @Override |
| 558 | + public TableSchemaBuilder getSchemaBuilder(String table) { |
| 559 | + return new DatabaseSchemaBuilder(q -> { |
| 560 | + try { |
| 561 | + return buildStatement(() -> q); |
| 562 | + } catch (SQLException e) { |
| 563 | + throw new RuntimeException(e); |
| 564 | + } |
| 565 | + }, table); |
| 566 | + } |
| 567 | + |
521 | 568 | @SuppressWarnings("all") |
522 | 569 | private void notifyError(int code) { |
523 | 570 | errorCount++; |
|
0 commit comments