【Spring】DAO 和 Repository 的区别

1.概述

通常,Repository 和 DAO 的实现被认为是可互换的,特别是在 以数据为中心 的应用程序中。这造成了对它们差异的混淆。

在本文中,我们将讨论 DAO 和 Repository 模式之间的差异。

在这里插入图片描述

2.DAO 模式

数据访问对象Data Access ObjectDAO)模式是数据持久性的抽象,被认为更接近于底层存储,而底层存储通常以表为中心。因此,在许多情况下,我们的 DAO 与数据库表匹配,允许更直接的方式从存储中 发送/检索 数据,从而隐藏丑陋的查询。

让我们来看看 DAO 模式的简单实现。

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 接口,该接口为 User 域提供简单的 CRUD 操作。

public interface UserDao {
    void create(User user);
    User read(Long id);
    void update(User user);
    void delete(String userName);
}

2.3 UserDaoImpl

最后,我们将创建实现 UserDao 接口的 UserDaoImpl 子类。

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);
    }
 
    // ...
}

在这里,为了简单起见,我们使用 JPA EntityManager 接口与底层存储交互,并为用户域提供数据访问机制。

3.Repository 模式

根据 Eric Evans 的《领域驱动设计》:Repository 是一种封装存储、检索和搜索行为的机制,它模拟对象的集合

同样,根据《企业应用架构模式》,它使用类似于集合的接口访问领域对象,在 领域层数据映射层 之间进行调解。换句话说,Repository 也处理数据并隐藏查询,与 DAO 类似。不过,它位于更高层次,更接近应用程序的业务逻辑。

因此,Repository 可以使用 DAO 从数据库获取数据并填充域对象。或者,它可以准备域对象中的数据,并使用 DAO 将其发送到存储系统以实现持久性。

让我们看看 User 域的 Repository 模式的简单实现。

3.1 UserRepository

首先,让我们创建用户存储库接口。

public interface UserRepository {
    User get(Long id);
    void add(User user);
    void update(User user);
    void remove(User user);
}

在这里,我们添加了一些常用方法,如 getaddupdateremove,以处理对象集合。

3.2 UserRepositoryImpl

然后,我们将创建 UserRepositoryImpl 类,提供 UserRepository 接口的实现。

public class UserRepositoryImpl implements UserRepository {
    private UserDaoImpl userDaoImpl;
    
    @Override
    public User get(Long id) {
        User user = userDaoImpl.read(id);
        return user;
    }
 
    @Override
    public void add(User user) {
        userDaoImpl.create(user);
    }
 
    // ...
}

在这里,我们使用 UserDaoImpl 从数据库中 发送/检索 数据。

到目前为止,我们可以说 DAO 和 Repository 的实现看起来非常相似,因为 User 类是一个 Anemic Domain。而且,Repository 只是数据访问层(DAO)的另一层。

🚀 只包含数据,不包含业务逻辑的类,就叫作 贫血模型(Anemic Domain Model)。贫血模型将数据与操作分离,破坏了面向对象的封装特性,是一种典型的面向过程的编程风格。

但是,DAO 似乎是 访问数据 的完美候选者,Repository 是 实现业务用例 的理想方式。

4.具有多个 DAO 的 Repository 模式

为了清楚地理解最后一句话,让我们加强 User 域来处理业务用例。

想象一下,我们想通过聚合一个用户的 Twitter 推文、Facebook 帖子等,为他准备一份社交媒体档案。

4.1 Tweet

首先,我们将创建具有一些保存推文信息的属性的 Tweet 类:

public class Tweet {
    private String email;
    private String tweetText;    
    private Date dateCreated;
 
    // getters and setters
}

4.2 TweetDao 和 TweetDaoImpl

然后,与 UserDao 类似,我们将创建允许获取推文的 TweetDao 界面:

public interface TweetDao {
    List<Tweet> fetchTweets(String email);    
}

同样,我们将创建 TweetDaoImpl 类,该类提供 fetchTweets 方法的实现:

public class TweetDaoImpl implements TweetDao {
    @Override
    public List<Tweet> fetchTweets(String email) {
        List<Tweet> tweets = new ArrayList<Tweet>();
        
        //call Twitter API and prepare Tweet object
        
        return tweets;
    }
}

在这里,我们将调用 Twitter API,使用用户的电子邮件获取其所有推文。

因此,在这种情况下,DAO 提供了一种使用第三方 API 的数据访问机制。

4.3 增强 User 域

最后,让我们创建 User 类的 UserSocialMedia 子类,以保留 Tweet 对象的列表。

public class UserSocialMedia extends User {
    private List<Tweet> tweets;
 
    // getters and setters
}

在这里,我们的 UserSocialMedia 是一个复杂的域,其中包含 User 域的属性。

4.4 UserRepositoryImpl

现在,我们将升级我们的 UserRepositoryImpl 类,以提供 User 域对象以及推文列表。

public class UserRepositoryImpl implements UserRepository {
    private UserDaoImpl userDaoImpl;
    private TweetDaoImpl tweetDaoImpl;
    
    @Override
    public User get(Long id) {
        UserSocialMedia user = (UserSocialMedia) userDaoImpl.read(id);
        
        List<Tweet> tweets = tweetDaoImpl.fetchTweets(user.getEmail());
        user.setTweets(tweets);
        
        return user;
    }
}

在这里,UserRepositoryImpl 使用 UserDaoImpl 提取用户数据,并使用 TweetDaoImpl 提取用户的推文。

然后,它聚合两组信息,并提供 UserSocialMedia 类的域对象,该对象对于我们的业务用例非常方便。因此,存储库依赖于 DAO 来访问来自各种源的数据。

同样,我们可以增强我们的用户域以保留 Facebook 帖子列表。

5.比较两种模式

现在我们已经看到了 DAO 和 Repository 模式的细微差别,让我们总结一下它们的区别:

  • DAO 是数据持久性的抽象。但是,Repository 是对象集合的抽象。
  • DAO 是一个较 级别的概念,更接近 存储系统。但是,Repository 是一个更 层次的概念,更接近 域对象
  • DAO 作为 数据映射/访问层 工作,隐藏丑陋的查询。但是,Repository 是 数据访问层 之间的一层,隐藏了整理数据和准备域对象的复杂性。
  • DAO 不能使用 Repository 实现。但是,Repository 可以使用 DAO 来访问底层存储。

而且,如果我们有一个贫血域,Repository 将只是一个 DAO。

此外,Repository 模式鼓励域驱动的设计,也为非技术团队成员提供了对数据结构的轻松理解。

  • 职责不同:Repository 负责数据访问的逻辑管理,封装与数据库的交互操作,包括数据的增删改查等。Repository 的设计目标是提供一个统一的接口,将数据访问逻辑与业务逻辑分离,使得业务逻辑更加清晰和可维护。DAO 更关注于数据访问的实现细节,与具体的数据库交互,实现数据的存储和检索等功能。
  • 抽象层次不同:Repository 提供一种更加抽象的数据访问方式,将数据访问的逻辑从业务逻辑中分离出来,使得业务逻辑与数据访问无关,降低代码耦合度,提高可维护性和可扩展性。Repository 定义一组通用的接口和方法,供业务模块调用。DAO 更接近于具体的数据访问实现,直接与数据库交互,实现数据的存储和检索等功能。DAO 的设计目标是提供一组具体的数据访问方法,以满足业务模块的具体需求。

6.结论

在本文中,我们探讨了 DAO 和 Repository 模式之间的差异。

首先,我们研究了 DAO 模式的基本实现。然后,我们看到了使用 Repository 模式的类似实现。

最后,我们研究了一个利用多个 DAO 的 Repository,它增强了域解决业务用例的能力。

因此,我们可以得出结论,当应用程序从以数据为中心转向面向业务时,Repository 模式被证明是一种更好的方法。

🚀 译自《DAO vs Repository Patterns

<think>好的,我现在需要帮助用户理解如何使用@Repository注解来声明DAO层组件。根据用户提供的引用内容,我需要结合这些例子来给出清晰的回答。 首先,用户引用了三个不同的来源。引用[1]提到通过继承JpaRepository来创建仓库接口,这可能Spring Data JPA相关。引用[2]展示了在DAO实现类上使用@Repository注解,并指定value属性。引用[3]则是一个更简单的例子,直接在类上添加@Repository,并包含一个查询方法。 我需要将这些信息整合起来,说明@Repository的不同用法。首先,@Repository的作用是将类标记为数据访问组件,同时处理异常转换,这是Spring框架的一个特性。然后,可能需要分步骤说明:比如基础用法、结合JPA的情况、以及自定义实现的情况。 注意到用户的问题是关于DAO层的声明,所以应该涵盖接口实现类两种情况。比如,引用[1]中的UserRepository是接口,而引用[2][3]是具体的实现类。这时候需要指出,在使用Spring Data JPA时,通常不需要写实现类,因为框架会自动生成。但如果是自定义的DAO,则需要用@Repository标注实现类。 还要注意代码示例的正确性。例如,引用[3]中的UserRepository类可能更接近一个传统的DAO实现,而不是使用JPA的情况。需要区分这两种情况,告诉用户在不同场景下如何应用注解。 另外,异常处理部分,需要提到@Repository注解会将数据访问异常转换为Spring的统一异常体系,这点应该在回答中强调,因为这是该注解的一个重要功能。 最后,生成的相关问题需要围绕用户可能的后续疑问,比如注解的作用、与其它注解的区别、异常处理机制等。 在组织回答时,应该分步骤,先介绍注解的作用,再给出不同情况下的示例,最后总结注意事项。确保每个示例都正确使用LaTeX格式,行内代码用$...$,独立代码块用```...```,但根据用户提供的系统指令,似乎代码块需要用更明确的格式,比如Python示例中的方式,用三个反引号包围。 可能还需要检查引用标注是否正确,比如引用[1]、[2]、[3]对应的内容是否在回答中正确引用,并在末尾添加引用标识。 现在需要把这些思考整理成一个结构清晰的中文回答,确保每个部分都符合用户的要求,并正确引用提供的参考资料。</think>### 使用@Repository注解声明DAO层组件的步骤及示例 #### 1. @Repository注解的作用 - **标记数据访问组件**:将类标识为DAO层组件,由Spring容器管理[^3]。 - **异常自动转换**:将数据访问层抛出的原生异常(如JDBC的SQLException)转换为Spring的统一数据访问异常体系[^3]。 --- #### 2. 基础用法示例 ##### 场景1:自定义DAO实现类 ```java import org.springframework.stereotype.Repository; @Repository // 标记为DAO组件 public class UserDao { public User findById(Long id) { // 模拟数据库查询 return new User(id, "John Doe"); } } ``` **说明**:直接标注在实现类上,Spring会自动扫描并注册为Bean。 --- ##### 场景2:结合Spring Data JPA接口 ```java import org.springframework.data.jpa.repository.JpaRepository; // 无需显式添加@Repository,因为JpaRepository接口的实现由Spring自动生成[^1] public interface UserRepository extends JpaRepository<User, Long> { // 可自定义查询方法 } ``` **说明**:继承`JpaRepository`的接口会自动被Spring识别为DAO组件,无需手动添加注解。 --- ##### 场景3:自定义DAO实现类(显式命名Bean) ```java @Repository("userDao") // 指定Bean名称 public class UserDaoImpl implements UserDao { @Override public User findUser(Long id) { // 具体数据库操作 return new User(id, "Alice"); } } ``` **说明**:通过`value`属性指定Bean名称,便于在其他地方通过名称注入[^2]。 --- #### 3. 结合其他注解的完整示例 ```java @Repository public class UserDaoImpl { @Autowired private JdbcTemplate jdbcTemplate; // 注入数据库操作工具 public User getUserById(Long id) { String sql = "SELECT * FROM users WHERE id = ?"; return jdbcTemplate.queryForObject(sql, new Object[]{id}, User.class); } } ``` **关键点**: - 通常与`@Autowired`或`@Resource`配合使用,注入依赖(如`JdbcTemplate`)。 - 若使用JPA,可直接通过方法命名规则定义查询,无需手动编写SQL。 --- #### 4. 注意事项 1. **包扫描配置**:确保`@Repository`所在包被`@ComponentScan`扫描到。 2. **与`@Component`的区别**:`@Repository`是`@Component`的特化,专用于数据访问层,且具有异常转换功能。 3. **事务管理**:DAO层通常结合`@Transactional`注解实现事务控制。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大数据与AI实验室

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值