DAO层核心设计原则
- 单一职责原则
每个DAO类仅负责一张表的操作,避免功能耦合。 - 接口隔离
通过接口定义方法,实现类提供具体逻辑,便于扩展和Mock测试。 - 异常统一处理
封装底层异常(如SQLException
),向上层抛出统一的自定义异常(如DataAccessException
)。 - 事务边界清晰
事务控制应在Service层,DAO层保持无状态。
标准实现步骤(以MyBatis为例)
目录结构规范
src ├── main │ ├── java │ │ └── com.example.dao │ │ ├── UserDao.java // 接口 │ │ └── impl │ │ └── UserDaoImpl.java // MyBatis实现 │ └── resources │ └── mapper │ └── UserMapper.xml // SQL映射文件
接口定义(UserDao.java)
public interface UserDao { User findById(Long id); void insert(User user); void update(User user); void deleteById(Long id); List<User> findByName(String name); // 条件查询示例 }
MyBatis实现(UserDaoImpl.java)
@Repository // Spring注解,声明为Bean public class UserDaoImpl implements UserDao { @Autowired private SqlSessionTemplate sqlSession; // MyBatis SQL操作模板 @Override public User findById(Long id) { return sqlSession.selectOne("com.example.dao.UserDao.findById", id); } @Override public void insert(User user) { sqlSession.insert("com.example.dao.UserDao.insert", user); } // 其他方法实现... }
SQL映射文件(UserMapper.xml)
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.dao.UserDao"> <select id="findById" resultType="com.example.model.User"> SELECT id, name, email FROM users WHERE id = #{id} </select> <insert id="insert" parameterType="com.example.model.User"> INSERT INTO users (name, email) VALUES (#{name}, #{email}) </insert> </mapper>
关键优化策略
-
防SQL注入
- 始终使用占位符(MyBatis自动预编译)
- 禁用拼接SQL(除非动态表名等特殊场景)
-
性能优化
- 批量操作:MyBatis的
<foreach>
标签或SqlSession#batch()
- 二级缓存:
<cache/>
标签控制缓存策略
- 批量操作:MyBatis的
-
事务管理
@Service public class UserService { @Autowired private UserDao userDao; @Transactional // Spring声明式事务 public void updateUser(User user) { userDao.update(user); // 其他DAO操作,同一事务 } }
-
分页查询
使用PageHelper插件:PageHelper.startPage(1, 10); // 第1页,每页10条 List<User> users = userDao.findByName("John");
不同技术栈选型
场景 | 推荐方案 | 特点 |
---|---|---|
简单项目/轻量级 | Spring JDBC Template | 无复杂映射,手动控制SQL |
复杂ORM映射 | Hibernate | 对象关系映射自动化 |
高灵活SQL | MyBatis | SQL与代码解耦,易于优化 |
响应式编程 | Spring Data R2DBC | 非阻塞IO,适合微服务 |
常见陷阱与解决方案
-
N+1查询问题
- 现象:查询主表后循环查询关联表。
- 方案:MyBatis用
<association>
一次性加载;Hibernate配置@Fetch(FetchMode.JOIN)
。
-
事务失效
- 根源:非public方法调用、异常类型非RuntimeException。
- 修复:确保
@Transactional
作用于public方法,并指定rollbackFor
。
-
连接泄漏
- 检测:监控Druid的
connectionHoldCount
。 - 预防:使用
try-with-resources
或模板类(如JdbcTemplate
)。
- 检测:监控Druid的
单元测试规范
@SpringBootTest public class UserDaoTest { @Autowired private UserDao userDao; @Test @Sql(scripts = "/test-data.sql") // 初始化数据 public void testFindById() { User user = userDao.findById(1L); assertNotNull(user); assertEquals("Alice", user.getName()); } }
要点:
- 使用内存数据库(H2)替代生产库
- 通过
@Sql
准备测试数据 - 验证SQL执行次数(
@ExpectedSql
)
优秀的DAO层应具备:
✅ 高内聚低耦合:通过接口隔离实现
✅ 安全可靠:参数化SQL+事务控制
✅ 可扩展性:支持多数据库(通过Dialect配置)
✅ 可维护性:清晰的日志(打印SQL+参数)
遵循上述实践可显著提升系统稳定性,随着云原生发展,建议逐步探索Spring Data JPA或NoSQL DAO模式以适应新技术趋势。
引用说明:
- MyBatis官方文档:配置项与缓存机制
- 《阿里巴巴Java开发手册》:DAO层设计规范
- Spring Framework 5.3.x:事务管理源码分析
- 数据库事务隔离级别:ANSI/ISO SQL标准
- 性能优化案例:GitHub开源项目MyBatis-Plus实践
原创文章,发布者:酷盾叔,转转请注明出处:https://www.kd.cn/ask/46237.html