通过账户信息操作加深对DTO,VO,BO理解

通过账户信息操作加深对DTO,VO,BO理解

好的!我来详细说明 DO、DTO、VO、BO、Query 这些概念在分层架构中的使用,并通过具体示例展示它们在各层的流转。

📚 概念定义

缩写 全称 作用域 描述
DO Domain Object DAO层 数据库实体,与表结构一一对应
DTO Data Transfer Object Service层 服务间数据传输对象
VO View Object Controller层 视图对象,返回给前端的数据
BO Business Object Service层 业务逻辑对象,组合多个DO
Query Query Object Controller→Service 查询条件封装对象

🏗️ 项目结构示例

controller-AccountController

config-PagingPlugConfig

entity-AccountBO,AccountDO,AccountDTO,AccountQuery,AccountVO

mapper-AccountMapper

service-AccountService,AccountServiceImpl

💻 代码示例

1. DO - 数据库实体 (DAO层)

java 复制代码
package com.geekmice.passparams.entity;

import lombok.Data;
import com.baomidou.mybatisplus.annotation.TableName;

/**
 * (Account)实体类
 * 与数据库表字段一致
 * @author pmb
 * @since 2025-12-01 17:10:09
 */
@Data
@TableName(value = "account")
public class AccountDO {
    private Integer id;
    private Integer uid;
    private Integer money;
}

2. Query - 查询条件对象

java 复制代码
package com.geekmice.passparams.entity;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.Data;

@Data
public class AccountQuery {
    private Integer id;
    private Integer uid;
    private Integer money;
    private Integer pageNum;
    private Integer pageSize;

    public LambdaQueryWrapper<AccountDO> buildQueryWrapper(){
        LambdaQueryWrapper<AccountDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.gt(AccountDO::getMoney,new Integer("60"));
        return wrapper;
    }
}

3. DTO - 数据传输对象

java 复制代码
package com.geekmice.passparams.entity;

import lombok.Data;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

/**
 * (Account)DTO类
 *
 * @author pmb
 * @since 2025-12-01 17:10:11
 */
@Data
public class AccountDTO  {
    private Integer id;
    private Integer uid;
    private Integer money;
}

4. BO - 业务对象

java 复制代码
package com.geekmice.passparams.entity;

import lombok.Data;

/**
 * (Account)实体类
 * 业务BO,包含多个DTO
 * @author pmb
 * @since 2025-12-01 17:10:09
 */
@Data
public class AccountBO {
    private AccountDTO accountDTO;

}

5. VO - 视图对象

java 复制代码
package com.geekmice.passparams.entity;

import lombok.Data;
import org.springframework.beans.BeanUtils;

/**
 * (Account)VO类
 *
 * @author pmb
 * @since 2025-12-01 17:10:11
 */
@Data
public class AccountVO {

    private Integer id;

    private Integer uid;

    private Integer money;

    public static AccountVO fromBO(AccountBO accountBO) {
        AccountVO accountVO = new AccountVO();
        AccountDTO accountDTO = accountBO.getAccountDTO();
        BeanUtils.copyProperties(accountDTO, accountVO);
        return accountVO;
    }

}

🔄 分层使用示例

Controller 层

java 复制代码
package com.geekmice.passparams.controller;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.geekmice.passparams.entity.AccountDTO;
import com.geekmice.passparams.entity.AccountQuery;
import com.geekmice.passparams.entity.AccountVO;
import com.geekmice.passparams.service.AccountService;
import com.geekmice.passparams.utils.R;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author pmb
 * @Desc (Account)表控制层
 * @Date 2025-12-01 17:10:08
 */
@RestController
@RequestMapping("account")
@RequiredArgsConstructor
public class AccountController {

    private final AccountService accountService;

    @GetMapping(value = "save")
    public R<Integer> save(@RequestBody AccountDTO accountDTO) {
        int count = accountService.add(accountDTO);
        return R.ok(count);
    }

    @GetMapping(value = "queryById")
    public R<AccountVO> queryById(@RequestBody AccountDTO accountDTO) {
        AccountVO result = accountService.getAccountById(accountDTO.getId());
        return R.ok(result);
    }

    @GetMapping(value = "pageAccounts")
    public R<IPage<AccountVO>> pageAccounts(@RequestBody AccountQuery accountQuery){
        IPage<AccountVO> res = accountService.selectPage(accountQuery);
        return R.ok(res);
    }
}

Service 层接口

复制代码
package com.geekmice.passparams.service;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.geekmice.passparams.entity.AccountDO;
import com.geekmice.passparams.entity.AccountDTO;
import com.geekmice.passparams.entity.AccountQuery;
import com.geekmice.passparams.entity.AccountVO;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.core.metadata.IPage;

import java.util.List;

/**
 * @Author pmb
 * @Desc (Account)表服务接口
 * @Date 2025-12-01 17:10:11
 */
public interface AccountService extends IService<AccountDO> {

    int add(AccountDTO accountDTO);

    AccountVO getAccountById(Integer id);

    IPage<AccountVO> selectPage(AccountQuery accountQuery);
}

Service 实现层

复制代码
package com.geekmice.passparams.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.geekmice.passparams.entity.*;
import com.geekmice.passparams.mapper.AccountMapper;
import com.geekmice.passparams.service.AccountService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

/**
 * @Author pmb
 * @Desc (Account)表服务实现类
 * @Date 2025-12-01 17:10:11
 */
@RequiredArgsConstructor
@Service("accountService")
public class AccountServiceImpl extends ServiceImpl<AccountMapper, AccountDO> implements AccountService {

    private final AccountMapper accountMapper;

    @Override
    public int add(AccountDTO accountDTO) {
        // DTO->DO
        AccountDO accountDO = convertToDO(accountDTO);
        // 持久化
        int count = accountMapper.insert(accountDO);
        // 受影响行数
        return count;
    }

    @Override
    public AccountVO getAccountById(Integer id) {
        // DO -> DTO
        AccountDO accountDO = accountMapper.selectById(id);
        assert accountDO != null;
        AccountDTO accountDTO = convertToDTO(accountDO);

        // 组装业务BO,实际业务为多个DTO
        AccountBO accountBO = assemebleAccountBO(accountDTO);

        // BO->VO
        return AccountVO.fromBO(accountBO);
    }

    @Override
    public IPage<AccountVO> selectPage(AccountQuery accountQuery) {
        // 分页
        Page<AccountDO> page = this.page(
                new Page<>(accountQuery.getPageNum(), accountQuery.getPageSize()),
                accountQuery.buildQueryWrapper()
        );
        // page<do> 转换ipage<vo>
        IPage<AccountVO> voPage = page.convert(accountDO -> {
            AccountVO accountVO = new AccountVO();
            BeanUtils.copyProperties(accountDO, accountVO);
            // 如果有日期时间需要格式化
            return accountVO;
        });
        return voPage;
    }

    private AccountBO assemebleAccountBO(AccountDTO accountDTO) {
        AccountBO accountBO = new AccountBO();
        accountBO.setAccountDTO(accountDTO);
        return accountBO;
    }

    private AccountDTO convertToDTO(AccountDO accountDO) {
        AccountDTO accountDTO = new AccountDTO();
        BeanUtils.copyProperties(accountDO, accountDTO);
        return accountDTO;
    }

    private AccountDO convertToDO(AccountDTO accountDTO) {
        AccountDO accountDO = new AccountDO();
        BeanUtils.copyProperties(accountDTO, accountDO);
        return accountDO;
    }
}

DAO 层

复制代码
package com.geekmice.passparams.mapper;

import com.geekmice.passparams.entity.AccountDO;
import com.geekmice.passparams.entity.AccountQuery;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

/**
 * @Author pmb
 * @Desc (Account)表数据库访问层
 * @Date 2025-12-01 17:10:08
 */
@Mapper
public interface AccountMapper extends BaseMapper<AccountDO> {

}

分页插件配置

java 复制代码
package com.geekmice.passparams.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class PagingPlugConfig {
    @Bean
    public MybatisPlusInterceptor getMybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

工具类

java 复制代码
package com.geekmice.passparams.utils;

import lombok.*;
import org.springframework.http.HttpStatus;

import java.io.Serializable;

@ToString
@NoArgsConstructor
@AllArgsConstructor
public class R<T> implements Serializable {

    private static final long serialVersionUID = 1L;

    @Getter
    @Setter
    private int code;

    @Getter
    @Setter
    private String msg;

    @Getter
    @Setter
    private T data;

    public static <T> R<T> ok() {
        return restResult(null, HttpStatus.OK.value(), HttpStatus.OK.getReasonPhrase());
    }

    public static <T> R<T> ok(T data) {
        return restResult(data, HttpStatus.OK.value(), HttpStatus.OK.getReasonPhrase());
    }

    public static <T> R<T> ok(T data, String msg) {
        return restResult(data, HttpStatus.OK.value(), msg);
    }

    public static <T> R<T> failed() {
        return restResult(null, HttpStatus.INTERNAL_SERVER_ERROR.value(), HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());
    }

    public static <T> R<T> failed(String msg) {
        return restResult(null, HttpStatus.INTERNAL_SERVER_ERROR.value(), msg);
    }

    public static <T> R<T> failed(T data) {
        return restResult(data, HttpStatus.INTERNAL_SERVER_ERROR.value(), HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());
    }

    public static <T> R<T> failed(T data, String msg) {
        return restResult(data, HttpStatus.INTERNAL_SERVER_ERROR.value(), msg);
    }

    public static <T> R<T> restResult(T data, int code, String msg) {
        R<T> apiResult = new R<>();
        apiResult.setCode(code);
        apiResult.setData(data);
        apiResult.setMsg(msg);
        return apiResult;
    }

}

🎯 关键流转路径总结

  1. 创建流程

    AccountDTOAccountDO→ 数据库 → 返回 Integer

  2. 查询流程

    AccountQueryAccountDO(分页) → AccountDTOAccountBOAccountVO→ JSON响应

  3. 对象转换原则

    • 向下转换(Controller→Service→DAO):逐步添加技术细节
    • 向上转换(DAO→Service→Controller):逐步剥离技术细节,增加业务含义
    • 每层职责单一:不跨层使用对象

这样的设计保证了各层的独立性和可维护性,是大型企业级应用的常见实践。

相关推荐
r***01381 小时前
Java进阶,时间与日期,包装类,正则表达式
java·mysql·正则表达式
APIshop1 小时前
Java爬虫第三方平台获取1688关键词搜索接口实战教程
java·开发语言·爬虫
k***12171 小时前
SpringCloud实战【九】 SpringCloud服务间调用
java·spring boot·spring cloud
请为小H留灯1 小时前
Java快捷健(详细版)
java·开发语言
执笔论英雄1 小时前
【RL】 ROLL Generate Scheduler
java·服务器·数据库
北郭guo1 小时前
垃圾回收底层原理【深入了解】
java·jvm·算法
D***44141 小时前
【SpringBoot】Spring Boot 项目的打包配置
java·spring boot·后端
5***E6851 小时前
Spring Boot接收参数的19种方式
java·spring boot·后端
u***B7921 小时前
Spring Boot的项目结构
java·spring boot·后端