Spring Architecture Series-6.Implementing JDBC Module in Spring
Introduce JDBC(Java Database Connectivity) is the standard API for database access in Java.However,working with JDBC can be tedious and error-prone.In this article,i"ll explore how to implement a JDBC module that simplifies database operations, based on my miniSpring project's implementation. Core Components The JDBC module consists of several key components: src/com/yaruyng/jdbc/ ├── core/ │ ├── JdbcTemplate.java │ ├── RowMapper.java │ ├── ResultSetExtractor.java │ ├── StatementCallBack.java │ ├── PreparedStatementCallBack.java │ └── ArgumentPreparedStatementSetter.java ├── datasource/ └── pool/ JdbcTemplate:The Core Class The JdbcTemplate class is the central component that simplifies JDBC operations: public class JdbcTemplate { private DataSource dataSource; public Object query(StatementCallBack stmtCallBack) { Connection con = null; Statement stmt = null; try { con = dataSource.getConnection(); stmt = con.createStatement(); return stmtCallBack.doInStatement(stmt); } catch (SQLException e) { e.printStackTrace(); } finally { try { stmt.close(); con.close(); } catch (SQLException e) { } } return null; } public Object query(String sql, Object[] args, PreparedStatementCallBack pstmtCallBack) { Connection con = null; PreparedStatement pstmt = null; try { con = dataSource.getConnection(); pstmt = con.prepareStatement(sql); ArgumentPreparedStatementSetter setter = new ArgumentPreparedStatementSetter(args); setter.setValues(pstmt); return pstmtCallBack.doInPreparedStatement(pstmt); } catch (SQLException e) { throw new RuntimeException(e); } finally { try { pstmt.close(); con.close(); } catch (SQLException e) { } } } } Key features: Resource management Exception handing Connection pooling support Prepared statement support Row Mapping The RowMapper interface provides a flexible way to map database rows to objects: public interface RowMapper { T mapRow(ResultSet rs, int rowNum) throws SQLException; } Usage example public class UserRowMapper implements RowMapper { @Override public User mapRow(ResultSet rs, int rowNum) throws SQLException { User user = new User(); user.setId(rs.getInt("id")); user.setName(rs.getString("name")); user.setEmail(rs.getString("email")); return user; } } Parameter Handling The ArgumentPreparedStatementSetter class handles parameter binding: public class ArgumentPreparedStatementSetter { private final Object[] args; public void setValues(PreparedStatement pstmt) throws SQLException { if (this.args != null) { for (int i = 0; i

Introduce
JDBC(Java Database Connectivity) is the standard API for database access in Java.However,working with JDBC can be tedious and error-prone.In this article,i"ll explore how to implement a JDBC module that simplifies database operations, based on my miniSpring project's implementation.
Core Components
The JDBC module consists of several key components:
src/com/yaruyng/jdbc/
├── core/
│ ├── JdbcTemplate.java
│ ├── RowMapper.java
│ ├── ResultSetExtractor.java
│ ├── StatementCallBack.java
│ ├── PreparedStatementCallBack.java
│ └── ArgumentPreparedStatementSetter.java
├── datasource/
└── pool/
JdbcTemplate:The Core Class
The JdbcTemplate class is the central component that simplifies JDBC operations:
public class JdbcTemplate {
private DataSource dataSource;
public Object query(StatementCallBack stmtCallBack) {
Connection con = null;
Statement stmt = null;
try {
con = dataSource.getConnection();
stmt = con.createStatement();
return stmtCallBack.doInStatement(stmt);
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
stmt.close();
con.close();
} catch (SQLException e) {
}
}
return null;
}
public Object query(String sql, Object[] args,
PreparedStatementCallBack pstmtCallBack) {
Connection con = null;
PreparedStatement pstmt = null;
try {
con = dataSource.getConnection();
pstmt = con.prepareStatement(sql);
ArgumentPreparedStatementSetter setter =
new ArgumentPreparedStatementSetter(args);
setter.setValues(pstmt);
return pstmtCallBack.doInPreparedStatement(pstmt);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
try {
pstmt.close();
con.close();
} catch (SQLException e) {
}
}
}
}
Key features:
- Resource management
- Exception handing
- Connection pooling support
- Prepared statement support
Row Mapping
The RowMapper interface provides a flexible way to map database rows to objects:
public interface RowMapper<T> {
T mapRow(ResultSet rs, int rowNum) throws SQLException;
}
Usage example
public class UserRowMapper implements RowMapper<User> {
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setEmail(rs.getString("email"));
return user;
}
}
Parameter Handling
The ArgumentPreparedStatementSetter class handles parameter binding:
public class ArgumentPreparedStatementSetter {
private final Object[] args;
public void setValues(PreparedStatement pstmt) throws SQLException {
if (this.args != null) {
for (int i = 0; i < this.args.length; i++) {
Object arg = this.args[i];
doSetValue(pstmt, i+1, arg);
}
}
}
private void doSetValue(PreparedStatement pstmt,
int parameterPosition, Object argValue) throws SQLException {
if (argValue instanceof String) {
pstmt.setString(parameterPosition, (String)argValue);
}
else if (argValue instanceof Integer) {
pstmt.setInt(parameterPosition, (int)argValue);
}
else if (argValue instanceof java.util.Date) {
pstmt.setDate(parameterPosition,
new java.sql.Date(((java.util.Date)argValue).getTime()));
}
}
}
Features:
- Type-safe parameter binding
- Support for common data types
- Extensible design
Result Set Extraction
The ResultSetExtractor interface provides a way to process result sets:
public interface ResultSetExtractor<T> {
T extractData(ResultSet rs) throws SQLException;
}
Implementation example:
public class RowMapperResultSetExtractor<T> implements ResultSetExtractor<List<T>> {
private final RowMapper<T> rowMapper;
public RowMapperResultSetExtractor(RowMapper<T> rowMapper) {
this.rowMapper = rowMapper;
}
@Override
public List<T> extractData(ResultSet rs) throws SQLException {
List<T> results = new ArrayList<>();
int rowNum = 0;
while (rs.next()) {
results.add(rowMapper.mapRow(rs, ++rowNum));
}
return results;
}
}
Usage Example
1. Simple Query
List<User> users = jdbcTemplate.query(
"SELECT * FROM users WHERE age > ?",
new Object[]{18},
new UserRowMapper()
);
2. Custom Statement Processing
Object result = jdbcTemplate.query(new StatementCallBack() {
@Override
public Object doInStatement(Statement stmt) throws SQLException {
ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM users");
if (rs.next()) {
return rs.getInt(1);
}
return 0;
}
});
3. Prepared Statement with Callback
Object result = jdbcTemplate.query(
"UPDATE users SET name = ? WHERE id = ?",
new Object[]{"John", 1},
new PreparedStatementCallBack() {
@Override
public Object doInPreparedStatement(PreparedStatement pstmt)
throws SQLException {
return pstmt.executeUpdate();
}
}
);
Key Features
1. Resource Management
- Automatic connection handling
- Statement cleanup
- Exception handling ### 2. Type Safety
- Generic row mapping
- Type-safe parameter binding
- Result type conversion ### 3. Flexibility
- Custom statement processing
- Extensible row mapping
- Configurable result extraction ### 4. Error Handling
- SQL exception wrapping
- Resource cleanup
- Transaction support
Best Practices
1. Connection Management
- Use connection pooling
- Proper resource cleanup
- Transaction boundaries ### 2. Exception Handling
- Custom exception types
- Proper error propagation
- Resource cleanup in finally blocks ### 3. Performance Optimization
- Statement caching
- Batch processing
- Connection pooling
Common Challenges and Solutions
1. Resource Leaks
- Use try-with-resources
- Proper cleanup in finally blocks
- Connection pooling ### 2. Type Conversion
- Implement type handlers
- Use prepared statements
- Handle null values ### 3. Transaction Management
- Spring transaction integration
- Proper isolation levels
- Rollback handling
Conclusion
Implementing a JDBC module provides:
- Simplified database access
- Type-safe operations
- Resource management
- Error handling Key takeaways:
- Understanding JDBC fundamentals
- Resource management patterns
- Type safety in database operations
- Performance optimization techniques
This implementation demonstrates how to create a robust database access layer while maintaining simplicity and flexibility.