深入浅出讲解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<User> getUserById(@PathVariable Integer id) {//id为路径参数
        User user = userService.getUserById(id);//调用服务层获取用户信息
        return Result.success(user); //返回json格式数据和用户信息
    }

    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody User user) {
        User newUser = userService.saveUser(user);//调用服务层创建新用户
        return Result.success(newUser);//返回json格式数据和新用户信息
    }
    //负责进行异常处理
    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<String> 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<User, Long> {
    // 继承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 语句和映射规则,相当便捷。

                                                       网址: 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<User> 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 映射文件~

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

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.project.dao.UserDao">
    <!-- Result Map:用于定义如何将数据库查询结果映射到实体类 -->
    <resultMap id="userResultMap" type="com.example.project.entity.User">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="password" column="password"/>
        <result property="email" column="email"/>
    </resultMap>
    <!-- 根据ID查询用户 -->
    <select id="findById" resultMap="userResultMap">
        SELECT id, username, password, email
        FROM users
        WHERE id = #{id}
    </select>
    <!-- 查询所有用户 -->
    <select id="findAll" resultMap="userResultMap">
        SELECT id, username, password, email
        FROM users
    </select>
    <!-- 添加新用户 -->
    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO users (username, password, email)
        VALUES (#{username}, #{password}, #{email})
    </insert>
    <!-- 修改用户信息 -->
    <update id="update">
        UPDATE users
        SET username = #{username},
            password = #{password},
            email = #{email}
        WHERE id = #{id}
    </update>
    <!-- 根据ID删除用户 -->
    <delete id="deleteById">
        DELETE FROM users
        WHERE id = #{id}
    </delete>
</mapper>

XML 映射文件解析:
        <mapper>:根元素,namespace属性指定了这个映射文件对应的 DAO 接口的全限定名。
        <resultMap>:定义了数据库结果如何映射到实体类的字段。id用于标识主键,result用于映射普通字段。
        <select>:用于执行查询操作,resultMap属性指定了查询结果的映射规则。
        <insert>:用于插入数据。useGeneratedKeys和keyProperty属性用于处理数据库自动生成的主键值。
        <update>:用于更新数据。
        <delete>:用于删除数据。

(5)Spring配置

        在Spring Boot项目中,MyBatis的配置通常在 application.properties 或 application.yml 文件中完成。

以下是一个典型的application.properties文件中的配置:

# 数据库连接配置
spring.datasource.url=jdbc:mysql://localhost:3306/your_database?useSSL=false&serverTimezone=UTC
spring.datasource.username=your_username
spring.datasource.password=your_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# MyBatis 配置
mybatis.mapper-locations=classpath:mybatis/*.xml
mybatis.type-aliases-package=com.example.project.entity

配置项解析:

        spring.datasource.*:用于配置数据库连接信息。
        mybatis.mapper-locations:指定 MyBatis 映射文件的位置。
        mybatis.type-aliases-package:指定实体类所在的包,这样在 XML 中可以直接使用类名而不必写全限定名。
        包含your_字段的是你自己定义的哈~

以下是一个典型的application.yml文件中的配置:

spring:
  # 数据库连接信息配置
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver    # 数据库驱动程序,这里是MySQL的JDBC驱动
    url: jdbc:mysql://localhost:3306/your_database # 数据库连接的URL,指定了数据库服务器地址和数据库名称
    username: your_username                        # 数据库连接的用户名
    password: your_password                        # 数据库连接的密码

  # Servlet相关配置
  servlet:
    multipart:
      max-file-size: 10MB       # 单个文件上传的最大限制,这里限制为10MB
      max-request-size: 100MB   # 单次请求中所有文件上传的总大小限制,这里限制为100MB

# MyBatis配置
mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl   # 指定MyBatis的日志实现,输出到标准输出控制台
    map-underscore-to-camel-case: true                      # 启用驼峰命名法转换,例如数据库字段`user_name`将映射为实体类中的 `userName`
  mapper-locations: classpath:mappers/*xml                  # 指定MyBatis的Mapper XML文件的位置,这里指向`mappers`目录下的所有XML文件
  type-aliases-package: com.it.mybatis.entity               # 指定MyBatis实体类的包名,MyBatis会自动将包中的类映射为数据库表

配置项解析:

        数据库连接信息配置:spring.datasource部分定义了如何连接到你的MySQL数据库,包括数据库驱动、连接URL、用户名和密码。
        Servlet相关配置:spring.servlet.multipart部分定义了文件上传的大小限制,max-file-size和max-request-size分别控制单个文件和一次请求中所有文件的大小。
        MyBatis配置:mybatis.configuration部分配置了MyBatis的日志输出方式和命名策略;mapper-locations指定了Mapper XML文件的存放路径;type-aliases-package则指定了实体类所在的包名,方便MyBatis自动进行映射。
        包含your_字段的是你自己定义的哈~

(6)在Service层中使用DAO

        在服务层中,可以注入DAO接口来调用相应的数据访问方法:

package com.example.project.service;

import com.example.project.dao.UserDao;
import com.example.project.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    @Transactional(readOnly = true)
    public User getUserById(Long id) {
        return userDao.findById(id);
    }

    @Transactional(readOnly = true)
    public List<User> getAllUsers() {
        return userDao.findAll();
    }

    @Transactional
    public User createUser(User user) {
        userDao.insert(user);
        return user;
    }

    @Transactional
    public void updateUser(User user) {
        userDao.update(user);
    }

    @Transactional
    public void deleteUser(Long id) {
        userDao.deleteById(id);
    }
}

        这里UserService通过@Autowired 注解注入UserDao 来调用数据库操作方法。
        @Transactional 注解确保数据操作的原子性,如果出现异常可以进行事务回滚。

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

在实际开发中有以下优势:

        1.灵活性:可以编写高度定制化的 SQL 语句,以应对复杂的查询或操作需求。
        2.分离关注点:通过 XML 映射文件将 SQL 语句与 Java 代码分离,使代码更清晰、更易维护。
        3.可扩展性:易于集成其他数据库操作,如存储过程调用、批量操作等。

建议:

        1.在 XML 文件中尽量避免冗长复杂的 SQL,保持 SQL 的可读性。
        2.充分利用 MyBatis 的动态 SQL 功能,如 <if>、<choose> 等来实现复杂条件查询。
        3.使用 @Mapper 注解和接口定义的方法,可以清晰定义 DAO 层的职责,使得代码结构更加清晰。

如有不足,欢迎指正,一起学习~

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值