深入浅出讲解JavaWeb后端的三层架构:Controller、Service、DAO 以及详解结合MyBatis和XML映射的DAO层设计

目录

1. 控制层(Controller)

          (1)职责及作用

          (2)实现        

        (3)总结

2. 服务层(Service)

        (1)职责与作用

        (2)实现

        (3)总结

3. 数据访问层(DAO)

        (1)职责及作用

        (2)实现

        (3)总结

4. 各层之间的关系

5. 三层架构的优势

6. 实际项目中的应用

7.总结——三层架构


1.MyBatis简介

2.DAO层职责回顾

3. DAO层结合MyBatis和XML映射的设计

        (1)项目结构

        (2)创建实体类

        (3)创建DAO接口

        (4)编写MyBatis XML映射文件

        (5)Spring配置

        (6)在Service层中使用DAO

        (7)总结——结合MyBatis和XML映射的DAO层设计


        Java Web 后端的三层架构,即控制层(Controller)、服务层(Service)、数据访问层(DAO),是企业级应用开发中常用的设计模式。这种分层架构将应用程序的不同职责分离开来,以提高代码的可维护性、可扩展性和测试性。下面我们将深入探讨这三层的职责、实现细节以及它们之间的关系。

1. 控制层(Controller)

(1)职责及作用

        控制层(Controller)是应用程序的入口,主要负责处理用户请求并返回响应。它直接与客户端(如浏览器、移动应用、第三方服务等)交互,通过接收 HTTP 请求、调用服务层来处理业务逻辑,并将处理结果(通常是视图或JSON数据)返回给客户端。控制层的核心职责可以归纳为以下几点:

        1.接收并解析请求:处理来自客户端的 HTTP 请求,解析请求参数、头信息、路径变量等。
        2.调用服务层:根据请求的类型(如获取数据、更新数据等),调用相应的服务层方法来处理具体的业务逻辑。
        3.返回响应:将服务层处理后的结果封装成响应对象,并通过 HTTP 协议返回给客户端。响应可以是视图页面、JSON、XML、文件下载等。
        4.处理异常:捕获在处理请求过程中可能发生的异常,并返回适当的错误信息或状态码。

(2)实现

        在Spring框架中,控制层通常使用@Controller或@RestController注解。@Controller用于返回视图页面,而@RestController是@Controller和@ResponseBody的组合,主要用于RESTful API开发,返回的是JSON或XML数据。

@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping("/{id}")
    public ResponseEntity getUserById(@PathVariable Integer id) {//id为路径参数
        User user = userService.getUserById(id);//调用服务层获取用户信息
        return Result.success(user); //返回json格式数据和用户信息
    }

    @PostMapping
    public ResponseEntity createUser(@RequestBody User user) {
        User newUser = userService.saveUser(user);//调用服务层创建新用户
        return Result.success(newUser);//返回json格式数据和新用户信息
    }
    //负责进行异常处理
    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity handleUserNotFound(UserNotFoundException ex) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
    }
}

        UserController负责处理用户相关的请求。getUserById方法接收一个用户ID(用id接收),并调用UserService来获取用户数据,然后将数据返回给客户端。createUser方法接收一个用户对象,调用UserService处理创建新用户的操作。

        Result类为自定义的响应数据类:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
    private Integer code;//响应码,1 代表成功; 0 代表失败
    private String msg;  //响应信息 描述字符串
    private Object data; //返回的数据
    //增删改 成功响应
    public static Result success(){
        return new Result(1,"success",null);
    }
    //查询 成功响应
    public static Result success(Object data){
        return new Result(1,"success",data);
    }
    //失败响应
    public static Result error(String msg){
        return new Result(0,msg,null);
    }
}

(3)总结

        控制层(Controller):负责接收请求,调度服务层并返回结果。主要是处理HTTP请求和响应。

2. 服务层(Service)

(1)职责与作用

        服务层(Service)是整个应用程序的核心部分,负责处理所有的业务逻辑。它在控制层和数据访问层之间起到中介作用,封装了业务流程,并协调DAO层的数据访问操作。服务层的职责包括:

        1.业务逻辑实现:将业务需求转化为可执行的逻辑操作,包括数据的校验、转换、计算等。
        2.事务管理:在处理涉及多个数据库操作的业务时,确保这些操作要么全部成功,要么全部失败(即事务的原子性)。
        3.调用DAO层:与数据访问层(DAO层)交互,完成数据的增删改查等各种操作。
        4.封装复杂逻辑:将复杂的业务流程封装在服务层,控制层只需要调用相应的服务方法,而不关心其内部实现细节。
        5.集成其他服务:在大型系统中,服务层可能还负责与外部服务的集成,例如调用其他微服务、消息队列、第三方API等。

(2)实现

        服务层通常使用@Service注解来标识,它可以独立于控制层进行单元测试。典型的服务层类通常包含多个业务方法,这些方法通过注入DAO层对象(通过注解@Autowired 和 @Resource实现,了解其区别可移步http://t.csdnimg.cn/Sjc9D)来完成数据操作。

@Service
public class UserService {
    @Autowired
    private UserDao userDao;

    @Transactional
    public User getUserById(Integer id) {
        return userDao.getById(id);//调用Dao层通过ID查找用户
    }

    @Transactional
    public User saveUser(User user) {
        //业务逻辑部分代码:如检查用户名是否已存在,设置用户创建时间等
        return userDao.save(user);
    }

    //其他处理方法代码
}

        在这个UserService类中,我们实现了根据ID查找和保存用户的相关业务逻辑部分代码。另外@Transactional注解用于管理事务,确保该注解下的方法内的数据库操作要么全部成功,要么全部回滚。

(3)总结

        服务层(Service):负责业务逻辑的处理和事务管理,是应用的核心层。

3. 数据访问层(DAO)

(1)职责及作用

        数据访问层(DAO:Data Access Object)负责直接与数据库进行交互。它封装了所有与数据库相关的操作,主要职责如下:

        1.CRUD 操作:提供增删改查数据库记录的操作。
        2.数据库连接管理:管理与数据库的连接以及连接池的使用(在现代框架中,这通常由ORM工具或框架自动管理)。
        3.数据转换:将数据库中的数据转换为应用程序中的对象(DTO,Data Transfer Object),以及将对象转换为适合存储在数据库中的格式。
        4.查询封装:将复杂的数据库查询封装为易于调用的方法,因此控制层(Controller)和服务层(Service)不会直接接触SQL语句,即不必关心具体的数据库实现。

(2)实现

        DAO层通常使用@Repository注解,并且依赖于ORM(如Hibernate或JPA)来简化数据访问。DAO层可以直接使用JPA的JpaRepository接口,或者自定义查询方法。

@Repository
public interface UserDao extends JpaRepository {
    // 继承JpaRepository后,自动获得基本的CRUD操作
    // 自定义查询方法
    User getById(Integer id);

     // 自定义的JPQL或Native SQL查询
    @Query("SELECT u FROM User u WHERE u.email = ?1")
    User findByEmail(String email);
}

        如上,UserDao类继承了JpaRepository接口,因此无需手动编写基本的CRUD方法。同时可以通过定义方法签名的方式来自动生成查询语句,或者使用@Query注解来编写自定义查询。

(3)总结

        数据访问层(DAO):负责数据的持久化,与数据库直接交互,执行CRUD操作。

4. 各层之间的关系

        Controller 层-- >Service 层-- >DAO 层:
        控制层负责接收客户端请求并传递给服务层,服务层在处理业务逻辑后,可能需要访问数据,于是调用DAO层进行数据库操作。
        DAO 层-->Service 层-- > Controller 层:
        DAO层将数据返回给服务层,服务层进行必要的处理后,将结果返回给控制层,最终控制层将响应发送给客户端。

        松耦合设计:每一层都只与它的下一级直接交互,这种设计使得代码更加模块化和可维护。每一层都高度封装,如果业务逻辑改变,只需修改服务层;如果数据库结构改变,只需修改DAO层即可,无需大动干戈。

5. 三层架构的优势

        1.职责分离:将不同的职责分布到不同的层中,使得每一层的职责单一且明确。
        2.可维护性:由于每一层都是独立的模块,维护和修改代码时可以做到“牵一发而不动全身”。
        3.可测试性:可以针对每一层分别进行单元测试。例如在不涉及数据库的情况下测试服务层的业务逻辑。
        4.可扩展性:可以很便捷地扩展功能。例如引入新的服务或数据源时,只需增加新的Service或DAO类,而不影响现有的代码。
        5.松耦合:各层之间通过接口和依赖注入进行通信,降低了代码的耦合性。

6. 实际项目中的应用

        在实际项目中,三层架构通常配合其他设计模式和架构模式一起使用:
        1.DTO(Data Transfer Object)模式:在控制层和服务层之间使用DTO来传递数据,避免直接暴露实体类。
        2.AOP(面向切面编程):用于日志记录、权限检查、事务管理等横切关注点,与三层架构无缝集成。
        3.Spring Boot:通过简化配置和集成依赖,使三层架构的实现更加便捷。

7.总结——三层架构

        三层架构在实际项目中不仅有助于组织代码结构,确保清晰的职责划分,还极大地提升了代码的可维护性、可扩展性和测试性。它使得应用程序的开发、维护和扩展变得更加容易,是Java Web开发中的一项最佳实践。通过灵活应用三层架构,开发者可以构建出健壮、可扩展和易于维护的企业级应用。

至此,三层架构讲解结束~ 下面开始讲解结合MyBatis和XML映射的DAO 层设计方式


1.MyBatis简介

        MyBatis 是一个优秀的持久层框架,它通过消除几乎所有的 JDBC 代码以及手动设置参数和获取结果集的工作,使得开发者能够专注于 SQL 语句本身。MyBatis 允许你以 XML 文件或注解的方式来编写 SQL 语句和映射规则,相当便捷。

深入浅出讲解JavaWeb后端的三层架构:Controller、Service、DAO 以及详解结合MyBatis和XML映射的DAO层设计_第1张图片

                                                       网址: MyBatis中文网

2.DAO层职责回顾

        忘了没关系~这里稍作总结:DAO层的主要职责是与数据库进行交互,执行 CRUD 操作(创建、读取、更新、删除)。在 MyBatis 中,DAO 层可以很便捷的通过映射文件(XML 文件)或者直接通过注解来定义这些操作。

3. DAO层结合MyBatis和XML映射的设计

(1)项目结构

        首先,让我们看一下项目中 DAO 层结合 MyBatis 的典型结构:

src/main/java
│
├── com.example.project.dao
│   └── UserDao.java  # 自定义的DAO层接口
│
├── com.example.project.entity
│   └── User.java     # 自定义的实体类
│
src/main/resources
│
├── com.example.project.dao # 同名
│   └── UserDao.xml         # MyBatis XML 映射文件
│
└── application.properties  # Spring Boot 配置文件

(2)创建实体类

        实体类代表数据库中的表结构,以User表为例,创建一个对应的User类:

package com.example.project.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String username;
    private String password;
    private String email;
    
}
@Data :自动为类生成所有字段的getter和setter方法,toString()方法,equals()和hashCode()方法,以及一个requiredArgsConstructor(包含final和@NonNull字段的构造函数)。
@AllArgsConstructor :自动生成一个包含所有字段(包括final和非final字段)的构造函数。
@NoArgsConstructor :自动生成一个无参数的默认构造函数。如果类中有final字段,默认生成的构造函数会让这些字段保持未初始化状态(即使用其默认值)。 

(3)创建DAO接口

        在 MyBatis 中,DAO 接口用于定义数据访问操作。我们可以在接口中定义方法,然后通过 XML 映射文件为每个方法编写对应的 SQL 语句。

package com.example.project.dao;

import com.example.project.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface UserDao {

    User findById(@Param("id") Long id);

    List findAll();

    void insert(User user);

    void update(User user);

    void deleteById(@Param("id") Long id);
}

        这里使用了@Mapper注解来标识这个接口是 MyBatis 的一个 Mapper(即 DAO)。
        @Param注解用于绑定方法参数到 SQL 语句中使用的参数。

(4)编写MyBatis XML映射文件

        在这里推荐IntelliJ IDEA里面的一个插件 MyBatisX,可以帮助我们便捷快速的编写MyBatis 的 XML 映射文件~

深入浅出讲解JavaWeb后端的三层架构:Controller、Service、DAO 以及详解结合MyBatis和XML映射的DAO层设计_第2张图片

        MyBatis 的 XML 映射文件用于将 DAO 接口中的方法与具体的 SQL 语句进行关联,以下是一个简单的 UserDao.xml 文件:




    
    
        
        
        
        
    
    
    
    
    
    
    
        INSERT INTO users (username, password, email)
        VALUES (#{username}, #{password}, #{email})
    
    
    
        UPDATE users
        SET username = #{username},
            password = #{password},
            email = #{email}
        WHERE id = #{id}
    
    
    
        DELETE FROM users
        WHERE id = #{id}
    

XML 映射文件解析:
        :根元素,namespace属性指定了这个映射文件对应的 DAO 接口的全限定名。
        :定义了数据库结果如何映射到实体类的字段。id用于标识主键,result用于映射普通字段。