PreparedStatementWrapper separated to two files; changed state from refcnt+monitor to enum

This commit is contained in:
Andrew Golovashevich 2025-03-26 19:52:01 +03:00
parent 50c03c9f63
commit a3909e2a5c
4 changed files with 608 additions and 294 deletions

View File

@ -28,6 +28,7 @@ xomrk {
optInContracts()
explicitApi()
jvmToolchain(21)
jvm {
withJava()

View File

@ -98,7 +98,7 @@ public class PreparedStatementsCompilationPhantomCache /*implements ReadOnlyProp
}
}
private static class BoundPreparedStatementWrapper extends _PreparedStatementWrapper {
private static class BoundPreparedStatementWrapper extends _PreparedStatementWrapper_Delegates {
@NotNull
private final ConnectionsMapNode _node;

View File

@ -0,0 +1,229 @@
package ru.langrafhomyak.db.jdbc_resources_manager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.atomic.AtomicReference;
import static java.util.Objects.requireNonNull;
/* package */ abstract class _PreparedStatementWrapper_Logic implements PreparedStatement {
private enum State {
OPEN, IN_USE, CLOSED
}
protected static class StateVar {
private final AtomicReference<State> _value;
StateVar(State initial) {
this._value = new AtomicReference<>(initial);
}
private void _throwState(@NotNull State v) {
if (v == State.OPEN) return;
if (v == State.CLOSED)
throw new IllegalStateException("This PreparedStatement was returned to pool to cache compilation for future uses");
if (v == State.IN_USE) this._throwConcurrent();
}
void enterMethod() {
this._throwState(this._compareAndExchange(State.OPEN, State.IN_USE));
}
void exitMethod() {
this._value.set(State.OPEN);
}
void close() {
this._throwState(this._compareAndExchange(State.OPEN, State.CLOSED));
}
boolean isClosed() {
return this._value.get() == State.CLOSED;
}
private void _throwConcurrent() {
throw new IllegalStateException("You are trying concurrently call method on PreparedStatement while it is not designed to be threadsafe");
}
@SuppressWarnings("SameParameterValue")
private State _compareAndExchange(State expected, State newValue) {
if (_isNativeCompareAndExchangeExists) {
return this._value.compareAndExchange(expected, newValue);
} else {
while (true) {
final State old = this._value.get();
if (old != expected) return old;
if (this._value.compareAndSet(expected, newValue)) return old;
}
}
}
private static final boolean _isNativeCompareAndExchangeExists;
static {
final AtomicReference<Object> a = new AtomicReference<>(new Object());
boolean f;
try {
a.compareAndExchange(a, a);
f = true;
} catch (NoSuchMethodError e) {
f = false;
}
_isNativeCompareAndExchangeExists = f;
}
}
@NotNull
protected final PreparedStatement _orig;
@NotNull
protected final StateVar _state;
// private ResultSet _currentQuery = null;
private boolean _closeOnCompletion = false;
_PreparedStatementWrapper_Logic(@NotNull PreparedStatement orig) {
this._orig = orig;
this._state = new StateVar(State.OPEN);
}
protected abstract void _onClose() throws SQLException;
@Override
public void close() throws SQLException {
this._state.close();
this._onClose();
}
@Override
public boolean isClosed() {
return this._state.isClosed();
}
@Override
public void closeOnCompletion() {
this._state.enterMethod();
try {
this._closeOnCompletion = true;
} finally {
this._state.exitMethod();
}
}
@Override
public boolean isCloseOnCompletion() {
this._state.enterMethod();
try {
return this._closeOnCompletion;
} finally {
this._state.exitMethod();
}
}
private class _BoundResultSetWrapper extends ResultSetWrapper {
public _BoundResultSetWrapper(@NotNull ResultSet orig) {
super(orig);
}
@Override
public void close() throws SQLException {
if (this._orig.isClosed())
return;
this._orig.close();
// _PreparedStatementWrapper_Logic.this._currentQuery = null;
if (_PreparedStatementWrapper_Logic.this._closeOnCompletion)
_PreparedStatementWrapper_Logic.this.close();
}
@Override
public Statement getStatement() {
return _PreparedStatementWrapper_Logic.this;
}
}
@NotNull
private ResultSet _prepareRS(@Nullable ResultSet orig) {
requireNonNull(orig, "Underlying PreparedStatement didn't returned ResultSet");
ResultSet wrapped = this.new _BoundResultSetWrapper(orig);
// this._currentQuery = wrapped;
return wrapped;
}
@Override
public ResultSet executeQuery() throws SQLException {
this._state.enterMethod();
try {
return this._prepareRS(this._orig.executeQuery());
} finally {
this._state.exitMethod();
}
}
@Override
public ResultSet getResultSet() throws SQLException {
this._state.enterMethod();
try {
return this._prepareRS(this._orig.getResultSet());
} finally {
this._state.exitMethod();
}
}
@Override
public ResultSet getGeneratedKeys() throws SQLException {
this._state.enterMethod();
try {
return this._prepareRS(this._orig.getGeneratedKeys());
} finally {
this._state.exitMethod();
}
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
this._state.enterMethod();
try {
return iface.isInstance(this) ? iface.cast(this) : this._orig.unwrap(iface);
} finally {
this._state.exitMethod();
}
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
this._state.enterMethod();
try {
return iface.isInstance(this) || this._orig.isWrapperFor(iface);
} finally {
this._state.exitMethod();
}
}
@Override
public void setPoolable(boolean value) throws SQLException {
this._state.enterMethod();
try {
throw new SQLException("Disabling pooling not allowed on prepared statements driven by PreparedStatementsCompilationCache");
} finally {
this._state.exitMethod();
}
}
@Override
public boolean isPoolable() {
this._state.enterMethod();
try {
return true;
} finally {
this._state.exitMethod();
}
}
}