当前位置: 首页 > news >正文

Java开发经验——阿里巴巴编码规范实践解析8

摘要

本文主要介绍了阿里巴巴编码规范在Java开发中的实践解析,强调了在表查询中不使用“*”作为查询字段列表的重要性,指出其会增加查询分析成本、浪费网络传输资源、降低可维护性、不利于缓存且存在安全隐患。同时,还提出了正例推荐写法,包括明确指定查询字段、在不同框架中的体现以及最佳实践建议。此外,还涉及了POJO类布尔属性命名规范、resultMap的使用、sql.xml配置参数的规范、数据更新接口的规范、事务的合理使用、分层结构推荐、异常处理规约以及分层领域模型规约等内容,旨在帮助Java开发者更好地遵循编码规范,提高代码质量和可维护性。

1. 【强制】在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明。

说明:

  1. 增加查询分析器解析成本。
  2. 增减字段容易与 resultMap 配置不一致。
  3. 无用字段增加网络消耗,尤其是 text 类型的字段。

1.1. 为什么不能使用 SELECT *

问题类别

详细说明

❌ 性能问题

解析 SQL 时会去查字段元数据,字段越多解析成本越大,尤其在复杂表连接中更明显。

❌ 网络传输浪费

加载了不需要的字段(如 TEXT、BLOB 等大字段),增加了数据库到应用间的网络 IO。

❌ 可维护性差

表结构变更(加字段、删字段)时容易造成 resultMap

、DTO 等解析失败或字段映射错乱。

❌ 难以做缓存

无法根据字段内容判断缓存命中,增加缓存粒度模糊性。

❌ 安全隐患

返回了原本不该暴露给前端/日志的敏感字段,例如密码、身份证、手机号等。

1.2. ✅ 正例推荐写法

1.2.1. ✅ 推荐 SQL 写法(明确字段)

SELECT id, name, email FROM user WHERE status = 1;

1.2.2. ❌ 错误写法(不可控)

SELECT * FROM user WHERE status = 1;

1.3. ✅ 在不同框架中的体现

1.3.1. MyBatis

<!-- resultMap 需字段一一对应 -->
<select id="getUser" resultMap="BaseResultMap">SELECT id, name, email FROM user WHERE id = #{id}
</select>

1.3.2. JPA / ORM

  • 虽然有默认实体映射,但最好使用 JPQL 或 Projection 显式列出字段:
@Query("SELECT u.id, u.name FROM User u WHERE u.status = 1")
List<UserDTO> findActiveUsers();

1.4. ✅ 最佳实践建议

场景

建议

DTO/VO 场景

只查询所需字段,避免加载多余字段,提升效率

高并发接口

尤其注意排除 TEXT/BLOB 等大字段

日志与敏感数据

严格控制查询字段,避免敏感信息误打印到日志或返回

2. 【强制】POJO 类的布尔属性不能加 is,而数据库字段必须加 is_,要求在 resultMap 中进行字段与属性之间的映射。

说明:参见定义 POJO 类以及数据库字段定义规定,在 sql.xml 增加映射,是必须的。

3. 【强制】不要用 resultClass 当返回参数,即使所有类属性名与数据库字段一一对应,也需要定义<resultMap>;反过来,每一个表也必然有一个<resultMap>与之对应。

说明:配置映射关系,使字段与 DO 类解耦,方便维护。

3.1. ✅ 规范要点总结

  • 不要用 resultTyperesultClass 直接映射返回结果类。
  • 即使 POJO 属性名和数据库字段名完全一致,也要定义 <resultMap>
  • 每张表对应至少一个 <resultMap> 映射,保证灵活维护。
  • <resultMap> 使数据库字段与对象字段的映射关系显式配置,实现解耦。

3.2. ❌ 为什么不推荐直接使用 resultType / resultClass

问题点

说明

❌ 隐藏映射规则

直接用类名,MyBatis 默认使用驼峰映射,字段名改变难定位映射问题。

❌ 难以维护

字段变动时,代码容易发生隐性错误,且调试难,错误不易发现。

❌ 无法灵活定制

<resultMap>可以手动调整字段名映射、类型转换、嵌套映射等。

❌ 代码耦合度高

类直接依赖数据库字段结构,耦合紧密,难以改动数据库或类结构。

3.3. ✅ 正确使用 <resultMap> 示例

<resultMap id="UserResultMap" type="com.example.domain.UserDO"><id property="id" column="user_id" /><result property="username" column="user_name" /><result property="email" column="email_address" /><result property="createdAt" column="created_at" />
</resultMap><select id="selectUser" resultMap="UserResultMap">SELECT user_id, user_name, email_address, created_at FROM user WHERE user_id = #{id}
</select>

3.4. ✅ 四、优点

  • 解耦:数据库字段名改变,不影响 Java 类,只改 <resultMap> 映射即可。
  • 灵活:支持字段重命名、复杂类型转换、嵌套关联映射。
  • 可维护:映射关系集中管理,方便排查和修改。
  • 规范化:符合大型项目的代码标准和规范,便于多人协作。

总结:无论字段名是否一一对应,都要定义 <resultMap>,杜绝直接使用 resultType/resultClass,保证映射的灵活性与系统的可维护性。

4. 【强制】sql.xml 配置参数使用:#{},#param# 不要使用 ${} 此种方式容易出现 SQL 注入。

5. 【强制】iBATIS 自带的 queryForList(String statementName,int start,int size) 不推荐使用。

说明:其实现方式是在数据库取到 statementName 对应的 SQL 语句的所有记录,再通过 subList 取 start,size

的子集合,线上因为这个原因曾经出现过 OOM。

正例:

Map<String,Object> map = new HashMap<>(16);
map.put("start", start);
map.put("size", size);

6. 【强制】不允许直接拿 HashMap 与 Hashtable 作为查询结果集的输出。

反例:某同学为避免写一个<resultMap>xxx</resultMap>,直接使用 Hashtable 来接收数据库返回结果,结果出现日常是把 bigint 转成 Long 值,而线上由于数据库版本不一样,解析成 BigInteger,导致线上问题。

6.1. ✅ 规范核心说明

  • 禁止用 HashMapHashtable 直接作为查询结果类型。
  • 即使临时方便,也会导致类型转换不确定,增加线上问题风险。
  • 需通过明确的实体类(DO/DTO)+ <resultMap> 来规范映射。

6.2. 为什么禁止用 HashMap/Hashtable

风险点

说明

类型不确定

数据库数值类型(如 bigint)在不同数据库版本或 JDBC 驱动下映射不同,可能是 LongBigInteger,导致强转异常。

缺乏编译期检查

HashMap<String, Object>无法在编译阶段发现字段名拼写错误或类型错误。

可维护性差

难以追踪和管理字段,后期变更数据库结构或字段时异常隐蔽。

严重线上隐患

不同环境中类型映射不一致,可能导致应用崩溃或数据处理错误。

6.3. ✅ 推荐做法

使用实体类 + <resultMap>

<resultMap id="UserResultMap" type="com.example.domain.UserDO"><id property="id" column="user_id"/><result property="name" column="user_name"/><result property="age" column="user_age"/>
</resultMap>

对应 Java 实体类:

public class UserDO {private Long id;private String name;private Integer age;// getter/setter
}

如果必须使用动态字段,可考虑使用专门的 VO 或 DTO 类,而非原生 Map。

总结:禁止使用 HashMapHashtable 作为查询结果,统一使用实体类映射,避免数据类型不一致和难以维护的问题。

7. 【强制】更新数据表记录时,必须同时更新记录对应的 update_time 字段值为当前时间。

8. 【推荐】不要写一个大而全的数据更新接口。 传入为 POJO 类,不管是不是自己的目标更新字段,都进行update table set c1 = value1 , c2 = value2 , c3 = value3;这是不对的。执行 SQL 时,不要更新无改动的字段,一是易出错;二是效率低;三是增加 binlog 存储。

“更新操作只更新实际变动的字段,避免大而全、无差别地全字段更新。”

8.1. ✅ 规范核心含义

不推荐:

update user set name = #{name}, email = #{email}, age = #{age} where id = #{id};

即使这些字段值根本没有变化,也全部更新。

推荐:

<set><if test="name != null"> name = #{name}, </if><if test="email != null"> email = #{email}, </if><if test="age != null"> age = #{age} </if>
</set>

仅更新真正需要改动的字段。

8.2. 为什么不能“无差别更新”?

问题点

说明

易误更新

POJO 被错误填充默认值(如 0/null/""),可能导致非目标字段被误更新

性能浪费

无差别 UPDATE 会修改整行数据,增加锁竞争,降低写入效率

binlog 膨胀

MySQL 会记录每次更新的全部字段,即使值没变,也会产生完整 binlog

主从同步压力

Binlog 体积大,导致主从同步延迟增加

容易丢数据

特别是在更新对象未做字段控制时,容易清空其他字段原始值(比如某些字段传 null)

8.3. ✅ 正确写法(以 MyBatis 为例)

<update id="updateUser" parameterType="com.example.User">update user<set><if test="name != null"> name = #{name}, </if><if test="email != null"> email = #{email}, </if><if test="age != null"> age = #{age} </if></set>where id = #{id}
</update>

8.4. ✅ 最佳实践建议

场景

建议

DTO 层

拆分多个“更新专用 DTO”,不要直接用全量 POJO

接口层

拆分为:修改基本信息、修改密码、修改头像等小接口

MyBatis 层

<if>标签拼接动态 SQL,仅更新需要的字段

JPA 层

尽量用 @Modifying @Query指定更新字段而不是 save(entity)

9. 【参考】@Transactional 事务不要滥用。 事务会影响数据库的 QPS,另外使用事务的地方需要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。

不要滥用 @Transactional,事务不是免费使用的,代价高、责任重。

9.1. ✅ 为什么要谨慎使用事务?

问题

说明

📉 性能损耗

事务开启后,MySQL/InnoDB 要维护 undo/redo log、加锁、限制并发,导致 QPS 降低。

🔒 资源占用

长事务会持有数据库连接、锁资源,造成阻塞甚至死锁。

不完整的回滚

大多数情况下,@Transactional

只保证数据库回滚,缓存、消息队列、搜索引擎等都不会自动回滚

💥 全局异常吞噬

某些异常(如 catch

住、或非 RuntimeException)不会触发事务回滚,易留坑。

🧩 分布式事务困难

一旦涉及多个系统或数据库表,事务控制难度和成本大增,可能需要引入消息补偿或 TCC 模式。

9.2. ✅ 常见滥用场景举例

场景

说明

在读操作上加事务

没必要浪费事务开销

Controller 层加 @Transactional

会造成事务粒度过大,不清晰

包含远程调用的业务中加事务

事务回滚了但远程服务没法一起回滚,造成数据不一致

操作缓存、ES、MQ 等非数据库资源但未配套补偿机制

回滚不完整,业务数据不一致

9.3. ✅ 最佳实践建议

9.3.1. 控制事务范围最小化(即最小事务单元)

@Transactional
public void updateUserInfo(UserDTO userDTO) {
// 只更新 DB,缓存单独处理
userMapper.update(...);
}

9.3.2. 不跨 RPC、MQ、Redis 等资源使用事务,使用补偿机制替代

  • 分布式事务 → 使用本地消息表 + 消息确认机制
  • Redis 缓存 → 明确使用 try-catch 后补偿、双写延迟队列
  • MQ 消息 → 使用幂等机制 + 状态标记

9.3.3. @Transactional应只出现在Service 层,不出现在Controller和DAO层。

9.3.4. 明确哪些操作需要事务,哪些不需要

  • 批量 insert/update/delete → 可考虑事务。
  • 单条读操作、缓存更新、状态轮询等 → 不需要事务。

9.4. ✅ 额外提醒:Spring 的事务默认规则坑点

事务规则

说明

默认只回滚 RuntimeException

checked exception 不会回滚,除非配置 rollbackFor

事务方法调用自身内部方法无效

事务必须通过 Spring AOP 调用才能生效

多线程场景下事务无效

子线程不会共享事务上下文

超时/嵌套事务不当容易造成锁未释放

配置不当会引发隐藏问题

总结:事务是业务保障的手段,不是默认配置。用事务前,请问自己:是否真的需要?事务能保护的全部资源是否都可回滚?

10. 【参考】<isEqual>中的 compareValue 是与属性值对比的常量,一般是数字,表示相等时带上此条件;<isNotEmpty>表示不为空且不为 null 时执行;<isNotNull>表示不为 null 值时执行。

11. 【推荐】根据业务架构实践,结合业界分层规范与流行技术框架分析,推荐分层结构如图所示,默认上层依赖于下层,箭头关系表示可直接依赖,如:开放 API 层可以依赖于 Web 层(Controller 层),也可以直接依赖于 Service 层,依此类推:

  • 开放 API 层:可直接封装 Service 接口暴露成 RPC 接口;通过 Web 封装成 http 接口;网关控制层等。
  • 终端显示层:各个端的模板渲染并执行显示的层。当前主要是 velocity 渲染,JS 渲染,JSP 渲染,移动端展示等。
  • Web 层:主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简单处理等。
  • Service 层:相对具体的业务逻辑服务层。
  • Manager 层:通用业务处理层,它有如下特征
    1. 对第三方平台封装的层,预处理返回结果及转化异常信息,适配上层接口。
    2. 对 Service 层通用能力的下沉,如缓存方案、中间件通用处理。
    3. 与 DAO 层交互,对多个 DAO 的组合复用。
  • DAO 层:数据访问层,与底层 MySQL、Oracle、Hbase、OceanBase 等进行数据交互。
  • 第三方服务:包括其它部门 RPC 服务接口,基础平台,其它公司的 HTTP 接口,如淘宝开放平台、支付宝付款服务、高德地图服务等。
  • 外部数据接口:外部(应用)数据存储服务提供的接口,多见于数据迁移场景中。

12. 【参考】(分层异常处理规约)在 DAO 层,产生的异常类型有很多,无法用细粒度的异常进行 catch,使用 catch(Exception e) 方式,并 throw new DAOException(e),不需要打印日志,

因为日志在Manager 或 Service 层一定需要捕获并打印到日志文件中去,如果同台服务器再打日志,浪费性能和存储。在 Service 层出现异常时,必须记录出错日志到磁盘,尽可能带上参数和上下文信息,相当于保护案发现场。Manager 层与 Service 同机部署,日志方式与 DAO 层处理一致,如果是单独部署,则采用与Service 一致的处理方式。Web 层绝不应该继续往上抛异常,因为已经处于顶层,如果意识到这个异常将导致页面无法正常渲染,那么就应该直接跳转到友好错误页面,尽量加上友好的错误提示信息。开放接口层要将异常处理成错误码和错误信息方式返回。

这是一个非常重要且成熟的分层异常处理规约,目标是在系统中做到:“异常分层抛出、按职责处理、按场景记录、避免重复打日志”。

12.1. ✅ 各层的职责与处理方式概览

层级

异常处理策略

是否打日志

抛出异常类型

DAO 层

catch(Exception e)

后包装为 DAOException

抛出

不打日志

throw new DAOException

Manager 层

可选择记录日志(若和 Service 层不同机),否则继续抛出

抛出封装后的业务异常

Service 层

捕获并记录完整日志(包含参数、上下文),保证案发现场完整

必须打日志

抛出业务异常或处理异常

Web 层

绝不继续向上抛出,处理为友好页面/响应码/提示消息

打用户友好日志

页面跳转或 JSON 响应

开放接口层

捕获所有异常,转为标准错误码和提示信息响应

日志 + 错误码

统一响应格式

12.2. ✅ DAO 层示例(不打日志,只包装异常)

不使用 try-catch,而是让异常自然抛出(即直接向上抛出),由 Service 层统一 catch 包装为 BizException 并打印日志。为什么 DAO 层通常不写 try-catch?

原因

说明

避免重复封装异常

MyBatis、JPA 已经会抛出具体的数据库异常(如 PersistenceException, SQLException),不需要手动再 try-catch

统一异常处理职责

DAO 只做数据访问,不处理业务,异常的封装和日志由上层(Service)负责

便于排查问题

Service 打日志时可以带上业务上下文(userId, 操作参数),DAO 很难拿到这些信息

保持代码简洁

大量 try-catch 会让 DAO 代码变得冗余

12.3. ✅ 推荐写法(更常见的做法)

// ✅ 不做 try-catch,异常自然抛出
public UserDO getUserById(Long id) {return sqlSession.selectOne("UserMapper.selectById", id);
}

然后在 Service 层封装日志与业务异常

public UserDTO queryUser(Long userId) {try {UserDO user = userDAO.getUserById(userId);return convert(user);} catch (Exception e) {log.error("查询用户失败 userId={}", userId, e);throw new BizException("用户查询失败");}
}

阿里规约 vs 实战最佳实践

规范来源

是否建议 DAO try-catch

是否建议封装为 DAOException

阿里 Java 开发手册(规范型项目)

✅ 建议 catch 后包装 DAOException(但不打日志)

实战项目(Spring Boot + MyBatis)

❌ 通常不 catch,让异常抛给 Service

否(让 Service 层统一包装)

12.4. 什么时候写 try,什么时候不写?

场景

建议

DAO 层复杂通用封装(如 BaseDAO)

可使用 try-catch,统一转为 DAOException

DAO 层用 JDBC 原生写法(抛 SQLException)

应封装为 DAOException

规范驱动团队(如银行/金融)

遵循阿里手册,catch Exception + 包装DAOException

敏捷开发团队或项目较轻量

直接抛出异常,在 Service 层统一处理更简洁

12.5. ✅ Service 层示例(打日志 + 上抛)

public UserDTO getUserById(Long id) {try {return userManager.getUserById(id);} catch (DAOException e) {log.error("查询用户失败,userId={},异常:{}", id, e.getMessage(), e);throw new BizException("用户查询异常", e);}
}

12.6. ✅ Controller 全局异常处理

Controller 层将异常转为响应码或错误页面的最佳实践方式就是使用 全局异常处理(Global Exception Handler),通过 Spring 提供的 @ControllerAdvice@ExceptionHandler 实现。

12.6.1. ✅ 为什么使用全局异常处理?

目的

说明

📦 统一异常格式

避免每个 Controller 手动 try-catch,减少重复代码

🎯 集中管理错误码

可以统一输出错误码、错误信息、traceId 等

😇 提升用户体验

前端展示统一风格的错误提示页或 JSON 格式

📄 方便监控审计

日志记录位置统一,便于日志采集、异常告警

12.6.2. ✅ 示例:全局异常处理类(针对 Web 和 API)

@RestControllerAdvice // 如果是页面应用,可用 @ControllerAdvice
public class GlobalExceptionHandler {// 处理自定义业务异常@ExceptionHandler(BizException.class)public ApiResponse<?> handleBizException(BizException ex) {log.warn("业务异常:{}", ex.getMessage(), ex);return ApiResponse.failure("BIZ_ERROR", ex.getMessage());}// 处理数据库异常@ExceptionHandler(DAOException.class)public ApiResponse<?> handleDaoException(DAOException ex) {log.error("DAO异常:{}", ex.getMessage(), ex);return ApiResponse.failure("DB_ERROR", "数据库操作失败");}// 处理参数校验失败@ExceptionHandler(MethodArgumentNotValidException.class)public ApiResponse<?> handleValidation(MethodArgumentNotValidException ex) {String msg = ex.getBindingResult().getAllErrors().get(0).getDefaultMessage();return ApiResponse.failure("VALIDATION_ERROR", msg);}// 兜底处理未知异常@ExceptionHandler(Exception.class)public ApiResponse<?> handleException(Exception ex) {log.error("系统异常", ex);return ApiResponse.failure("SYSTEM_ERROR", "系统繁忙,请稍后再试");}
}

12.6.3. ✅ 页面异常处理(跳转错误页)

@ControllerAdvice
public class PageExceptionHandler {@ExceptionHandler(Exception.class)public String handlePageError(Exception ex, Model model) {model.addAttribute("errorMsg", "页面加载失败:" + ex.getMessage());return "errorPage";}
}

12.6.4. ✅ 示例响应体类:ApiResponse

public class ApiResponse<T> {private String code;private String message;private T data;public static <T> ApiResponse<T> success(T data) {ApiResponse<T> res = new ApiResponse<>();res.code = "SUCCESS";res.message = "操作成功";res.data = data;return res;}public static <T> ApiResponse<T> failure(String code, String message) {ApiResponse<T> res = new ApiResponse<>();res.code = code;res.message = message;return res;}// getter/setter 略
}

12.6.5. ✅ 总结

  • ✅ Controller 层异常不应使用 try-catch,而应通过 全局异常处理集中解决。
  • @RestControllerAdvice 适用于前后端分离的接口(JSON 返回)。
  • @ControllerAdvice 适用于传统页面应用(返回 ModelAndView 或跳转页)。
  • ✅ 每种异常可细分处理,记录日志 + 统一响应结构 是最佳实践。

12.7. ✅ 重点总结

内容

不重复打日志

异常只在 Service/Web 层 打日志,避免 DAO 打日志浪费资源

上层处理上下文

Service 层必须记录:入参、上下文、堆栈,便于排查

分层包装异常

DAO → DAOException,Manager/Service → BizException,统一异常结构

接口返回友好

页面 → 跳转友好错误页,接口 → 错误码 + 提示

13. DAO层是否需要使用try 捕获异常? 什么需要使用,什么时候不需要?

13.1. DAO是否捕获异常结论总结

是否使用 try-catch

使用场景

原因说明

不使用(推荐方式)

80% 场景都不需要,如使用 MyBatis、JPA、Spring Data

异常由框架抛出(如 PersistenceException, SQLException),由 Service 层统一处理即可

使用

需要封装为自定义异常(如 DAOException),或使用 JDBC 原生操作

原生 JDBC 抛 SQLException,需要封装;某些规范要求分层异常

使用

编写框架/中间件/公共组件中的 BaseDAO

可捕获异常统一转换(如抛 DAOException),提高通用性和一致性

使用

捕获数据库某些特定错误(如主键冲突)并做业务分支处理

需要判断 SQLState 或 errorCode 执行特定逻辑

13.2. ❌ 不使用 try-catch(常见写法)

public UserDO getUserById(Long id) {return sqlSession.selectOne("UserMapper.selectById", id);
}
  • 简洁清晰,异常直接抛出
  • Service 层统一记录日志、抛业务异常
  • 实际项目中更常见 ✅

13.3. ✅ 使用 try-catch 的场景一:需要包装为自定义异常

如果你遵循的是分层异常体系(DAO 层抛 DAOException,Service 层抛 BizException),可以这样写:

public UserDO getUserById(Long id) {
try {return sqlSession.selectOne("UserMapper.selectById", id);
} catch (Exception e) {// 不打日志,向上抛 DAO 异常throw new DAOException("查询用户失败,id=" + id, e);
}
}
  • 使用自定义异常 DAOException,方便统一识别来源
  • 常见于 大型公司、强规范金融类项目
  • 缺点是代码冗余,Service 层还需再包装一次

13.4. ✅ 使用 try-catch 的场景二:原生 JDBC 时必须使用

public UserDO getUserById(Long id) {Connection conn = null;PreparedStatement stmt = null;ResultSet rs = null;try {conn = dataSource.getConnection();stmt = conn.prepareStatement("SELECT * FROM user WHERE id = ?");stmt.setLong(1, id);rs = stmt.executeQuery();if (rs.next()) {// 封装成 UserDOreturn mapToUserDO(rs);}return null;} catch (SQLException e) {throw new DAOException("JDBC 查询失败", e);} finally {closeQuietly(rs, stmt, conn);}
}
  • 原生 JDBC 一定需要 try-catch
  • 注意释放连接资源
  • 推荐封装为工具类

13.5. ✅ 使用 try-catch 的场景三:针对 SQL 错误码做逻辑判断

try {sqlSession.insert("UserMapper.insertUser", user);
} catch (DuplicateKeyException e) {log.warn("插入用户失败,主键冲突 userId={}", user.getId());throw new BizException("用户已存在");
}
  • 某些业务逻辑上需要识别特定异常类型做降级或提示
  • 通常在 Service 层做即可

13.6. 📌 总结建议:你的项目应该怎么做?

项目类型

DAO 是否 try-catch

异常处理建议

中小型项目、Spring Boot+MyBatis

❌ 不用,异常直接抛

Service 统一日志和封装 BizException

传统企业规范项目(如金融、电信)

✅ 使用,包装为 DAOException

不打日志,由 Service 记录

原生 JDBC 使用较多的场景

✅ 必须使用

封装成工具类或 BaseDAO

14. 【参考】分层领域模型规约:

  • DO(Data Object):此对象与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。
  • DTO(Data Transfer Object):数据传输对象,Service 或 Manager 向外传输的对象。
  • BO(Business Object):业务对象,可以由 Service 层输出的封装业务逻辑的对象。
  • Query:数据查询对象,各层接收上层的查询请求。注意超过 2 个参数的查询封装,禁止使用 Map 类来传输。
  • VO(View Object):显示层对象,通常是 Web 向模板渲染引擎层传输的对象。

博文参考

《阿里java规范设计》

相关文章:

  • 无人机桥梁3D建模的拍摄频率
  • Transformer《Attention is all you need》
  • 美化显示GDB调试的数据结构
  • 在ROS2(humble)+Gazebo+rqt下,实时显示仿真无人机的相机图像
  • 无人机报警器探测模块技术解析!
  • Spring框架学习day1--基础概念
  • HackMyVM-Dejavu
  • uniapp 实现腾讯云 IM 消息已读回执
  • 基于大模型的颈椎病全周期预测与治疗方案研究报告
  • 代码随想录算法训练营第60期第五十一天打卡
  • 代理模式核心概念
  • Python模块中__all__变量失效问题深度解析
  • Java Vritual Machine
  • $3 #12阶段三小结Java se
  • SpringCloud基础知识
  • 逻辑回归详解:从原理到实践
  • 从“刚性扩容”到“弹性供给”:移动充电服务重构配电网边际成本
  • Java求职者面试题详解:计算机网络、操作系统、设计模式与数据结构
  • Java 面试实录:从Spring到微服务的技术探讨
  • 【Redis】大key问题详解
  • 锦州网站制作/网站怎么做推广和宣传
  • 陕西省工程建设交易服务中心网站/千锋教育出来好找工作吗
  • 杭州网站建设培训班/怎样在百度上发表文章
  • 幼儿园网站建设介绍/优书网首页
  • 门户类网站的主页设计/外贸网站如何推广优化
  • 网站设计一般包括什么/百度联盟项目看广告挣钱