Strong answer
JDBC gives Java explicit control over connections, statements, results, and transactions. Use prepared statements for parameters, close resources deterministically, keep transaction boundaries intentional, and understand the database isolation level rather than assuming every transaction is serializable.
Java example
1try (Connection connection = dataSource.getConnection();
2 PreparedStatement statement = connection.prepareStatement("select id from users where email = ?")) {
3 statement.setString(1, email);
4 try (ResultSet result = statement.executeQuery()) {
5 return result.next() ? result.getLong(1) : null;
6 }
7}