Skip to content

Commit b89b7dc

Browse files
committed
Streamline Statement execution and SQLException wrapping
- Introduced a common executeImpl helper method in UcanaccessStatement to reduce boilerplate code across execute/executeQuery/executeUpdate overloads. This centralizes connection setup, modification checks, and exception handling. - Prefix certain exception messages with info about the failed statement. - Updated UcanaccessSQLException.wrap methods to consistently use the new '(reason, throwable)' constructor for improved error message contextualization. This ensures SQLState, ErrorCode, and Cause are always preserved when wrapping SQLException instances.
1 parent 8eb114d commit b89b7dc

3 files changed

Lines changed: 74 additions & 53 deletions

File tree

src/main/java/net/ucanaccess/exception/UcanaccessSQLException.java

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,47 @@ public String getMessage() {
9696
}
9797

9898
/**
99-
* Wraps the specified exception into a {@link UcanaccessSQLException}
100-
* unless its type can be cast to {@link UcanaccessSQLException}.
99+
* Wraps a {@link Throwable} into a {@link UcanaccessSQLException}.
100+
* <p>
101+
* If the throwable is already a {@link UcanaccessSQLException}, it's returned as is.
102+
* This is a convenience method calling {@link #wrap(String, Throwable)} without a reason prefix.
103+
* </p>
101104
*
102-
* @param <T> the type of exception to wrap
103-
* @param _t the exception to wrap
104-
* @return wrapped exception or parameter if can be cast to {@link UcanaccessSQLException}
105+
* @param <T> the type of the {@link Throwable} to wrap.
106+
* @param _t the {@link Throwable} to wrap.
107+
* @return a {@link UcanaccessSQLException} instance.
105108
*/
106109
public static final <T extends Throwable> UcanaccessSQLException wrap(T _t) {
107-
return _t instanceof UcanaccessSQLException ? (UcanaccessSQLException) _t : new UcanaccessSQLException(_t);
110+
return wrap(null, _t);
111+
}
112+
113+
/**
114+
* Wraps a {@link Throwable} into a {@link UcanaccessSQLException},
115+
* prepending an optional reason message.
116+
* <p>
117+
* Preserves original SQLState, ErrorCode, and Cause when wrapping {@link SQLException} types.
118+
* If {@code _reason} is null or blank, the message is derived solely from {@code _t}.
119+
* </p>
120+
*
121+
* @param <T> the type of the {@link Throwable} to wrap
122+
* @param _reason an optional custom message prefix
123+
* @param _t the {@link Throwable} to wrap
124+
* @return a {@link UcanaccessSQLException} instance
125+
*/
126+
public static final <T extends Throwable> UcanaccessSQLException wrap(String _reason, T _t) {
127+
String reason = _reason == null || _reason.isBlank() ? null : _reason.trim();
128+
if (_t instanceof UcanaccessSQLException) {
129+
UcanaccessSQLException ex = (UcanaccessSQLException) _t;
130+
if (reason == null) {
131+
return ex;
132+
}
133+
return new UcanaccessSQLException(reason + ": " + ex.getMessage(), ex.getSQLState(), ex.getErrorCode(), ex.getCause());
134+
} else if (_t instanceof SQLException) {
135+
SQLException ex = (SQLException) _t;
136+
return new UcanaccessSQLException(reason == null ? ex.getMessage() : reason + ": " + ex.getMessage(), ex.getSQLState(), ex.getErrorCode(), ex.getCause());
137+
} else {
138+
return new UcanaccessSQLException(reason == null ? _t.getMessage() : reason + ": " + _t.getMessage(), _t);
139+
}
108140
}
109141

110142
public static final <T extends UcanaccessSQLException> void throwIf(BooleanSupplier _condition, Supplier<T> _exceptionSupplier) throws T {

src/main/java/net/ucanaccess/jdbc/UcanaccessStatement.java

Lines changed: 35 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010

1111
import java.sql.Connection;
1212
import java.sql.ResultSet;
13+
import java.sql.SQLException;
1314
import java.sql.SQLWarning;
1415
import java.sql.Statement;
1516
import java.util.Map;
17+
import java.util.function.Function;
1618

1719
public class UcanaccessStatement implements Statement {
1820

@@ -110,93 +112,76 @@ protected void checkLastModified() throws UcanaccessSQLException {
110112
}
111113
}
112114

113-
@Override
114-
public boolean execute(String _sql) throws UcanaccessSQLException {
115+
/**
116+
* Executes a generic SQL operation, handling common setup and error mapping.
117+
* This method centralizes the logic for setting the current statement,
118+
* performing modification checks, and wrapping exceptions.
119+
*
120+
* @param <R> The return type of the SQL operation.
121+
* @param _sql The SQL string associated with the operation (used for error messages).
122+
* @param _supplier A supplier that provides the result of the actual SQL operation.
123+
* @return The result of the SQL execution, of type {@code <R>}.
124+
* @throws UcanaccessSQLException if an SQL error occurs during execution or wrapping.
125+
*/
126+
private <R> R executeImpl(String _sql, IThrowingSupplier<R, SQLException> _supplier) throws UcanaccessSQLException {
115127
return tryCatch(() -> {
116128
connection.setCurrentStatement(this);
117129
checkLastModified();
118-
return new Execute(this, convertSql(_sql, connection)).execute();
119-
});
130+
return _supplier.get();
131+
}, ex -> UcanaccessSQLException.wrap("Failed to execute [" + _sql + "]", ex));
132+
}
133+
134+
@Override
135+
public boolean execute(String _sql) throws UcanaccessSQLException {
136+
return executeImpl(_sql, () -> new Execute(this, convertSql(_sql, connection)).execute());
120137
}
121138

122139
@Override
123140
public boolean execute(String _sql, int _autoGeneratedKeys) throws UcanaccessSQLException {
124-
return tryCatch(() -> {
125-
connection.setCurrentStatement(this);
126-
checkLastModified();
127-
return new Execute(this, convertSql(_sql, connection), _autoGeneratedKeys).execute();
128-
});
141+
return executeImpl(_sql, () -> new Execute(this, convertSql(_sql, connection), _autoGeneratedKeys).execute());
129142
}
130143

131144
@Override
132145
public boolean execute(String _sql, int[] _indexes) throws UcanaccessSQLException {
133-
return tryCatch(() -> {
134-
connection.setCurrentStatement(this);
135-
checkLastModified();
136-
return new Execute(this, convertSql(_sql, connection), _indexes).execute();
137-
});
146+
return executeImpl(_sql, () -> new Execute(this, convertSql(_sql, connection), _indexes).execute());
138147
}
139148

140149
@Override
141150
public boolean execute(String _sql, String[] _columnNames) throws UcanaccessSQLException {
142-
return tryCatch(() -> {
143-
connection.setCurrentStatement(this);
144-
checkLastModified();
145-
return new Execute(this, _sql, _columnNames).execute();
146-
});
151+
return executeImpl(_sql, () -> new Execute(this, _sql, _columnNames).execute());
147152
}
148153

149154
@Override
150155
public int[] executeBatch() throws UcanaccessSQLException {
151156
return tryCatch(() -> {
152157
connection.setCurrentStatement(this);
153158
return new ExecuteUpdate(this).executeBatch();
154-
});
159+
}, ex -> UcanaccessSQLException.wrap("Failed to execute batch", ex));
155160
}
156161

157162
@Override
158163
public ResultSet executeQuery(String _sql) throws UcanaccessSQLException {
159-
return tryCatch(() -> {
160-
connection.setCurrentStatement(this);
161-
checkLastModified();
162-
return new UcanaccessResultSet(wrapped.executeQuery(convertSql(_sql, connection)), this);
163-
});
164+
return executeImpl(_sql, () -> new UcanaccessResultSet(wrapped.executeQuery(convertSql(_sql, connection)), this));
164165
}
165166

166167
@Override
167168
public int executeUpdate(String _sql) throws UcanaccessSQLException {
168-
return tryCatch(() -> {
169-
connection.setCurrentStatement(this);
170-
checkLastModified();
171-
return new ExecuteUpdate(this, convertSql(_sql)).execute();
172-
});
169+
return executeImpl(_sql, () -> new ExecuteUpdate(this, convertSql(_sql)).execute());
173170
}
174171

175172
@Override
176173
public int executeUpdate(String _sql, int _autoGeneratedKeys) throws UcanaccessSQLException {
177-
return tryCatch(() -> {
178-
connection.setCurrentStatement(this);
179-
checkLastModified();
180-
return new ExecuteUpdate(this, convertSql(_sql), _autoGeneratedKeys).execute();
181-
});
174+
return executeImpl(_sql, () -> new ExecuteUpdate(this, convertSql(_sql), _autoGeneratedKeys).execute());
182175
}
183176

184177
@Override
185178
public int executeUpdate(String _sql, int[] _indexes) throws UcanaccessSQLException {
186-
return tryCatch(() -> {
187-
connection.setCurrentStatement(this);
188-
checkLastModified();
189-
return new ExecuteUpdate(this, convertSql(_sql), _indexes).execute();
190-
});
179+
return executeImpl(_sql, () -> new ExecuteUpdate(this, convertSql(_sql), _indexes).execute());
191180
}
192181

193182
@Override
194183
public int executeUpdate(String _sql, String[] _columnNames) throws UcanaccessSQLException {
195-
return tryCatch(() -> {
196-
connection.setCurrentStatement(this);
197-
checkLastModified();
198-
return new ExecuteUpdate(this, convertSql(_sql), _columnNames).execute();
199-
});
184+
return executeImpl(_sql, () -> new ExecuteUpdate(this, convertSql(_sql), _columnNames).execute());
200185
}
201186

202187
@Override
@@ -406,7 +391,11 @@ protected static final <T extends Throwable> void tryCatch(IThrowingRunnable<T>
406391
}
407392

408393
protected static final <R, T extends Throwable> R tryCatch(IThrowingSupplier<R, T> _catchable) throws UcanaccessSQLException {
409-
return Try.catching(_catchable).orThrow(UcanaccessSQLException::wrap);
394+
return tryCatch(_catchable, UcanaccessSQLException::wrap);
395+
}
396+
397+
protected static final <R, T extends Throwable> R tryCatch(IThrowingSupplier<R, T> _catchable, Function<T, UcanaccessSQLException> _exSupplier) throws UcanaccessSQLException {
398+
return Try.catching(_catchable).orThrow(_exSupplier);
410399
}
411400

412401
}

src/test/java/net/ucanaccess/jdbc/UnproperExecuteQueryTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ void testExecute(AccessVersion _accessVersion) throws Exception {
2929
"DELETE FROM " + tableName)) {
3030
assertThatThrownBy(() -> st.executeQuery(sql))
3131
.isInstanceOf(UcanaccessSQLException.class)
32-
.hasMessageMatching("UCAExc:::[0-9]\\.[0-9][0-9\\.]*(?:-SNAPSHOT)? General error");
32+
.hasMessageMatching("UCAExc:::[0-9]\\.[0-9][0-9\\.]*(?:-SNAPSHOT)? .*General error");
3333
st.execute(sql);
3434
}
3535
}

0 commit comments

Comments
 (0)