1. 概述
在实际开发中,尤其是数据驱动型应用里,DAO 和 Repository 的实现常常被混用,甚至当成可以互换的组件。这导致很多开发者对两者的边界感到困惑。
本文将深入剖析 DAO 模式 和 Repository 模式 的本质区别,帮你理清什么时候该用哪个,避免在架构设计上“踩坑”。
2. DAO 模式
DAO(Data Access Object)模式,中文叫 数据访问对象,它的核心是对数据持久化的抽象,并且通常更贴近底层存储结构,比如数据库表。
✅ 通俗点说:DAO 就是数据库表的“代言人”,它负责把 Java 对象和数据库记录之间做映射,屏蔽掉 SQL 等底层细节。
我们来看一个简单的例子。
2.1. User 实体类
先定义一个基本的 User
领域对象:
public class User {
private Long id;
private String userName;
private String firstName;
private String email;
// getters and setters
}
2.2. UserDao 接口
接着定义 UserDao
接口,提供标准的 CRUD 操作:
public interface UserDao {
void create(User user);
User read(Long id);
void update(User user);
void delete(String userName);
}
⚠️ 注意:这里的 delete
是根据 userName
删除,不是按主键,说明 DAO 的操作可以非常灵活,贴近实际表结构。
2.3. UserDaoImpl 实现类
最后是实现类,使用 JPA 的 EntityManager
来操作数据库:
public class UserDaoImpl implements UserDao {
private final EntityManager entityManager;
@Override
public void create(User user) {
entityManager.persist(user);
}
@Override
public User read(Long id) {
return entityManager.find(User.class, id);
}
@Override
public void update(User user) {
entityManager.merge(user);
}
@Override
public void delete(String userName) {
// 构造查询并执行删除
entityManager.createQuery("DELETE FROM User u WHERE u.userName = :userName")
.setParameter("userName", userName)
.executeUpdate();
}
}
📌 这里我们用 EntityManager
直接和数据库交互,DAO 的职责非常明确:只管数据怎么存、怎么取。
3. Repository 模式
Repository 模式的定义来自 DDD(领域驱动设计)鼻祖 Eric Evans 的著作:
“Repository 是一种封装了存储、检索和查询行为的机制,它模拟了一个对象集合。”
简单粗暴理解:Repository 看起来就像一个内存中的集合,你可以 add
、get
、remove
,而不用关心背后是查数据库、调 API 还是读文件。
根据《企业应用架构模式》的描述,Repository 的作用是:
“在领域层和数据映射层之间做协调,提供类似集合的接口来访问领域对象。”
所以,Repository 的定位比 DAO 更高,更贴近业务逻辑,而不是数据库结构。
它通常会调用一个或多个 DAO 来组装数据,最终返回一个完整的领域对象。
3.1. UserRepository 接口
我们定义一个 UserRepository
接口:
public interface UserRepository {
User get(Long id);
void add(User user);
void update(User user);
void remove(User user);
}
看起来和 DAO 很像?别急,关键在实现。
3.2. UserRepositoryImpl 实现类
public class UserRepositoryImpl implements UserRepository {
private UserDaoImpl userDaoImpl;
@Override
public User get(Long id) {
return userDaoImpl.read(id);
}
@Override
public void add(User user) {
userDaoImpl.create(user);
}
@Override
public void update(User user) {
userDaoImpl.update(user);
}
@Override
public void remove(User user) {
userDaoImpl.delete(user.getUserName());
}
}
乍一看,这不就是套了个壳?
✅ 没错,如果领域对象是“贫血”的(只有 getter/setter,没有行为),那 Repository 确实和 DAO 差不多。
但它的真正价值体现在复杂业务场景中。
4. Repository 模式结合多个 DAO
为了体现 Repository 的优势,我们来模拟一个真实业务场景:
用户登录后,要展示他的社交资料,包括 Twitter 推文、Facebook 帖子等。
这时候,单靠一个 User
表远远不够,需要聚合多个数据源。
4.1. Tweet 类
先定义推文实体:
public class Tweet {
private String email;
private String tweetText;
private Date dateCreated;
// getters and setters
}
4.2. TweetDao 与 TweetDaoImpl
定义 DAO 接口:
public interface TweetDao {
List<Tweet> fetchTweets(String email);
}
实现类中调用第三方 API(比如 Twitter)获取数据:
public class TweetDaoImpl implements TweetDao {
@Override
public List<Tweet> fetchTweets(String email) {
List<Tweet> tweets = new ArrayList<>();
// 调用 Twitter API,根据 email 查询用户推文
// mock: 实际项目中会通过 HTTP 客户端调用 REST API
Tweet tweet = new Tweet();
tweet.setEmail(email);
tweet.setTweetText("Hello, this is my first tweet!");
tweet.setDateCreated(new Date());
tweets.add(tweet);
return tweets;
}
}
⚠️ 注意:这里的 DAO 不再操作数据库,而是封装了对外部服务的调用。DAO 的本质是“数据访问”,不局限于数据库。
4.3. 扩展 User 领域模型
我们创建一个增强版的 UserSocialMedia
类:
public class UserSocialMedia extends User {
private List<Tweet> tweets;
// getters and setters
}
这个类代表了一个完整的业务概念:带社交内容的用户资料。
4.4. 升级 UserRepositoryImpl
现在,Repository 的价值体现出来了:
public class UserRepositoryImpl implements UserRepository {
private UserDaoImpl userDaoImpl;
private TweetDaoImpl tweetDaoImpl;
@Override
public User get(Long id) {
// 1. 从数据库加载用户基本信息
UserSocialMedia user = (UserSocialMedia) userDaoImpl.read(id);
// 2. 从 Twitter 获取推文
List<Tweet> tweets = tweetDaoImpl.fetchTweets(user.getEmail());
user.setTweets(tweets);
// 3. 返回完整的业务对象
return user;
}
// 其他方法略...
}
✅ 关键点:
- Repository 协调了多个 DAO(
UserDaoImpl
、TweetDaoImpl
) - 聚合来自不同数据源的信息
- 返回一个完整的、可直接用于业务逻辑的领域对象
这就是 Repository 的核心价值:为业务服务,而不是为数据库服务。
5. 两种模式对比总结
特性 | DAO 模式 | Repository 模式 |
---|---|---|
定位 | 数据访问层,贴近存储 | 领域层,贴近业务逻辑 |
抽象目标 | 数据库表或数据源 | 对象集合(领域对象) |
层级 | 较低,靠近数据库 | 较高,靠近业务 |
依赖关系 | 不依赖 Repository | 可以依赖一个或多个 DAO |
职责 | 执行 CRUD,隐藏 SQL | 聚合数据,构建完整领域对象 |
适用场景 | 简单 CRUD、数据驱动应用 | 复杂业务逻辑、领域驱动设计 |
📌 核心结论:
- ✅ DAO 是基础设施,Repository 是业务抽象
- ✅ Repository 可以组合多个 DAO,但 DAO 不该依赖 Repository
- ✅ 如果你的领域模型是贫血的(anemic),那 Repository 很可能只是 DAO 的马甲
- ✅ 真正的 Repository 应该体现领域逻辑,比如
findActiveUsersWithPremiumSubscription()
6. 结论
DAO 和 Repository 并不是非此即彼的选择,而是不同层次的抽象工具。
- 当你的应用是简单的 CRUD 时,用 DAO 完全够用,简单直接。
- 但当你开始做领域驱动设计、业务逻辑变复杂时,Repository 才真正发挥价值。
📌 一句话总结:
DAO 关心“数据怎么存”,Repository 关心“业务需要什么数据”。
合理使用两者,才能让代码既灵活又可维护。
所有示例代码已上传至 GitHub:https://github.com/baeldung/design-patterns-architectural