引言:困惑的起源
在Java持久层开发中,我们经常会同时遇到DAO和Mapper这两个概念。许多开发者会产生这样的疑问:它们是不是同一种东西的不同叫法?为什么MyBatis用Mapper而不用DAO?本文将系统解析二者的关系,帮助开发者建立清晰的认识。
一、核心概念解析
1. DAO:经典设计模式
DAO(Data Access Object)是一种设计模式,最早出现在J2EE核心模式中。它的核心价值在于:
- 抽象接口:定义数据访问的标准方法
- 实现分离:业务层不依赖具体数据库操作
- 统一契约:为不同数据源提供一致访问方式
// 典型DAO接口
public interface UserDao {
User findById(Long id);
List<User> findAll();
void save(User user);
}
2. Mapper:MyBatis的实现方式
Mapper是MyBatis框架对DAO模式的具体实现,其特点包括:
- 接口绑定:Java接口直接映射SQL语句
- 动态代理:运行时自动生成实现类
- SQL解耦:SQL保存在XML或注解中
// MyBatis Mapper示例
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User findById(Long id);
}
二、二者关系图解
[数据访问层抽象]
│
├── DAO(设计模式)
│ ├── JDBC实现(传统DAO)
│ ├── Hibernate实现
│ └── JPA实现
│
└── Mapper(MyBatis对DAO的实现)
├── XML配置方式
└── 注解配置方式
三、本质区别与联系
维度 | DAO | Mapper |
---|---|---|
抽象级别 | 设计模式 | 框架实现 |
实现方式 | 需编写接口和实现类 | 只需接口,自动生成实现 |
SQL管理 | 通常嵌入Java代码 | 分离在XML/注解中 |
技术绑定 | 通用,跨框架 | 特定于MyBatis |
灵活性 | 实现方式完全可控 | 受限于MyBatis机制 |
关键结论:Mapper是DAO模式在MyBatis框架中的特定实现形式,就像"汽车是交通工具的一种具体实现"。
四、演进历程
- 传统DAO时代(2000年代初)
- 每个DAO接口都需要对应的实现类
- 大量重复的JDBC样板代码
- 典型实现:Spring的JdbcTemplate
- ORM框架兴起(2005年后)
- Hibernate等框架简化数据访问
- 仍保持DAO模式的思想
- 实现类由框架部分简化
- MyBatis的Mapper革命(2010年后)
- 完全消除实现类
- SQL与Java代码彻底解耦
- 动态代理自动生成实现
- 现代演进(Spring Data时代)
public interface UserRepository extends JpaRepository<User, Long> {
// 结合了DAO和Mapper的优点
}
五、实践建议
何时使用传统DAO?
- 需要支持多种数据库
- 使用非MyBatis的持久层框架
- 需要精细控制数据访问逻辑
- 项目已有成熟的DAO基础设施
何时选择Mapper?
- 使用MyBatis技术栈
- 需要快速开发迭代
- 强调SQL的可维护性
- 希望减少样板代码
代码风格对比
传统DAO实现:
public class UserDaoImpl implements UserDao {
private final JdbcTemplate jdbcTemplate;
@Override
public User findById(Long id) {
String sql = "SELECT * FROM users WHERE id = ?";
return jdbcTemplate.queryForObject(sql, this::mapRow, id);
}
private User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getLong("id"));
// 其他字段映射...
return user;
}
}
MyBatis Mapper实现:
<!-- UserMapper.xml -->
<mapper namespace="com.example.UserMapper">
<select id="findById" resultType="User">
SELECT * FROM users WHERE id = #{id}
</select>
</mapper>
六、常见误区澄清
- 误区:“Mapper是DAO的替代品”
- 正解:Mapper是DAO的一种实现方式
- 误区:“用了MyBatis就不需要DAO模式”
- 正解:MyBatis的Mapper本身就是DAO模式的体现
- 误区:“DAO必须要有实现类”
- 正解:实现方式取决于框架,MyBatis通过动态代理省去实现类
结语:理解本质,灵活运用
理解DAO与Mapper的关系,关键在于区分设计模式与具体实现的层次差异。DAO作为经典模式,定义了数据访问层的基本规范;Mapper作为MyBatis的实现方式,提供了更便捷的开发体验。
在实际项目中,开发者应该:
- 在架构层面遵循DAO模式的思想
- 在实现层面根据技术栈选择合适方式
- 保持接口设计的业务语义明确
- 关注SQL的可维护性和性能优化
正如软件工程中许多抽象与实现的关系一样,掌握这种分层思考方式,将帮助我们在复杂的技术生态中保持清晰的设计思路。