本文目录导读:

在Java开发中,规范的数据查询流程是保证代码可维护性、性能和安全性的关键,以下是一个从架构到编码的标准化流程规范:
核心原则
- 分层隔离:严格遵循 Controller -> Service -> DAO(Repository) -> DataSource 的分层,禁止跨层调用。
- 最小化查询:只查询需要的字段,避免
SELECT *。 - 安全第一:防止 SQL 注入,敏感数据脱敏。
- 资源释放:确保连接、流、Session 等资源的正确释放。
标准查询流程清单
graph TD
A[客户端请求] --> B{Controller层}
B --> C[参数校验 & 封装]
C --> D[调用 Service 层]
D --> E{Service 层}
E -- 业务逻辑 & 缓存判断 --> F[调用 DAO 层]
F --> G{DAO/Repository 层}
G --> H[ORM 映射 / SQL 执行]
H --> I[(数据库)]
I --> H
H --> J[结果集映射]
J --> K[返回至 Service]
K --> L[数据装配 / 缓存回写]
L --> M[返回至 Controller]
M --> N[视图/JSON序列化输出]
各层详细规范
Controller 层(唯一使命:接收与响应)
- 职责:参数解析、格式验证、调用 Service、返回结果。
- 禁止:包含任何业务逻辑、SQL 拼接、直接调用 DAO。
- 规范代码示例:
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public Result<UserVO> getUser(@PathVariable Long id) {
// 1. 基础参数校验
if (id == null || id <= 0) {
return Result.fail("Invalid user ID");
}
// 2. 调用 Service
UserVO userVO = userService.getUserById(id);
// 3. 响应处理
return Result.success(userVO);
}
}
Service 层(业务核心,负责编排)
- 职责:业务规则、事务控制、缓存策略、数据装配。
- 规范:
- 使用
@Transactional管理事务(读写分离时注意只读事务readOnly=true)。 - 大量数据查询时考虑分库分表或缓存(Redis)。
- 组装多个 DAO 查询结果(数据聚合)。
- 使用
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Autowired
private OrderDao orderDao;
@Autowired
private RedisTemplate redisTemplate;
@Override
@Transactional(readOnly = true)
public UserVO getUserById(Long id) {
// 1. 缓存穿透保护:先查缓存
String cacheKey = "user:" + id;
UserVO cacheUser = (UserVO) redisTemplate.opsForValue().get(cacheKey);
if (cacheUser != null) {
return cacheUser;
}
// 2. 数据库查询
UserDO userDO = userDao.selectById(id);
if (userDO == null) {
throw new BusinessException("User not found");
}
// 3. 业务规则校验
if (userDO.getStatus() == 0) {
throw new BusinessException("User has been disabled");
}
// 4. 数据装配(映射为 VO)
UserVO userVO = UserConvert.INSTANCE.toVO(userDO);
// 5. 回写缓存
redisTemplate.opsForValue().set(cacheKey, userVO, 30, TimeUnit.MINUTES);
return userVO;
}
}
DAO/Repository 层(数据库读写)
- 职责:执行 SQL/ORM 查询,映射结果。
- 规范:
- 返回类型:原则只有
DO(数据库对象)或List<DO>。 - 命名规则:
insert,insertBatch,selectById,selectListByXXX,updateById,deleteById。 - 分页:统一使用分页对象(如 Mybatis-Plus 的
Page),避免手动LIMIT。
- 返回类型:原则只有
// 使用 MyBatis-Plus 规范
@Repository
public interface UserDao extends BaseMapper<UserDO> {
// 自定义查询
@Select("SELECT id, name, email FROM user WHERE status = #{status} AND create_time > #{startTime}")
List<UserDO> selectActiveUsers(@Param("status") Integer status,
@Param("startTime") LocalDateTime startTime);
// 分页查询
IPage<UserDO> selectPageByCondition(Page<UserDO> page, @Param("condition") UserQueryDTO condition);
}
关键规范节点
参数传递规范
-
原则:禁止使用
Map作为 DAO 或 Service 入参,必须封装为具体 DTO 或 Query 对象。 -
优点:
- 编译期类型检查,避免运行时
ClassCastException。 - 方便参数校验注解(如
@NotNull,@Size)。 - 代码可读性高。
- 编译期类型检查,避免运行时
-
反例:
// 避免 public List<UserVO> queryUsers(Map<String, Object> params); // 推荐 public List<UserVO> queryUsers(UserQueryDTO query);
结果映射与脱敏
- DO:数据库表直接映射对象(
POJO+@TableName,@TableField)。 - VO:前端视图对象,只包含需要展示的字段,且必须脱敏(如手机号
138****1234)。 - DTO:不同服务间数据传输。
- 转换工具:统一使用
MapStruct、BeanCopier(如Spring BeanUtils),禁止手动 get/set。
// 脱敏枚举
public enum SensitiveType {
PHONE,
EMAIL,
ID_CARD
}
// 工具类
public String desensitize(String value, SensitiveType type) {
// 根据类型脱敏...
}
SQL 规范
- *禁止 `SELECT
**:明确指定所需字段,列变更时SELECT *` 会返回脏数据或导致内存浪费。 - 使用
LIMIT限制结果集:防止大数据量 OOM(OutOfMemoryError)。 - JOIN 数量控制:单表查询优先,超过 3 张表 JOIN 考虑冗余字段或 ES 搜索引擎。
- 参数化查询:永远使用
#{value}(MyBatis)或 (JDBC),禁止 拼接。
// 正确
@Select("SELECT id, name FROM user WHERE name = #{name}")
List<UserDO> findByName(@Param("name") String name);
// 错误(SQL注入风险)
@Select("SELECT id, name FROM user WHERE name = '${name}'")
List<UserDO> findByName(@Param("name") String name);
异常处理
- DAO 层:不捕获异常,向上抛出运行时异常(如
DataAccessException)。 - Service 层:捕获 DAO 异常,转换为业务异常(
BusinessException)。 - 全局拦截:
@RestControllerAdvice统一处理,返回友好错误码。
@ExceptionHandler(BusinessException.class)
public Result<?> handleBusinessException(BusinessException e) {
log.warn("Business error: {}", e.getMessage());
return Result.fail(e.getCode(), e.getMessage());
}
性能规范
- N+1 查询问题:使用
IN查询 或 嵌套循环优化 解决,查询订单列表时,不要在循环里逐条查用户信息,而是先用IN查出所有用户再组装。 - 分压与限流:非核心查询使用
@Async异步?错!查询不要轻易异步,不仅增加复杂度,还可能造成线程池膨胀,应做限流保护(如 Sentinel)。 - 超时设置:重要查询设置
StatementTimeout,防止慢 SQL 拖垮数据库。// MyBatis 全局 spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.validation-timeout=5000
代码示例(完整流程)
Controller -> Service -> DAO -> DB 并返回脱敏结果
// 1. Controller
@GetMapping("/list")
public Result<PageResult<UserVO>> listUsers(@Valid UserQueryDTO query) {
PageResult<UserVO> result = userService.pageQueryUsers(query);
return Result.success(result);
}
// 2. Service
@Service
public class UserServiceImpl implements UserService {
@Override
public PageResult<UserVO> pageQueryUsers(UserQueryDTO query) {
// 构建分页参数
Page<UserDO> page = new Page<>(query.getPageNum(), query.getPageSize());
// 执行查询(条件由 Mybatis-Plus 的 QueryWrapper 或 XML 处理)
IPage<UserDO> userPage = userDao.selectPage(page, query);
// 转换并脱敏
List<UserVO> voList = userPage.getRecords().stream()
.map(user -> {
UserVO vo = UserConvert.INSTANCE.toVO(user);
if (StringUtils.isNotBlank(vo.getPhone())) {
vo.setPhone(SensitiveUtil.maskPhone(vo.getPhone()));
}
return vo;
})
.collect(Collectors.toList());
// 封装为统一分页返回对象
return new PageResult<>(voList, userPage.getTotal(), userPage.getCurrent(), userPage.getSize());
}
}
// 3. DAO
@Repository
public interface UserDao extends BaseMapper<UserDO> {
// 继承 BaseMapper 自动拥有 CRUD
IPage<UserDO> selectPage(Page<UserDO> page, @Param("query") UserQueryDTO query);
}
一个规范的查询流程必须做到:
- 分层清晰(Controller-Service-DAO)
- 参数明确(DTO/Query,不使用 Map)
- 安全(参数化查询,SQL注入防御)
- 结果精确(返回 VO 且脱敏)
- 性能可控(分页,避免 N+1,设置超时)
- 异常友好(统一业务异常处理)
坚持这套流程,可以有效减少线上故障并提高团队协作效率。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播