DAO 类的职责与设计原则

1. DAO 的核心职责

DAO(Data Access Object,数据访问对象)的主要职责是封装对数据的访问逻辑,但它与纯粹的数据实体类(如 DTO、POJO)不同,也与 Service 业务逻辑层不同。

DAO 应该做什么?

✅ 数据访问操作

  • 执行数据库 CRUD(增删改查)操作

  • 提供查询接口(如 findByIdfindAllsavedelete

  • 处理数据库事务(通常由 @Transactional 管理)

✅ 数据转换

  • 将数据库实体(如 UserEntity)转换为业务对象(如 User

  • 处理数据库特有的数据类型(如 LocalDateTime 转换)

✅ SQL/HQL/JPQL 管理

  • 编写 SQL 查询(如 @Query 或 XML 映射)

  • 处理分页、排序等数据库操作

DAO 不应该做什么?

❌ 业务逻辑(属于 Service 层):

  • 例如:计算订单折扣、验证用户权限等

❌ HTTP 请求/响应处理(属于 Controller 层):

  • 例如:解析 @RequestBody、返回 ResponseEntity

❌ 直接暴露数据库细节

  • 例如:不应该让上层直接接触 Connection 或 PreparedStatement


2. DAO 与实体类的区别

类别 DAO 类 实体类(Entity/DTO)
用途 封装数据访问逻辑 表示数据结构(数据库表或 API 数据)
是否含逻辑 有简单的查询逻辑 通常只是数据容器(纯 POJO)
示例 UserRepository.findByName() User { id, name, email }
Spring 注解 @Repository @Entity(JPA)或 无注解(DTO)

3. DAO 的典型实现方式

(1) Spring Data JPA(推荐)

@Repository
public interface UserRepository extends JpaRepository {
    // 自动实现基本 CRUD
    List findByName(String name);  // 方法名自动解析为 SQL
    
    @Query("SELECT u FROM User u WHERE u.email LIKE %?1%")
    List findByEmailContaining(String email);
}

特点

  • 方法名自动生成查询(如 findByName → SELECT * FROM user WHERE name = ?

  • 支持 @Query 自定义 SQL/JPQL

(2) MyBatis(SQL 更灵活)

@Repository
public interface UserMapper {
    @Select("SELECT * FROM user WHERE id = #{id}")
    User findById(Long id);
    
    @Insert("INSERT INTO user(name, email) VALUES(#{name}, #{email})")
    void insert(User user);
}

特点

  • 直接写 SQL,适合复杂查询

  • 需配合 XML 或注解映射

 (3) 传统 JDBC(低层控制)

@Repository
public class UserDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public User findById(Long id) {
        String sql = "SELECT * FROM user WHERE id = ?";
        return jdbcTemplate.queryForObject(sql, new UserRowMapper(), id);
    }
}

特点

  • 完全手动控制 SQL 和执行过程

  • 适合需要高度优化的场景

4. DAO 与 Service 的分工

DAO 层

@Repository
public interface OrderRepository extends JpaRepository {
    List findByUserId(Long userId);
}

Service 层(业务逻辑)

@Service
public class OrderService {
    @Autowired
    private OrderRepository orderRepository;
    
    public BigDecimal calculateTotalPrice(Long userId) {
        List orders = orderRepository.findByUserId(userId);
        return orders.stream()
            .map(Order::getPrice)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
}

关键区别

  • DAO 只关心怎么查数据findByUserId

  • Service 关心业务计算(如汇总订单金额)


5. 最佳实践

  1. 保持 DAO 单一职责

    • 只做数据访问,不混入业务逻辑。

  2. 使用接口 + 实现

    • 便于替换数据源(如从 MySQL 切到 MongoDB)。

  3. 避免“贫血 DAO”

    • 如果 DAO 只是简单调用 JPA/MyBatis,可以直接用 Repository,无需额外封装。

  4. 事务管理

    • 事务注解(@Transactional)通常放在 Service 层。

DAO(Data Access Object)层 在大多数现代Java应用中,特别是使用 MyBatis 或 JPA 框架时,通常对应你所说的 Mapper 类。不过具体实现方式可能略有不同,取决于你使用的技术栈。下面详细解释它们的关系和区别:

DAO 层和 Mapper 的关系

概念 DAO(数据访问对象) Mapper(MyBatis 术语)
定位 数据访问层,封装数据库操作 MyBatis 对 DAO 的实现方式
技术实现 可以是接口或类 通常是接口(MyBatis)或 XML 映射文件
Spring 注解 @Repository @Mapper(MyBatis)或 @Repository
典型方法 insert(User user)findById(Long id) @Select@Insert 等 SQL 注解方法

结论

  • DAO 是一个设计概念,表示数据访问层。

  • Mapper 是 MyBatis 对 DAO 的具体实现方式(用接口+注解或XML定义SQL)。

  • 在 Spring + MyBatis 组合中,Mapper ≈ DAO


总结

  • DAO 类:负责数据访问(查询/存储),有简单逻辑(如 SQL 拼接),但不包含业务规则

  • 实体类:纯数据结构,无逻辑,用于表示数据库表或 API 数据。

  • Service 类:处理业务逻辑,调用 DAO 并组合数据。

合理分层能让代码更清晰、更易维护! 

你可能感兴趣的:(原型模式)