DButils + BasicDAO 深度整合指南
文章目录
- 一、BasicDAO 设计理念
- 二、基础实现方案
- 1. 核心抽象类设计
- 2. 完整CRUD实现
- 三、高级功能扩展
- 1. 动态条件查询
- 2. 批量操作增强
- 3. 关联查询处理
- 四、事务管理增强
- 1. 注解式事务支持
- 五、性能优化方案
- 1. 缓存集成
- 2. 分页查询优化
- 六、实战应用示例
- 1. 用户管理完整实现
- 七、最佳实践总结
一、BasicDAO 设计理念
BasicDAO 是基于 DButils 的通用数据访问层抽象,旨在提供:
- 常见CRUD操作的默认实现
- 类型安全的查询方法
- 统一的异常处理机制
- 可扩展的模板方法
二、基础实现方案
1. 核心抽象类设计
public abstract class BasicDAO<T, ID> {
protected final QueryRunner runner;
protected final Class<T> entityClass;
public BasicDAO(DataSource dataSource, Class<T> entityClass) {
this.runner = new QueryRunner(dataSource);
this.entityClass = entityClass;
}
// 获取表名(可被子类覆盖)
protected String getTableName() {
return entityClass.getSimpleName().toLowerCase();
}
// 获取主键字段名(默认"id")
protected String getIdColumn() {
return "id";
}
}
2. 完整CRUD实现
// 插入操作
public ID insert(T entity) throws DataAccessException {
try {
Map<String, Object> fieldMap = BeanUtils.describe(entity);
fieldMap.remove(getIdColumn()); // 排除ID字段
String[] columns = fieldMap.keySet().toArray(new String[0]);
Object[] values = fieldMap.values().toArray();
// 构建动态SQL
String sql = String.format("INSERT INTO %s (%s) VALUES (%s)",
getTableName(),
String.join(",", columns),
String.join(",", Collections.nCopies(columns.length, "?"))
);
// 执行插入并返回生成的主键
return runner.insert(sql, new ScalarHandler<ID>(), values);
} catch (Exception e) {
throw new DataAccessException("Insert operation failed", e);
}
}
// 更新操作
public int update(T entity) throws DataAccessException {
try {
Map<String, Object> fieldMap = BeanUtils.describe(entity);
Object idValue = fieldMap.remove(getIdColumn());
String setClause = fieldMap.keySet().stream()
.map(col -> col + "=?")
.collect(Collectors.joining(","));
String sql = String.format("UPDATE %s SET %s WHERE %s=?",
getTableName(),
setClause,
getIdColumn()
);
// 合并参数值
Object[] params = Stream.concat(
fieldMap.values().stream(),
Stream.of(idValue)
).toArray();
return runner.update(sql, params);
} catch (Exception e) {
throw new DataAccessException("Update operation failed", e);
}
}
// 按ID查询
public Optional<T> findById(ID id) throws DataAccessException {
String sql = String.format("SELECT * FROM %s WHERE %s=?",
getTableName(),
getIdColumn()
);
try {
T entity = runner.query(sql, new BeanHandler<>(entityClass), id);
return Optional.ofNullable(entity);
} catch (SQLException e) {
throw new DataAccessException("Find by ID failed", e);
}
}
// 删除操作
public int delete(ID id) throws DataAccessException {
String sql = String.format("DELETE FROM %s WHERE %s=?",
getTableName(),
getIdColumn()
);
try {
return runner.update(sql, id);
} catch (SQLException e) {
throw new DataAccessException("Delete operation failed", e);
}
}
三、高级功能扩展
1. 动态条件查询
// 条件查询构建器
public List<T> findByCriteria(Criteria criteria) {
StringBuilder sql = new StringBuilder("SELECT * FROM ")
.append(getTableName())
.append(" WHERE 1=1");
List<Object> params = new ArrayList<>();
// 处理条件
criteria.getConditions().forEach((field, value) -> {
sql.append(" AND ").append(field).append("=?");
params.add(value);
});
// 处理排序
if (!criteria.getSorts().isEmpty()) {
sql.append(" ORDER BY ")
.append(criteria.getSorts().entrySet().stream()
.map(e -> e.getKey() + " " + e.getValue())
.collect(Collectors.joining(",")));
}
// 处理分页
if (criteria.getPageSize() > 0) {
sql.append(" LIMIT ? OFFSET ?");
params.add(criteria.getPageSize());
params.add(criteria.getOffset());
}
try {
return runner.query(sql.toString(),
new BeanListHandler<>(entityClass),
params.toArray());
} catch (SQLException e) {
throw new DataAccessException("Query by criteria failed", e);
}
}
// 使用示例
List<User> users = userDAO.findByCriteria(
new Criteria()
.addCondition("status", 1)
.addSort("create_time", "DESC")
.setPage(1, 20)
);
2. 批量操作增强
// 批量插入
public int[] batchInsert(List<T> entities) {
if (entities.isEmpty()) {
return new int[0];
}
// 获取字段列表(排除ID)
Map<String, Object> sample = BeanUtils.describe(entities.get(0));
sample.remove(getIdColumn());
String[] columns = sample.keySet().toArray(new String[0]);
// 构建SQL
String sql = String.format("INSERT INTO %s (%s) VALUES (%s)",
getTableName(),
String.join(",", columns),
String.join(",", Collections.nCopies(columns.length, "?"))
);
// 准备参数
Object[][] params = entities.stream()
.map(entity -> {
try {
Map<String, Object> fieldMap = BeanUtils.describe(entity);
return Arrays.stream(columns)
.map(fieldMap::get)
.toArray();
} catch (Exception e) {
throw new RuntimeException(e);
}
})
.toArray(Object[][]::new);
try {
return runner.batch(sql, params);
} catch (SQLException e) {
throw new DataAccessException("Batch insert failed", e);
}
}
// 使用存储过程的批量更新
public void batchUpdateStatus(List<ID> ids, int status) {
String sql = "{call batch_update_status(?, ?)}";
Object[][] params = ids.stream()
.map(id -> new Object[]{id, status})
.toArray(Object[][]::new);
try {
runner.batch(sql, params);
} catch (SQLException e) {
throw new DataAccessException("Batch update failed", e);
}
}
3. 关联查询处理
// 一对多关联查询
public <R> List<T> findWithRelations(ID id,
String relationSql,
ResultSetHandler<List<R>> relationHandler,
BiConsumer<T, List<R>> relationSetter) {
// 查询主对象
T mainEntity = findById(id).orElseThrow(() ->
new EntityNotFoundException("Entity not found with id: " + id));
// 查询关联对象
List<R> relations = runner.query(relationSql, relationHandler, id);
// 设置关联关系
relationSetter.accept(mainEntity, relations);
return Collections.singletonList(mainEntity);
}
// 使用示例
List<Order> orders = orderDAO.findWithRelations(
orderId,
"SELECT * FROM order_items WHERE order_id=?",
new BeanListHandler<>(OrderItem.class),
(order, items) -> order.setItems(items)
);
四、事务管理增强
1. 注解式事务支持
// 事务注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Transactional {
int timeout() default 30; // 秒
Propagation propagation() default Propagation.REQUIRED;
}
// 事务切面
public class TransactionAspect {
private DataSource dataSource;
public Object manageTransaction(ProceedingJoinPoint pjp) throws Throwable {
Method method = ((MethodSignature)pjp.getSignature()).getMethod();
Transactional transactional = method.getAnnotation(Transactional.class);
Connection conn = null;
boolean existingTransaction = TransactionSynchronizationManager.hasConnection(dataSource);
try {
if (!existingTransaction) {
conn = dataSource.getConnection();
conn.setAutoCommit(false);
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
TransactionSynchronizationManager.bindConnection(dataSource, conn);
}
Object result = pjp.proceed();
if (!existingTransaction) {
conn.commit();
}
return result;
} catch (Exception e) {
if (!existingTransaction && conn != null) {
conn.rollback();
}
throw e;
} finally {
if (!existingTransaction && conn != null) {
TransactionSynchronizationManager.unbindConnection(dataSource);
DbUtils.closeQuietly(conn);
}
}
}
}
// 使用示例
@Transactional
public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
accountDAO.decreaseBalance(fromId, amount);
accountDAO.increaseBalance(toId, amount);
}
五、性能优化方案
1. 缓存集成
// 带缓存的DAO实现
public class CachedBasicDAO<T, ID> extends BasicDAO<T, ID> {
private final Cache<ID, T> cache;
public CachedBasicDAO(DataSource dataSource,
Class<T> entityClass,
Cache<ID, T> cache) {
super(dataSource, entityClass);
this.cache = cache;
}
@Override
public Optional<T> findById(ID id) {
// 先查缓存
T cached = cache.getIfPresent(id);
if (cached != null) {
return Optional.of(cached);
}
// 缓存未命中则查询数据库
Optional<T> result = super.findById(id);
result.ifPresent(entity -> cache.put(id, entity));
return result;
}
@Override
public int update(T entity) {
try {
int affected = super.update(entity);
if (affected > 0) {
// 获取ID值
ID id = (ID) PropertyUtils.getProperty(entity, getIdColumn());
cache.invalidate(id); // 失效缓存
}
return affected;
} catch (Exception e) {
throw new DataAccessException("Update failed", e);
}
}
}
2. 分页查询优化
// 高性能分页实现
public PageResult<T> queryPage(PageQuery query) {
// 主查询
String dataSql = String.format(
"SELECT * FROM %s WHERE %s ORDER BY %s LIMIT ? OFFSET ?",
getTableName(),
query.getWhereClause(),
query.getSortClause()
);
// 计数查询
String countSql = String.format(
"SELECT COUNT(*) FROM %s WHERE %s",
getTableName(),
query.getWhereClause()
);
try {
// 并行执行查询和计数
Future<List<T>> dataFuture = executor.submit(() ->
runner.query(dataSql,
new BeanListHandler<>(entityClass),
query.getParametersWithPaging()));
Future<Long> countFuture = executor.submit(() ->
runner.query(countSql,
new ScalarHandler<>(),
query.getParametersWithoutPaging()));
return new PageResult<>(
dataFuture.get(),
countFuture.get(),
query.getPageNumber(),
query.getPageSize()
);
} catch (Exception e) {
throw new DataAccessException("Page query failed", e);
}
}
六、实战应用示例
1. 用户管理完整实现
// 用户实体
@Data
public class User {
private Long id;
private String username;
private String email;
private Integer status;
private Date createTime;
}
// 用户DAO扩展
public interface UserDAO extends BasicDAO<User, Long> {
// 自定义查询方法
List<User> findByStatus(int status);
// 复杂查询
List<User> searchUsers(String keyword, Date startDate, Date endDate);
// 批量更新状态
int batchUpdateStatus(List<Long> ids, int status);
}
// 实现类
public class UserDAOImpl extends BasicDAO<User, Long> implements UserDAO {
public UserDAOImpl(DataSource dataSource) {
super(dataSource, User.class);
}
@Override
protected String getTableName() {
return "sys_user"; // 自定义表名
}
@Override
public List<User> findByStatus(int status) {
String sql = "SELECT * FROM sys_user WHERE status=? ORDER BY create_time DESC";
try {
return runner.query(sql, new BeanListHandler<>(entityClass), status);
} catch (SQLException e) {
throw new DataAccessException("Query by status failed", e);
}
}
@Override
public List<User> searchUsers(String keyword, Date startDate, Date endDate) {
StringBuilder sql = new StringBuilder("SELECT * FROM sys_user WHERE 1=1");
List<Object> params = new ArrayList<>();
if (StringUtils.isNotBlank(keyword)) {
sql.append(" AND (username LIKE ? OR email LIKE ?)");
params.add("%" + keyword + "%");
params.add("%" + keyword + "%");
}
if (startDate != null) {
sql.append(" AND create_time >= ?");
params.add(new java.sql.Date(startDate.getTime()));
}
if (endDate != null) {
sql.append(" AND create_time <= ?");
params.add(new java.sql.Date(endDate.getTime()));
}
try {
return runner.query(sql.toString(),
new BeanListHandler<>(entityClass),
params.toArray());
} catch (SQLException e) {
throw new DataAccessException("User search failed", e);
}
}
@Override
public int batchUpdateStatus(List<Long> ids, int status) {
String sql = "UPDATE sys_user SET status=? WHERE id IN (" +
String.join(",", Collections.nCopies(ids.size(), "?")) + ")";
Object[] params = new Object[ids.size() + 1];
params[0] = status;
for (int i = 0; i < ids.size(); i++) {
params[i + 1] = ids.get(i);
}
try {
return runner.update(sql, params);
} catch (SQLException e) {
throw new DataAccessException("Batch update status failed", e);
}
}
}
七、最佳实践总结
- 分层设计:保持DAO层纯净,只做数据访问操作
- 合理抽象:通过BasicDAO提供通用实现,特殊需求通过子类扩展
- 异常转换:将SQLException转换为业务异常体系
- 性能考量:对高频操作添加缓存支持
- 事务控制:复杂业务使用声明式事务管理
- SQL安全:始终使用参数化查询防止注入
- 资源管理:确保所有JDBC资源正确释放
这种DButils+BasicDAO的组合方案特别适合中小型项目,在保持轻量级的同时提供了足够的灵活性,能够满足大多数业务场景的数据访问需求。