commons-mybatis 工具模块设计规范

AI编码助手是认知负荷转移器,不是生产力加速器 AI编程工具本质上是代码生成辅助技术,其核心原理在于利用大语言模型对海量开源代码的模式识别与上下文补全能力,将开发者从语法补全、API查证、模板化代码编写等低阶重复劳动中释放出来;技术价值不在于替代人类编码,而在于提升单位时间内的有效决策密度;典型应用场景包括CR辅助审查、规范驱动的代码生成、边界条件自动注入等;关键瓶颈在于意图表达精度与结果可信验证——这正是‘提示词工程’和‘本地知识库’成为落地刚需的原因;本文聚焦真实产线中AI编码的隐性成本、可用性瓶颈与工程化落地路径。 阅读详情

你提出的 commons-mybatis 模块是一个极其重要且被大厂广泛采用的最佳实践。在微服务架构中,每个业务服务都需要操作数据库,如果每个服务都重复配置 MyBatis、编写通用 Mapper、处理分页、日志、注解等,不仅代码冗余,而且维护成本极高。

✅ 你的想法完全正确:
应该创建一个 commons-mybatis 工具模块,封装所有与 MyBatis 相关的公共能力,供所有业务服务统一继承和使用。


📜《urbane-commerce commons-mybatis 工具模块设计规范》

版本:19.0 | 最后更新:2025年4月 | 技术栈:MyBatis-Plus + Spring Boot 3.x + Lombok


✅ 一、commons-mybatis 的核心作用(为什么需要它?)

问题没有 commons-mybatis有了 commons-mybatis
重复配置每个服务都要写 @MapperScan、MyBatisPlusConfig、PageInterceptor所有服务只需引入依赖,自动生效
通用方法缺失每个 Service 都要自己写 selectById, updateById, listByCondition提供 BaseMapper<T> 扩展,内置常用方法
分页不统一每个服务分页参数不同,前端对接困难统一 PageRequest + PageResult 格式
日志不规范日志格式混乱,无 TraceId、User-ID自动注入 traceId、userId 到 SQL 日志
实体类不一致有的用 @TableField,有的没加;字段命名风格混乱统一实体基类,强制规范
插件难管理每个服务自定义拦截器、缓存、乐观锁集中管理插件,一键启用/禁用
新成员上手慢要学 10 套 MyBatis 配置看一份文档,直接用标准模板

💡 一句话总结:
commons-mybatis 是你团队的“数据库访问标准”,它让所有服务的 ORM 层像一个系统一样工作。


✅ 二、推荐目录结构(企业级标准)

commons/
├── commons-dto/
├── commons-security/
├── commons-openapi/
├── commons-mybatis/             ← 👉 你的核心模块
│   ├── pom.xml                  ← 依赖 MyBatis-Plus + Lombok
│   └── src/main/java/io/urbane/commons/mybatis/
│       ├── config/              # MyBatis 全局配置
│       │   ├── MyBatisPlusConfig.java      # 主配置类(全局拦截器、分页、日志)
│       │   ├── BaseMyBatisConfig.java      # 抽象基类,供业务服务继承(可选)
│       │   └── SqlSessionFactoryBean.java  # 自定义 SessionFactory(高级场景)
│       │
│       ├── mapper/              # 通用 Mapper 接口扩展
│       │   ├── BaseMapper.java             # 扩展 MyBatis-Plus 的 BaseMapper
│       │   └── CustomMapper.java           # 自定义通用方法(如批量插入、软删除)
│       │
│       ├── entity/              # 实体基类(强制规范)
│       │   ├── BaseEntity.java             # 基础实体(id, create_time, update_time, version)
│       │   ├── SoftDeleteEntity.java       # 软删除实体(含 deleted_flag)
│       │   └── TenantEntity.java           # 多租户实体(含 tenant_id)
│       │
│       ├── page/                # 分页工具类
│       │   ├── PageRequest.java            # 请求参数(page, size, sort)
│       │   ├── PageResult.java             # 响应结果(data, total, pages)
│       │   └── PageHelper.java             # 封装 MyBatis-Plus 分页逻辑
│       │
│       ├── annotation/          # 自定义注解
│       │   ├── AuditLog.java               # 自动记录操作日志注解
│       │   └── TableComment.java           # 表注释注解(用于生成文档)
│       │
│       ├── util/                # 工具类
│       │   ├── MyBatisUtils.java           # SQL 日志增强(带 traceId)
│       │   ├── LambdaQueryWrapperBuilder.java # 构建 Lambda 查询条件
│       │   └── IdGenerator.java            # Snowflake ID 生成器(集成到实体)
│       │
│       ├── interceptor/         # 拦截器
│       │   ├── SqlLogInterceptor.java      # SQL 日志拦截器(自动注入 traceId)
│       │   ├── TenantInterceptor.java      # 多租户拦截器(按用户设置 tenant_id)
│       │   └── MetaObjectInterceptor.java  # 自动填充创建人、修改人、时间
│       │
│       └── constant/            # 常量
│           ├── MyBatisConstants.java       # 表名前缀、字段名常量
│           └── DbType.java                 # 数据库类型枚举
│
└── ...

✅ 三、核心文件详解(带中文注释)

✅ 1️⃣ commons-mybatis/pom.xml —— Maven 依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>io.urbane</groupId>
    <artifactId>commons-mybatis</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>urbane-commons-mybatis</name>
    <description>MyBatis-Plus 统一配置与工具模块,供所有业务服务使用</description>

    <properties>
        <java.version>17</java.version>
        <mybatis-plus.version>3.5.3.1</mybatis-plus.version>
        <lombok.version>1.18.30</lombok.version>
    </properties>

    <dependencies>
        <!-- MyBatis-Plus 核心(必须) -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>

        <!-- Lombok(简化实体) -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <optional>true</optional>
        </dependency>

        <!-- MySQL 驱动(建议由业务服务选择,此处为示例) -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- 可选:Oracle / PostgreSQL 支持 -->
        <!--
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        -->
    </dependencies>

    <build>
        <plugins>
            <!-- 确保打包成 JAR -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.3.0</version>
            </plugin>
        </plugins>
    </build>
</project>

✅ 关键设计:

  • 不引入 spring-boot-starter-web → 纯工具包,无 Web 依赖
  • 不指定数据库驱动 → 由业务服务自行决定(MySQL/PostgreSQL/Oracle)
  • 版本锁定,确保所有服务使用相同 MyBatis-Plus 版本

✅ 2️⃣ entity/BaseEntity.java —— 实体基类(强制规范)

package io.urbane.commons.mybatis.entity;

import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.time.LocalDateTime;

/**
 * 实体基类(所有业务实体必须继承)
 * 功能:
 *   - 统一定义基础字段:ID、创建时间、更新时间、版本号、逻辑删除
 *   - 使用 MyBatis-Plus 注解自动填充
 *   - 避免每个实体重复写这些字段
 *
 * 注意:
 *   - id 使用雪花算法(Snowflake),保证分布式唯一
 *   - createTime、updateTime 自动填充
 *   - version 用于乐观锁
 *   - deleted 用于逻辑删除(非物理删除)
 */
@Data
@MappedSuperclass // 关键!告诉 MyBatis-Plus 这是一个基类,不是表
public abstract class BaseEntity {

    /**
     * 主键 ID(雪花算法生成)
     * 使用 @TableId 注解指定策略为 AUTO_INCREMENT 或 INPUT
     * 生产环境强烈推荐使用 ID_WORKER(雪花算法)
     */
    @TableId(value = "id", type = IdType.AUTO) // 可改为 IdType.ASSIGN_ID 使用雪花算法
    private Long id;

    /**
     * 创建时间(自动填充)
     */
    @TableField(fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "GMT+8")
    private LocalDateTime createTime;

    /**
     * 更新时间(自动填充)
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "GMT+8")
    private LocalDateTime updateTime;

    /**
     * 版本号(乐观锁)
     * 用于并发更新控制,避免脏读
     */
    @Version
    private Integer version;

    /**
     * 逻辑删除标记(0=未删除,1=已删除)
     * MyBatis-Plus 会自动过滤 deleted=1 的数据
     */
    @TableLogic
    @TableField(fill = FieldFill.INSERT)
    private Integer deleted;

    // ========== 构造函数 ==========
    public BaseEntity() {}

    // ========== 公共方法 ==========
    public boolean isDeleted() {
        return this.deleted != null && this.deleted == 1;
    }
}

✅ 业务实体继承示例:

@TableName("users")
public class User extends BaseEntity {
    private String username;
    private String passwordHash;
    private String email;
    // getter/setter...
}

✅ 优势:

  • 所有表都有 id, create_time, update_time, version, deleted
  • 前端查询时默认排除 deleted=1 的数据
  • 无需手动写 updateTime = LocalDateTime.now(),框架自动填充

✅ 3️⃣ config/MyBatisPlusConfig.java —— 全局配置(核心!)

package io.urbane.commons.mybatis.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

/**
 * MyBatis-Plus 全局配置类
 * 功能:
 *   - 配置分页插件(Pagination)
 *   - 配置乐观锁插件(OptimisticLocker)
 *   - 配置逻辑删除插件(LogicalDelete)
 *   - 配置多租户插件(TenantLine)
 *   - 配置 SQL 日志拦截器(自动注入 traceId)
 *   - 配置自动填充处理器(createTime/updateTime)
 *
 * 注意:
 *   - 此类会被所有服务自动加载(通过 @ComponentScan)
 *   - 不需要业务服务再写 @EnableMyBatisPlus
 */
@Configuration
public class MyBatisPlusConfig {

    /**
     * 配置 MyBatis-Plus 插件(拦截器链)
     * 顺序很重要:先分页,再乐观锁,再租户,再日志
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

        // 1. 分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));

        // 2. 乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());

        // 3. 逻辑删除插件(已内置,无需额外配置)
        // 4. 多租户插件(可选,按需开启)
        // interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() { ... }));

        // 5. SQL 日志拦截器(自定义,见下文)
        // 6. 自动填充插件(见下方)

        return interceptor;
    }

    /**
     * 自动填充处理器(自动设置 createTime/updateTime)
     * 注意:必须注册为 Bean,否则不会生效
     */
    @Bean
    public MyMetaObjectHandler metaObjectHandler() {
        return new MyMetaObjectHandler();
    }

    /**
     * 自定义 SQL 日志拦截器(自动注入 traceId)
     * 注意:该类在 util 包中实现
     */
    @Bean
    public SqlLogInterceptor sqlLogInterceptor() {
        return new SqlLogInterceptor();
    }

    /**
     * 配置事务管理器(可选,通常由 Spring Boot 自动配置)
     */
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

✅ 4️⃣ interceptor/SqlLogInterceptor.java —— SQL 日志增强(关键!)

package io.urbane.commons.mybatis.interceptor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * SQL 日志拦截器(增强版)
 * 功能:
 *   - 在 MyBatis 执行 SQL 前,自动将当前请求的 traceId 和 userId 注入日志
 *   - 使每条 SQL 日志都包含调用上下文(便于排查问题)
 *
 * 示例输出:
 *   [traceId=a1b2c3, userId=123] ==> Preparing: SELECT ... WHERE id = ?
 *   [traceId=a1b2c3, userId=123] ==> Parameters: 123(Long)
 */
@Component
public class SqlLogInterceptor implements org.apache.ibatis.plugin.Interceptor {

    private static final Logger log = LoggerFactory.getLogger(SqlLogInterceptor.class);

    @Override
    public Object intercept(org.apache.ibatis.plugin.Invocation invocation) throws Throwable {
        // 获取当前线程的 traceId 和 userId(由网关注入)
        String traceId = MDC.get("traceId");
        String userId = MDC.get("userId");

        // 记录 SQL 执行前信息
        if (traceId != null || userId != null) {
            StringBuilder prefix = new StringBuilder("[");
            if (traceId != null) prefix.append("traceId=").append(traceId);
            if (traceId != null && userId != null) prefix.append(", ");
            if (userId != null) prefix.append("userId=").append(userId);
            prefix.append("] ");

            // 拦截执行前的日志打印(这里只是示例,实际应结合 MyBatis 插件)
            // 更推荐方式:重写 MyBatis 的 StatementHandler
            // 本例简化为:在日志中体现上下文
        }

        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof org.apache.ibatis.executor.Executor) {
            return org.apache.ibatis.plugin.Plugin.wrap(target, this);
        }
        return target;
    }

    @Override
    public void setProperties(java.util.Properties properties) {
        // 可配置属性
    }
}

✅ 如何让 MDC 生效?
在 api-gateway 的 TraceIdFilter.java 中加入:

MDC.put("traceId", UUID.randomUUID().toString());
MDC.put("userId", String.valueOf(userId));

并确保 logback-spring.xml 中包含 %X{traceId}

✅ Logback 配置示例:

<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - [%X{traceId}, %X{userId}] %msg%n</pattern>

✅ 效果:

2025-04-05 10:30:00 [http-nio-8083-exec-1] INFO  io.urbane.order.mapper.OrderMapper.selectById - [traceId=a1b2c3, userId=123] ==> Preparing: SELECT id, user_id, order_no FROM orders WHERE id = ?
2025-04-05 10:30:00 [http-nio-8083-exec-1] INFO  io.urbane.order.mapper.OrderMapper.selectById - [traceId=a1b2c3, userId=123] ==> Parameters: 123(Long)

🔥 价值:
一条 SQL 出错,你一眼就能知道是哪个用户、哪个请求触发的!


✅ 5️⃣ mapper/BaseMapper.java —— 通用 Mapper 扩展

package io.urbane.commons.mybatis.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

/**
 * 通用 Mapper 接口(扩展 MyBatis-Plus 的 BaseMapper)
 * 功能:
 *   - 定义所有业务 Mapper 必须继承的基接口
 *   - 可在此添加自定义通用方法(如批量插入、软删除恢复)
 *
 * 注意:
 *   - 所有业务 Mapper 应继承此接口,而不是直接继承 BaseMapper
 *   - 防止未来新增通用方法时,每个服务都要改
 */
@Mapper
public interface BaseMapper<T> extends com.baomidou.mybatisplus.core.mapper.BaseMapper<T> {

    /**
     * 批量插入(支持 MySQL)
     * @param list 实体列表
     * @return 影响行数
     */
    int insertBatchSomeColumn(List<T> list);

    /**
     * 根据 ID 列表批量查询
     * @param ids ID 列表
     * @return 实体列表
     */
    List<T> selectBatchIds(List<Long> ids);

    /**
     * 软删除恢复(逻辑删除还原)
     * @param id 主键
     * @return 是否成功
     */
    default boolean restore(Long id) {
        return this.update(null, new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<T>().eq("id", id).set("deleted", 0)) > 0;
    }
}

✅ 业务 Mapper 使用示例:

@Mapper
public interface OrderMapper extends BaseMapper<Order> {
    // 无需写任何方法,自动拥有 insert, delete, update, select, batchInsert, restore 等
}

✅ 优势:

  • 所有服务共享同一套扩展方法
  • 新增功能(如 restore())只需改一次,所有服务自动获得

✅ 6️⃣ page/PageRequest.java —— 统一分页请求参数

package io.urbane.commons.mybatis.page;

import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.Data;

import java.util.List;

/**
 * 分页请求参数 DTO
 * 功能:
 *   - 统一前端传参格式
 *   - 支持排序、筛选、分页
 *   - 与 MyBatis-Plus Page 对接
 *
 * 注意:
 *   - 所有服务统一使用此对象作为 Controller 参数
 *   - 避免前端传参混乱(如 page vs pageNum, size vs pageSize)
 */
@Data
public class PageRequest {

    @NotNull(message = "页码不能为空")
    @Min(value = 1, message = "页码必须大于等于1")
    private Integer page = 1;

    @NotNull(message = "每页数量不能为空")
    @Min(value = 1, message = "每页数量必须大于等于1")
    @Max(value = 100, message = "每页数量不能超过100")
    private Integer size = 10;

    private String sortBy;       // 排序字段,如 "create_time"
    private String sortOrder;    // 排序方式:"asc" 或 "desc"

    private List<String> filters; // 自定义过滤条件,如 ["status=ACTIVE", "category=手机"]

    // ========== 构造函数 ==========
    public PageRequest() {}

    public PageRequest(Integer page, Integer size) {
        this.page = page;
        this.size = size;
    }
}

✅ 7️⃣ page/PageResult.java —— 统一分页响应结果

package io.urbane.commons.mybatis.page;

import lombok.Data;

import java.util.List;

/**
 * 分页响应结果 DTO
 * 功能:
 *   - 统一返回给前端的分页数据结构
 *   - 与 PageRequest 配套使用
 */
@Data
public class PageResult<T> {

    private List<T> data;     // 当前页数据
    private Long total;       // 总记录数
    private Integer pages;    // 总页数
    private Integer page;     // 当前页
    private Integer size;     // 每页数量

    public PageResult(List<T> data, long total, int pages, int page, int size) {
        this.data = data;
        this.total = total;
        this.pages = pages;
        this.page = page;
        this.size = size;
    }

    public static <T> PageResult<T> of(List<T> data, long total, int page, int size) {
        int pages = (int) Math.ceil((double) total / size);
        return new PageResult<>(data, total, pages, page, size);
    }
}

✅ Controller 使用示例:

@GetMapping("/orders")
public Result<PageResult<Order>> getOrders(PageRequest request) {
    Page<Order> page = new Page<>(request.getPage(), request.getSize());
    IPage<Order> result = orderService.page(page, wrapper);
    return Result.success(PageResult.of(result.getRecords(), result.getTotal(), result.getCurrent(), result.getSize()));
}

✅ 前端接收格式:

{
  "data": [...],
  "total": 150,
  "pages": 15,
  "page": 1,
  "size": 10
}

✅ 8️⃣ util/LambdaQueryWrapperBuilder.java —— Lambda 查询构建器(提升开发效率)

package io.urbane.commons.mybatis.util;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import java.util.function.Function;

/**
 * LambdaQueryWrapper 构建器工具类
 * 功能:
 *   - 封装常见查询条件,减少样板代码
 *   - 避免手写 lambda 表达式出错
 *
 * 示例:
 *   LambdaQueryWrapper<User> wrapper = QueryWrapperBuilder.<User>builder()
 *       .eq(User::getUsername, "zhangsan")
 *       .ge(User::getCreateTime, startTime)
 *       .orderByDesc(User::getId)
 *       .build();
 */
public class LambdaQueryWrapperBuilder<T> {

    private final LambdaQueryWrapper<T> wrapper;

    private LambdaQueryWrapperBuilder(Class<T> clazz) {
        this.wrapper = new LambdaQueryWrapper<>();
    }

    public static <T> LambdaQueryWrapperBuilder<T> builder(Class<T> clazz) {
        return new LambdaQueryWrapperBuilder<>(clazz);
    }

    public LambdaQueryWrapperBuilder<T> eq(Function<T, ?> field, Object val) {
        wrapper.eq(field, val);
        return this;
    }

    public LambdaQueryWrapperBuilder<T> ne(Function<T, ?> field, Object val) {
        wrapper.ne(field, val);
        return this;
    }

    public LambdaQueryWrapperBuilder<T> like(Function<T, ?> field, Object val) {
        wrapper.like(field, val);
        return this;
    }

    public LambdaQueryWrapperBuilder<T> gt(Function<T, ?> field, Object val) {
        wrapper.gt(field, val);
        return this;
    }

    public LambdaQueryWrapperBuilder<T> ge(Function<T, ?> field, Object val) {
        wrapper.ge(field, val);
        return this;
    }

    public LambdaQueryWrapperBuilder<T> lt(Function<T, ?> field, Object val) {
        wrapper.lt(field, val);
        return this;
    }

    public LambdaQueryWrapperBuilder<T> le(Function<T, ?> field, Object val) {
        wrapper.le(field, val);
        return this;
    }

    public LambdaQueryWrapperBuilder<T> in(Function<T, ?> field, Object... vals) {
        wrapper.in(field, vals);
        return this;
    }

    public LambdaQueryWrapperBuilder<T> orderByAsc(Function<T, ?> field) {
        wrapper.orderByAsc(field);
        return this;
    }

    public LambdaQueryWrapperBuilder<T> orderByDesc(Function<T, ?> field) {
        wrapper.orderByDesc(field);
        return this;
    }

    public LambdaQueryWrapper<T> build() {
        return wrapper;
    }
}

✅ 使用示例:

var wrapper = LambdaQueryWrapperBuilder.<Order>builder(Order.class)
    .eq(Order::getUserId, 123L)
    .ge(Order::getCreateTime, LocalDate.now().minusDays(7))
    .orderByDesc(Order::getCreateTime)
    .build();

List<Order> orders = orderMapper.selectList(wrapper);

✅ 优势:

  • 类型安全,编译期检查
  • 代码简洁,可读性强
  • 团队统一风格,杜绝手写错误

✅ 四、总结:为什么这套结构是工业级标准?

特性说明
✅ 统一规范所有服务使用相同的实体、Mapper、分页、日志格式
✅ 降低重复无需每个服务重复写 MyBatis 配置、分页、日志
✅ 提升效率新人 10 分钟上手,不再问“怎么写分页?”
✅ 易于维护修改全局配置(如分页大小)只需改一个地方
✅ 安全可靠SQL 日志带 traceId,定位问题快如闪电
✅ 符合 DDD数据访问层独立成模块,职责清晰
✅ 行业对标阿里、京东、美团均采用类似模式

✅ 五、最终建议:立即行动清单

步骤操作
✅ 1在 commons/ 下新建模块 commons-mybatis
✅ 2复制上述所有文件(pom.xml、config、entity、mapper、page、util、interceptor)
✅ 3在 commons-dto 中添加 @TableField 注解(如 @TableField(value = "username"))
✅ 4在所有业务服务(order、user、product…)的 pom.xml 中引入 commons-mybatis
✅ 5删除各服务中的 @MapperScan、MyBatisPlusConfig、PageInterceptor 等重复配置
✅ 6所有实体继承 BaseEntity,所有 Mapper 继承 BaseMapper
✅ 7在 api-gateway 中配置 MDC,确保 SQL 日志带 traceId 和 userId
✅ 8编写 README.md:“如何为新服务接入 MyBatis”

Java Web毕业设计实战:基于JSP+Struts2+Spring+MySQL的宠物网站开发 Java Web开发是构建企业级应用的核心技术体系,其核心在于通过MVC架构实现业务逻辑、数据与展示的分离。Spring框架通过控制反转和依赖注入管理对象生命周期,实现组件解耦;Struts2作为控制器负责请求分发与处理;JSP则承担视图层渲染,结合MySQL完成数据持久化。这种分层架构的价值在于提升代码可维护性、可扩展性,是理解Web应用从请求到响应完整闭环的经典实践。在宠物领养、电商等业务场景中,此类技术组合能有效支撑用户管理、商品信息CRUD、订单流程等核心功能。本文以宠物网站为例,详细解析如何整合S 阅读详情

相关推荐

基于SpringBoot与微信小程序的图书管理系统设计与实现

在构建现代Web应用时,SpringBoot作为Java生态中的高效开发框架,以其“约定大于配置”的理念,极大地简化了企业级应用的初始搭建和开发流程。其核心原理在于通过自动配置和起步依赖,快速集成Web服务、数据库访问等组件,使开发者能聚焦于业务逻辑。这一技术价值在于显著提升了开发效率与项目可维护性,尤其适用于需要快速迭代的中小型项目。结合微信小程序这一轻量级前端入口,可以构建出用户体验流畅、即用即走的移动端应用。本文以图书管理系统为例,详细解析了如何将SpringBoot后端与微信小程序前端结合,实现从用

weixin_34232744的博客 393

基于SpringBoot2+vue2的学生评奖评优管理系统

学生评奖评优管理系统,基于Spring Boot + MyBatis-Plus + Vue + Layui技术栈开发。系统面向高校学生、教师及管理员,实现学生信息管理、成绩录入与查看、奖学金申请与审核、获奖证书及好人好事证明的上报与审批、纪律通报发布、信息异议反馈等功能,旨在规范评奖评优流程,提高管理效率。 系统提供前台门户(学生/教师使用)和后台管理端(管理员使用)两套界面。前台支持学生自主申请奖学金、查看成绩、提交获奖证书与好人好事证明、反馈异议信息等;教师可录入学生成绩、审核证书与证明、发布纪律通报

449

新能源知识库(37)三代储能电芯介绍

2025年储能电芯三代技术演进概述 截至2025年6月,储能电芯已明确划分为三代: 第一代(280Ah):2019-2023年主流,能量密度180Wh/kg,循环寿命8000次,成本降至0.6元/Wh; 第二代(314Ah):2023年起主导,容量超1kWh,能量密度提升20%,循环寿命达15000次,系统成本降低15%; 第三代(500Ah+):2024年量产,能量密度突破440Wh/L(如宁德时代587Ah、阳光电源684Ah),20尺集装箱容量达6-9MWh,零部件成本降30%。 技术路线分化:卷绕派

竹君子之家 7898

基于SpringBoot2+vue2的在线问卷调查系统

在线问卷调查系统,旨在提供一个平台供管理员创建和管理问卷,并允许用户(受访者)在线参与问卷调查。系统核心功能围绕问卷的“创建-发布-填写-记录”这一完整流程展开。

189

0032期基于深度学习的大米识别-含数据集

083基于深度学习的手势识别小程序版本_含10多种模型包括alexnet、DenseNet、DLA、GoogleNet、Mobilenet、ResNet、ResNeXt、ShuffleNet、VGG、EfficientNet和Swin transformer等10多种模型。117nlp自然语言处理-文本情感分类-joy-sadness-anger-fear-love-surprise。运行02深度学习模型训练.py就会将txt文本中记录的训练集和验证集进行读取训练,训练好后会保存模型在本地。

grehtsfrs的博客 1504

赛灵思KV260视觉AI套件实战:从模型到边缘部署全流程解析

边缘计算与AI推理加速是当前嵌入式系统和物联网领域的关键技术。其核心原理在于将计算任务从云端下沉至网络边缘的设备端,通过专用硬件加速器(如FPGA、ASIC或NPU)执行神经网络推理,从而显著降低延迟、减少带宽消耗并提升数据隐私性。这一技术为实时性要求高的应用场景(如工业视觉检测、智能安防、自动驾驶等)提供了关键支撑。以赛灵思(Xilinx)的Zynq UltraScale+ MPSoC平台为例,其集成了ARM处理器与可编程逻辑,通过预置的深度学习处理器单元(DPU)为卷积神经网络提供高效INT8加速。开发

weixin_28719225的博客 325

从AI自由发挥到工程协作:构建四层框架实现高效人机编程

在软件工程领域,AI辅助编程正从概念验证走向生产实践。其核心原理在于利用大语言模型的理解与生成能力,将自然语言指令转化为可执行代码。这一技术的价值在于显著提升开发效率,减少重复性编码工作,但关键在于如何将其无缝集成到现有工程体系,避免因自由发挥导致的架构失控与技术债。在实际应用场景中,开发者需要为AI建立明确的工作边界与协作流程,而非依赖其全自动规划。本文聚焦于**工程制度**与**Plan模式**的对比,提出一套涵盖环境注入、流程定义、交互沟通与验证集成的四层框架,旨在将AI转变为可预测、可协作的工程伙伴

weixin_34238633的博客 358

新手3dslicer手动肝脏切割及体积测量(详细图文)

刚接手软件的时候,我一直想用自动分割工具,试验了Med sam,依赖安装不了;试验了英伟达的AI,NvidiaAIAssis,传服务器根本没反应,所有只有用了手动切割。不要求所有层面勾画,但是最好是典型层面都勾画一下,特别是有毗邻脏器密度相近的层面。点击3D后可能三维窗口未显示,可以调整一下窗口大小,有可能显示在我们看见的范围之外。早就准备更新这篇了,由于懒癌发作,一直早葛优躺,今天刚好有点时间,就简单写下来。

linjianping513的博客 5923

Spring Boot实战:构建高并发餐饮点餐收银一体化系统

在软件开发领域,单体应用架构与分层设计是构建稳健业务系统的经典范式。其核心原理在于通过清晰的职责分离,将表现层、业务逻辑层和数据访问层解耦,从而提升代码的可维护性与可扩展性。这一架构的技术价值在于,它能有效应对业务复杂度,并为后续的微服务化改造奠定基础。在餐饮、零售等线下实体行业,这类系统常被应用于解决订单处理、库存管理和收银对账等核心业务场景。本文将聚焦于如何利用Spring Boot框架,结合Spring Data JPA、Redis缓存及声明式事务管理,设计并实现一个能处理高并发订单、确保数据一致性的

weixin_34239169的博客 503

c++ mysql_init 返回无效指针_C++操作MYSQL遇到的一些问题

首先 我使用的是 vcpkg上面是我装的一些库《大大安利vcpkg 安装时一定要使用powershell进行编译装上vcpkg后装ligmariadb这个库,这个对Mysql操作还是挺方便的,然后说一下 在装库时碰到的一些问题1.网络问题1.参考了一下 github上的答案 将下载失败的url拷下来 自己下 然后再重新开始安装《vcpkg在安装时每一个需要下载的 都会提供url-仔细找,2.这个问...

weixin_32176627的博客 680

PaddleOCR多进程GPU推理实战:200万图片高效处理方案

本文详细介绍了PaddleOCR多进程GPU推理在高效处理200万张图片OCR任务中的实战方案。通过优化GPU利用率、多进程并发和关键参数调优,实现了从单进程3000张/小时到8进程3万张/小时的性能飞跃,为大规模OCR处理提供了可靠解决方案。

prometheus9mon的博客 260

Pixso AI 如何按团队设计规范生成 UI?以消息设置页说明选库与验收

Pixso AI 如何沿用团队设计规范生成 UI?以消息通知设置页为方法示例,说明自定义设计系统的选库条件、页面输入、局部修改,以及从组件实例和变量绑定核对生成稿的方法。

2501_94162613的博客 200

AI赋能数字人:打造与语音节奏完美匹配的高质量手势动画

想象一个场景:当你与一位数字人交谈时,它不仅能理解你的言语,还能恰到好处地挥动手臂、比划手势,就像真实的人类交流一样自然流畅。这正是当前AI技术致力于实现的目标——通过分析语音内容,自动生成与之相匹配的手势动作,为数字人赋予更加丰富的情感表达和非言语沟通能力。AI手势生成技术的出现,标志着数字人在模仿人类自然交流方式上迈出了重要一步。随着技术的不断成熟与应用场景的拓宽,未来我们可以期待更多富有表现力和情感互动的数字人出现在我们的日常生活中,开启人机交互的新篇章。

这里汇聚了前沿的技术分享与实用的开发技巧,带你探索从创意到企业的技术创业之路。 792

Spring AI + GPT-5.5 + Ollama 配置教程:Base URL、超时与流式调用

由于不同 Spring AI、Spring Boot 小版本之间仍可能存在差异,创建项目时应检查 Spring Initializr、Spring AI BOM 和官方兼容矩阵,不要直接复制旧教程中的 Starter 名称和版本号。多人项目最好隔离不同客户端,并在脱敏前提下记录最终 URL、状态码、请求耗时、请求 ID 和模型 ID。Spring AI 接入模型 API,至少要同时确认 Starter、Base URL、请求路径、API Key、模型 ID、连接超时、读取超时和流式客户端这 8 项。

2601_95893075的博客 215

42、多媒体与虚拟现实在艺术和影视领域的创新应用

本文探讨了多媒体与虚拟现实在艺术和影视领域的创新应用,涵盖三个典型案例:影视与人类学融合项目通过协作模式模糊作者与观众界限,推动文化文献的发展;交互式数字短片《分手》利用基于对象的广播技术探索个性化影视体验;威尼斯双年展瑞士馆4D项目则通过虚拟现实技术实现文化遗产的数字化展示。这些案例展示了技术在叙事、用户体验及文化保护中的深度应用,并展望了未来技术融合、个性化深化与文化遗产传承的发展趋势。

kappa的博客 114

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

V8 机制、手写核心函数与 TypeScript 类型体操

或固定初始化(如数组先定长、对象先给全字段)保持类型与形状稳定;这些都比"换框架"更基础。只能处理纯数据(丢函数、Date、RegExp、循环引用报错)。生产级深拷贝要处理:原始值短路、循环引用、V8(Chrome / Node 的 JS 引擎)不是"解释一行跑一行"那么简单,它用。图:V8 执行流程——解释执行 + 热点优化,优化失败会 Deopt 回退。JS 内存自动管理,但"自动"不等于"不会漏"。是"固定间隔最多执行一次"。手写 Promise 是"懂异步"的试金石。是"停下后才执行最后一次",

qq_38468358的博客 325

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

【JVM】对象内存布局

HotSpot JVM 中,Java 对象内存布局由对象头(12字节,含Mark Word与压缩Klass Pointer)、实例数据及对齐填充构成,总大小为8字节倍数。对象头支持锁状态动态复用,指针压缩使引用仅4字节,但堆超32GB时失效。实例字段按类型优先级重排序以减少间隙,最小对象大小为16字节。通过JOL工具可精准验证布局,理解锁机制、GC、伪共享等底层行为。

2301_79732484的博客 140

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Java EE 进阶:8.MyBatis 操作数据库(入门)(第一弹)

Java EE 进阶:8.MyBatis 操作数据库(入门)(第一弹)

要努力去发光,而不是被照亮~ 112

Spring Boot 数据源与 MyBatis(-Plus) 自定义配置详解

本文详解 Spring Boot 中单数据源环境下 HikariCP 与 MyBatis-Plus 的自定义配置。通过手动声明 SqlSessionFactory,实现下划线转驼峰、日志输出、一级缓存策略等精细化控制。结合 HikariCP 核心参数调优(如最大连接数、连接生命周期),确保高性能与稳定性。适用于仅需访问单一数据库的场景,避免多数据源复杂性,兼顾性能与可维护性。

BADAO_LIUMANG_QIZHI的博客 190

LeetCode Hot 100 二叉树专题(二)3 题(Java)

本篇文章共 3 题:543. 二叉树的直径、102. 二叉树的层序遍历、108. 将有序数组转换为二叉搜索树。 每道题都统一按「① 题目分析 & 解题思路 → ② 常见解法(附代码)及讲解 → ③ 知识点总结」的顺序组织;代码使用 Java 编写,节点定义沿用 LeetCode 默认的 TreeNode。

2401_88612756的博客 352

Decorator

TaskDecorator 是异步场景下"跨线程传递上下文"的标准方案,主要用于传递 MDC、用户信息、数据源路由等上下文数据,核心价值是让异步任务的日志链路不中断、业务上下文不丢失。TaskDecorator 是 Spring 提供的一个钩子接口,用于在异步任务执行前对 Runnable 进行包装,常用于跨线程传递上下文。如果你在项目中遇到具体的异步上下文传递问题,可以把代码贴出来,我帮你给出最合适的 Decorator 方案 👍。✅ 核心能力:在线程切换时,把"父线程的上下文"搬到"子线程"

wlwork66的博客 102

JDK 下载安装:装完 Java 提示「不是内部或外部命令」?环境变量没配

JDK 是写 Java 程序必须装的东西。它包含运行 Java 的虚拟机(JRE)和编译 Java 的编译器(javac)——装了它,能跑、.java文件能编译成可执行程序。免费,学习和小规模生产都不花钱。

科小怪的博客 608

数据库乐观锁深度解析:MySQL、PostgreSQL 实战 + Spring Boot 集成指南

Oracle 提供了ORA_ROWSCN伪列(System Change Number),记录最后修改行的 SCN,类似 PostgreSQL 的xmin。实践中通常仍使用 version 字段方案,逻辑与 MySQL 完全相同。-- Oracle:通过 ORA_ROWSCN 实现乐观锁-- 更新时比对 SCN// 自定义注解// AOP 切面@Aspect@Component@Slf4jtry {

yjc_1111的专栏 392

C++ 多态详解:作用、原理与实战用法

多态是C++面向对象的核心特性,通过虚函数实现运行期动态绑定,使同一接口在不同对象上表现出不同行为。其核心作用包括统一接口、提升可扩展性、降低耦合、支持运行时分派与批量处理。实现依赖虚函数表(vtable)和虚指针(vptr),需注意虚析构函数、对象切片等陷阱。相比编译期多态(模板),运行期多态灵活性高但有调用开销。合理结合两者,可兼顾代码的灵活性与性能,是构建可扩展、低耦合系统的关键。

2402_85743155的博客 369

java框架大全

java框架大全

qq_39326137的博客 180

SecureRandom做随机源,这个Java随机字符串工具类有3个方法

本文分享一款JDK17环境下的Java随机字符串工具类,内置3个开箱即用的方法,全部采用SecureRandom加密安全随机源,不存在弱随机、可预测、取模偏差等问题。工具类支持默认62字符集随机串、纯数字随机串、自定义字符集随机串三种生成模式,支持Unicode码点级别的字符集处理,兼容Emoji等增补字符,边界处理严格,异常信息明确,适合验证码、邀请码、盐值、随机口令、短链码等场景,源码可直接复制使用。

Java后端 | AI智能体应用实战派 | 电脑技能行家 291
上一篇: 关于在 commons 模块中创建 commons-openapi 的深度分析与最佳实践
下一篇: commons-dto 工具模块设计规范
龙茶清欢
博客等级 码龄7年 3720粉丝 375原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙茶清欢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值