Class SQLiteDataStore
- All Implemented Interfaces:
DataStore
DataStore.
A connection pool is keyed by the canonical path of the backing .db file, independent
of any single entity class -- two entity classes stored in the same file share exactly one
HikariDataSource. The operator cache is keyed by (that same file path, entity class), so
two callers resolving to two different files (two plugins, or the plugin path vs. the external
File path) never share a DataOperator instance. Neither map is static:
DataStoreManager registers exactly one SQLiteDataStore instance, and instance
scoping is what stops both maps from outliving a /reload.
- Since:
- 1.0.0
- Author:
- wisdomme
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionprivate static final classComposite operator-cache key: the backing .db file's canonical path plus the entity class. -
Field Summary
FieldsModifier and TypeFieldDescriptionprivate final Map<SQLiteDataStore.OperatorKey, DataOperator<?>> Operator cache, keyed by (backing .db file canonical path, entity class).Connection pool cache, keyed by the backing .db file's canonical path.private static final Loggerprivate final Map<String, JdbcTransactionManager> JdbcTransactionManagercache, keyed identically todataSourceMap-- the backing .db file's canonical path (D-02: one JDBCDataSourceper file already means one manager per file is "one per plugin container", since each plugin has its own file). -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionprivate static StringcanonicalPath(File dbFile) private static StringResolvesscope's backing .db file path, matching whichever legacygetOperatoroverload the same scope would actually route through: the internal (dbPathForPlugin) shape whenscopeis an internal plugin's own scope (itsdataFolderis exactly the core framework's own data folder,DataScope.forPlugin's construction -- the same disambiguationJsonStore.identityOf(DataScope)uses, 02-05), the external (dbPathForFolder) shape otherwise.private static StringdbPathForFolder(File dataFolder) The external-plugin .db path shape:<plugin's own data folder>/data.db.private static StringdbPathForPlugin(String pluginName) The internal-plugin .db path shape:<core data folder>/sqliteDB/<plugin name>.db.voidCloses every pool this store holds and empties both caches.private static voidensureExists(File dataFolder) getDataSource(DataScope scope) Get the JDBCDataSourcebacking this store for the given scope, needing no entity class up front (a connection pool needs only a JDBC URL).<T extends BaseDataEntity<String>>
DataOperator<T> getOperator(UltiToolsPlugin plugin, Class<T> dataEntity) Get data operation entity class.<T extends BaseDataEntity<String>>
DataOperator<T> getOperator(File dataFolder, Class<T> dataEntity) Get data operator for an external plugin, scoped to the plugin's own data folder.private <T extends BaseDataEntity<String>>
DataOperator<T> getOperatorForPath(String dbPath, Class<T> dataEntity) Shared operator-construction body for all three entry points above, once ownership has already been established by whichever one of them was called.<T extends BaseDataEntity<String>>
DataOperator<T> getOperatorUnchecked(DataScope scope, Class<T> dataEntity) Internal, unchecked construction path (CR-01/CR-03, 02-13):DataStore.getOperator(DataScope, Class)'s default body calls this only after its ownscope.owns(...)check has already passed.Get the type of data storage.private com.zaxxer.hikari.HikariDataSourceReturns the pool fordbPath, building it on first touch.transactionManagerFor(DataScope scope) Resolves the same per-plugin-containerJdbcTransactionManagerinstancegetOperator(com.ultikits.ultitools.abstracts.UltiToolsPlugin, java.lang.Class<T>)wires onto the operators it hands out forscope's own database file -- the seamPluginManager.wireTransactionalcalls to bind the AOP@Transactionalinterceptor to it, so a module's data operators and its@Transactionalbeans share one transaction, not two (T-02-TAM-11).private JdbcTransactionManagertransactionManagerForPath(String dbPath) Returns the manager fordbPath, building it on first touch -- the samecomputeIfAbsentsingle-construction guaranteepoolFor(String)already gives the connection pool for that file.Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface com.ultikits.ultitools.interfaces.DataStore
checkOwnership, checkOwnership, getOperator
-
Field Details
-
LOGGER
-
dataOperatorMap
Operator cache, keyed by (backing .db file canonical path, entity class). Not static -- see class javadoc. -
dataSourceMap
Connection pool cache, keyed by the backing .db file's canonical path. One pool per file, regardless of how many entity classes or callers resolve to it. Not static -- see class javadoc. -
transactionManagerMap
JdbcTransactionManagercache, keyed identically todataSourceMap-- the backing .db file's canonical path (D-02: one JDBCDataSourceper file already means one manager per file is "one per plugin container", since each plugin has its own file). Not static -- same reload-safety reasoning as the other two maps.This is the single seam through which BOTH
transactionManagerFor(DataScope)(called byPluginManager.wireTransactionalfor the AOP interceptor) andgetOperator(com.ultikits.ultitools.abstracts.UltiToolsPlugin, java.lang.Class<T>)(below, for the operators this store hands module authors) resolve a manager for the same file --computeIfAbsentguarantees the second caller to touch a given key gets back the exact instance the first one built, never a second, independent one (T-02-TAM-11: two managers over one file would give a@Transactionalmethod callinginsertAlltwo independent transactions, which is worse than no transaction at all because it looks correct).
-
-
Constructor Details
-
SQLiteDataStore
public SQLiteDataStore()
-
-
Method Details
-
getStoreType
Description copied from interface:DataStoreGet the type of data storage.- Specified by:
getStoreTypein interfaceDataStore- Returns:
- Data storage type
-
getOperator
public <T extends BaseDataEntity<String>> DataOperator<T> getOperator(UltiToolsPlugin plugin, Class<T> dataEntity) Description copied from interface:DataStoreGet data operation entity class.- Specified by:
getOperatorin interfaceDataStore- Type Parameters:
T- Must inheritBaseDataEntity- Parameters:
plugin- PlugindataEntity- Data entity class- Returns:
- Data operation entity
-
getOperator
public <T extends BaseDataEntity<String>> DataOperator<T> getOperator(File dataFolder, Class<T> dataEntity) Description copied from interface:DataStoreGet data operator for an external plugin, scoped to the plugin's own data folder. Refuses outright (D-18) whendataFolderresolves to a registered external plugin's scope that does not owndataEntity, or whendataFoldermatches no registered scope at all -- an unresolvable folder fails closed rather than proceeding unchecked (D-15). ADataStorethat overrides this method directly (as every backend shipped by this framework does) does not inherit this check; see the@deprecatednote.- Specified by:
getOperatorin interfaceDataStore- Type Parameters:
T- entity type- Parameters:
dataFolder- the external plugin's data folder (e.g., plugins/MyPlugin/)dataEntity- data entity class- Returns:
- data operator
-
getOperatorUnchecked
public <T extends BaseDataEntity<String>> DataOperator<T> getOperatorUnchecked(DataScope scope, Class<T> dataEntity) Internal, unchecked construction path (CR-01/CR-03, 02-13):DataStore.getOperator(DataScope, Class)'s default body calls this only after its ownscope.owns(...)check has already passed. Resolvesscopeto the exact same.dbpathdbPathFor(DataScope)already derives fortransactionManagerFor(DataScope)-- internal scopes resolve todbPathForPlugin(String)keyed on the plugin's own name, external scopes todbPathForFolder(File)-- so an internal scope never collapses onto the framework's shared core folder the way routing through the publicgetOperator(File, Class)overload used to (the exact regression 02-07 guarded against and CR-01 found reopened as a security bypass).- Specified by:
getOperatorUncheckedin interfaceDataStore- Type Parameters:
T- Must inheritBaseDataEntity- Parameters:
scope- the requesting scope, already verified to owndataEntitydataEntity- data entity class- Returns:
- data operator for that entity
-
getOperatorForPath
private <T extends BaseDataEntity<String>> DataOperator<T> getOperatorForPath(String dbPath, Class<T> dataEntity) Shared operator-construction body for all three entry points above, once ownership has already been established by whichever one of them was called. Single-sourced so the pool cache (keyed by backing-file path), the operator cache (keyed by (path, entity)), and thetransactionManagerFor(...)wiring are each written in exactly one place. -
getDataSource
Description copied from interface:DataStoreGet the JDBCDataSourcebacking this store for the given scope, needing no entity class up front (a connection pool needs only a JDBC URL).- Specified by:
getDataSourcein interfaceDataStore- Parameters:
scope- the requesting scope- Returns:
- the JDBC data source for that scope
-
transactionManagerFor
Resolves the same per-plugin-containerJdbcTransactionManagerinstancegetOperator(com.ultikits.ultitools.abstracts.UltiToolsPlugin, java.lang.Class<T>)wires onto the operators it hands out forscope's own database file -- the seamPluginManager.wireTransactionalcalls to bind the AOP@Transactionalinterceptor to it, so a module's data operators and its@Transactionalbeans share one transaction, not two (T-02-TAM-11).- Parameters:
scope- the identity token to resolve the manager for- Returns:
- the shared manager for that scope's database file
-
transactionManagerForPath
Returns the manager fordbPath, building it on first touch -- the samecomputeIfAbsentsingle-construction guaranteepoolFor(String)already gives the connection pool for that file. -
dbPathFor
Resolvesscope's backing .db file path, matching whichever legacygetOperatoroverload the same scope would actually route through: the internal (dbPathForPlugin) shape whenscopeis an internal plugin's own scope (itsdataFolderis exactly the core framework's own data folder,DataScope.forPlugin's construction -- the same disambiguationJsonStore.identityOf(DataScope)uses, 02-05), the external (dbPathForFolder) shape otherwise. -
dbPathForPlugin
The internal-plugin .db path shape:<core data folder>/sqliteDB/<plugin name>.db. Shared bygetOperator(UltiToolsPlugin, Class)anddbPathFor(DataScope). -
dbPathForFolder
The external-plugin .db path shape:<plugin's own data folder>/data.db. Shared bygetOperator(File, Class)anddbPathFor(DataScope). -
poolFor
Returns the pool fordbPath, building it on first touch.Map.computeIfAbsent(K, java.util.function.Function<? super K, ? extends V>)makes a concurrent first touch by two callers build exactly one pool, never two.- Parameters:
dbPath- canonical path of the backing .db file- Returns:
- the shared pool for that file
-
ensureExists
-
canonicalPath
-
destroyAllOperators
public void destroyAllOperators()Closes every pool this store holds and empties both caches. Idempotent -- calling it twice closes nothing the second time, since the first call already emptieddataSourceMap. A failure closing one pool is logged and does not stop the rest from being closed.- Specified by:
destroyAllOperatorsin interfaceDataStore
-