-
Notifications
You must be signed in to change notification settings - Fork 220
Open
Description
Expected Behavior
Both read and write methods should be executable from the Repository (i.e., findAll() and deleteAll()) while using SQLite
Actual Behaviour
An error occurs during execution
io.micronaut.data.connection.exceptions.ConnectionException: Could not set JDBC Connection [org.sqlite.jdbc4.JDBC4Connection@6c63e2b6] read-only: Cannot change read-only flag after establishing a connection. Use SQLiteConfig#setReadOnly and SQLiteConfig.createConnection().
at io.micronaut.data.connection.support.JdbcConnectionUtils.setConnectionReadOnly(JdbcConnectionUtils.java:170)
at io.micronaut.data.connection.support.JdbcConnectionUtils.applyReadOnly(JdbcConnectionUtils.java:75)
at io.micronaut.data.connection.jdbc.operations.DefaultDataSourceConnectionOperations.lambda$setupConnection$0(DefaultDataSourceConnectionOperations.java:67)
at java.base/java.util.Optional.ifPresent(Optional.java:178)
at io.micronaut.data.connection.jdbc.operations.DefaultDataSourceConnectionOperations.setupConnection(DefaultDataSourceConnectionOperations.java:65)
at io.micronaut.data.connection.support.AbstractConnectionOperations.executeWithNewConnection(AbstractConnectionOperations.java:166)
at io.micronaut.data.connection.support.AbstractConnectionOperations.execute(AbstractConnectionOperations.java:114)
at io.micronaut.data.jdbc.operations.DefaultJdbcRepositoryOperations.executeRead(DefaultJdbcRepositoryOperations.java:816)
at io.micronaut.data.jdbc.operations.DefaultJdbcRepositoryOperations.findAll(DefaultJdbcRepositoryOperations.java:557)
at io.micronaut.data.jdbc.operations.DefaultJdbcRepositoryOperations.findAll(DefaultJdbcRepositoryOperations.java:145)
at io.micronaut.data.runtime.intercept.DefaultFindAllInterceptor.intercept(DefaultFindAllInterceptor.java:49)
at io.micronaut.data.runtime.intercept.DataIntroductionAdvice.intercept(DataIntroductionAdvice.java:84)
at io.micronaut.aop.chain.MethodInterceptorChain.proceed(MethodInterceptorChain.java:143)
at com.apxeolog.olympus.repository.GridReadRepository$Intercepted.findByGridIdIn(Unknown Source)
at com.apxeolog.olympus.service.GridRelationService.updateGridRelationsBatch(GridRelationService.java:95)
at com.apxeolog.olympus.util.TaskProcessor.processLoop(TaskProcessor.java:91)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614)
at java.base/java.lang.Thread.run(Thread.java:1474)
Caused by: java.sql.SQLException: Cannot change read-only flag after establishing a connection. Use SQLiteConfig#setReadOnly and SQLiteConfig.createConnection().
at org.sqlite.jdbc3.JDBC3Connection.setReadOnly(JDBC3Connection.java:146)
at io.micronaut.data.connection.support.JdbcConnectionUtils.setConnectionReadOnly(JdbcConnectionUtils.java:167)
Steps To Reproduce
- Simple SQLite setup
... micronaut 4.9.4
annotationProcessor("io.micronaut.data:micronaut-data-processor")
implementation("io.micronaut.data:micronaut-data-jdbc")
implementation("io.micronaut.sql:micronaut-jdbc-hikari")
implementation("org.xerial:sqlite-jdbc:3.50.3.0")
datasources:
default:
url: jdbc:sqlite:./data/test.db?journal_mode=WAL&synchronous=NORMAL&cache_size=-32000&temp_store=MEMORY&busy_timeout=5000
driverClassName: org.sqlite.JDBC
dialect: H2
hikari:
maximum-pool-size: 1
minimum-idle: 0@JdbcRepository(dialect = Dialect.H2)
public interface TestRepository extends CrudRepository<Entity, Long> {
}- Call different (read/write) repository methods
testRepository.deleteAll();
testRepository.findAll();The problem here is that Repository forces a specific connection state, i.e., (call chain):
@NonNull
@Override
public <T, R> List<R> findAll(@NonNull PreparedQuery<T, R> preparedQuery) {
SqlPreparedQuery<T, R> sqlPreparedQuery = getSqlPreparedQuery(preparedQuery);
return executeRead(connection -> findAll(connection, sqlPreparedQuery, true), sqlPreparedQuery.getInvocationContext());
}
...
private <I> I executeRead(Function<Connection, I> fn, AnnotationMetadata annotationMetadata) {
if (!jdbcConfiguration.isAllowConnectionPerOperation() && connectionOperations.findConnectionStatus().isEmpty()) {
throw connectionNotFoundAndNewNotAllowed();
}
return connectionOperations.execute(ConnectionDefinition.READ_ONLY.withAnnotationMetadata(annotationMetadata), status -> {
Connection connection = status.getConnection();
applySchema(connection);
return fn.apply(connection);
});
}
...
@Override
protected void setupConnection(ConnectionStatus<Connection> connectionStatus) {
connectionStatus.getDefinition().isReadOnly().ifPresent(readOnly -> {
List<Runnable> onCompleteCallbacks = new ArrayList<>(1);
JdbcConnectionUtils.applyReadOnly(LOG, connectionStatus.getConnection(), readOnly, onCompleteCallbacks);
if (!onCompleteCallbacks.isEmpty()) {
connectionStatus.registerSynchronization(new ConnectionSynchronization() {
@Override
public void executionComplete() {
for (Runnable onCompleteCallback : onCompleteCallbacks) {
onCompleteCallback.run();
}
}
});
}
});
}
...
public static void applyReadOnly(Logger logger,
Connection connection,
boolean isReadOnly,
List<Runnable> onCompleteCallbacks) {
boolean connectionReadOnly = isReadOnly(connection);
if (connectionReadOnly != isReadOnly) {
setConnectionReadOnly(logger, connection, isReadOnly);
onCompleteCallbacks.add(() -> setConnectionReadOnly(logger, connection, connectionReadOnly));
}
}If the current connection state does not match the one enforced by the Repository operation, it would call connection.setReadOnly which is not supported by the SQLite driver.
Naturally, if you plan to write anything, you would not create a read-only connection from the beginning, so any READ method from the repository would result in the error
Environment Information
- Windows 10
- OpenJDK 25
Example Application
No response
Version
4.9.4
Metadata
Metadata
Assignees
Labels
No labels