关于为什么使用 Mybatis Plus 以及使用 Spring Boot 如何集成 Mybatis Plus 这两个问题。如果不太清楚的小伙伴可以查看之前分享的博客 Spring Boot 集成 Mybatis Plus 简化数据库操作。今天主要和大家分享的是通过 Mybatis Plus generator 生成数据库访问的模板代码。

注意:这里针对的主要是高版本的 Mybatis Plus。也就是 >= 3.5.0

1、数据库表

这里我们简单的创建一个商品订单表,为了方便演示我们就只是列举了几个简单的属性。

商品订单表

CREATE TABLE `tb_order` (
    `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id',
    `order_no` varchar(64) NOT NULL DEFAULT '' COMMENT '商品订单号',
    `order_name` varchar(128) NOT NULL DEFAULT '' COMMENT '商品名称',
    `merchant_no` varchar(36) NOT NULL DEFAULT '' COMMENT '商户号',
    `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '订单状态(0:初始化;1:支付中;2:支付成功;3:支付失败)',
    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品订单表';

2、pom.xml

这里的 jdk 版本使用的是 11,当然可以使用 jdk 8.

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example.mybatisplus</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.3</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

3、生成代码预览

下面红色方框内就是表 tb_order 生成的数据库访问模板代码,这里只需要有一个大概印象,后续会一一讲解。

mybatis sqlite创建数据库_java

4、实体对象

这里生成的实体对象与 tb_order 这个表以及表中的字段一一对应。

@Getter
@Setter
@Accessors(chain = true)
@TableName("tb_order")
public class Order extends Model<Order> {

    private static final long serialVersionUID = 1L;

    /**
     * 主键id
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /**
     * 商品订单号
     */
    @TableField("order_no")
    private String orderNo;

    /**
     * 商品名称
     */
    @TableField("order_name")
    private String orderName;

    /**
     * 商户号
     */
    @TableField("merchant_no")
    private String merchantNo;

    /**
     * 订单状态(0:初始化;1:支付中;2:支付成功;3:支付失败)
     */
    @TableField("status")
    private Integer status;

    /**
     * 创建时间
     */
    @TableField("create_time")
    private LocalDateTime createTime;

    /**
     * 修改时间
     */
    @TableField("update_time")
    private LocalDateTime updateTime;


    @Override
    public Serializable pkVal() {
        return this.id;
    }

}

5、OrderMapper.java

OrderMapper 这个类继承了 Mybatis Plus 中定义的 BaseMapper 这个类。Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能。

@Mapper
public interface OrderMapper extends BaseMapper<Order> {

}

6、OrderDao.java

可以认为是 OrderMapper 的包装类,外部如果直接访问 OrderMapper 就没有地方对通用的逻辑就行抽象。比如:业务方需要一个场景就是根据订单号查询订单。那么就可以把这个逻辑抽象到 OrderDao,具体的查询逻辑就封装到它的实现类 OrderDaoImpl 里面。

public interface OrderDao extends IService<Order> {

}

比如:

public interface OrderDao extends IService<Order> {

    Order getByOrderNo(String orderNo);

}

7、OrderDaoImpl.java

把根据订单号查询逻辑抽取到 OrderDaoImpl.java 这个类当中,业务方在根据订单号查询订单的时候只需要调用 OrderDao#getByOrderNo。而不需要关心内部是如何实现的。

@Service
public class OrderDaoImpl extends ServiceImpl<OrderMapper, Order> implements OrderDao {

    @Override
    public Order getByOrderNo(String orderNo) {
        return getOne(new LambdaQueryWrapper<Order>()
                .eq(Order::getOrderNo, orderNo)
        );
    }

}

8、OrderMapper.xml

如果你觉得你的查询逻辑使用 OrderDao 也不能够表达出来。那么,你就可以直接把 SQL 写到 OrderMapper.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.mybatisplus.demo.mapper.OrderMapper">

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.example.mybatisplus.demo.domain.Order">
        <id column="id" property="id" />
        <result column="order_no" property="orderNo" />
        <result column="order_name" property="orderName" />
        <result column="merchant_no" property="merchantNo" />
        <result column="status" property="status" />
        <result column="create_time" property="createTime" />
        <result column="update_time" property="updateTime" />
    </resultMap>

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, order_no, order_name, merchant_no, status, create_time, update_time
    </sql>

</mapper>

9、代码生成类

最后,最重要的就是下面这个 Mybatis Plus Generator 代码生成类。如果大家还需要定制化自己的生成的模板代码可以查看 代码生成器配置新
,这里有详细的描述。

CodeGenerator.java

public class CodeGenerator {

    public static String URL = "jdbc:mysql://localhost:3306/test";
    public static String USERNAME = "root";
    public static String PASSWORD = "xxxxxxxxxxxx";

    public static String SUPPER_MAPPER_CLASS = "com.baomidou.mybatisplus.core.mapper.BaseMapper";
    public static String BASE_PACKAGE = "com.example.mybatisplus.demo";
    public static String ENTITY_PACKAGE = "domain";
    public static String MAPPER_PACKAGE = "mapper";
    public static String SERVICE_PACKAGE = "repository";
    public static String SERVICE_IMPL_PACKAGE = SERVICE_PACKAGE + ".impl";
    public static String CONTROLLER_PACKAGE = "";
    public static String PROJECT_PATH = System.getProperty("user.dir");
    public static String MAPPER_OUT_PATH = PROJECT_PATH + "/src/main/resources/mapper";
    public static String PROJECT_RESOURCE_PATH = PROJECT_PATH + "/src/main/java";

    public static String AUTHOR = "carl.zhao";

    public static void main(String[] args) {
        generate();
    }

    /**
     * 生成代码
     */
    private static void generate() {
        AutoGenerator autoGenerator = new AutoGenerator(dataSourceConfig())
                .injection(injectionConfig())
                .strategy(strategyConfig())
                .packageInfo(packageInfo())
                .template(templateConfig())
                .global(globalConfig())
                ;
        autoGenerator.execute();
    }

    /**
     * 策略配置(StrategyConfig)
     *
     * @return
     */
    private static StrategyConfig strategyConfig() {
        return new StrategyConfig.Builder()
                .enableCapitalMode()
                .enableSkipView()
                .disableSqlFilter()
                .addInclude(scanner("表名,多个以逗号分开:").split(","))
                .addTablePrefix("tb_", "t_")
//                .addFieldSuffix("_flag")
//                .likeTable(new LikeTable("USER"))
                .build()
                // Mapper 策略配置
                .mapperBuilder()
                .superClass(SUPPER_MAPPER_CLASS)
                .enableMapperAnnotation()
                .enableBaseResultMap()
                .enableBaseColumnList()
                .formatMapperFileName("%sMapper")
                .build()
                // Entity 策略配置
                .entityBuilder()
                .enableChainModel()
                .enableLombok()
//                .enableRemoveIsPrefix()
                .enableTableFieldAnnotation()
                .enableActiveRecord()
                .versionColumnName("version")
                .versionPropertyName("version")
                .logicDeleteColumnName("deleted")
                .logicDeletePropertyName("deleteFlag")
                .naming(NamingStrategy.underline_to_camel)
                .columnNaming(NamingStrategy.underline_to_camel)
//                .addSuperEntityColumns("id", "created_by", "created_time", "updated_by", "updated_time")
//                .addIgnoreColumns("age")
//                .addTableFills(new Column("create_time", FieldFill.INSERT))
//                .addTableFills(new Property("updateTime", FieldFill.INSERT_UPDATE))
                .idType(IdType.AUTO)
//                .formatFileName("%sEntity")
                .build()
                // Service 策略配置
                .serviceBuilder()
                .formatServiceFileName("%sDao")
                .formatServiceImplFileName("%sDaoImpl")
                .build()
                // Controller 策略配置
                .controllerBuilder()
                .build();
    }

    /**
     * 数据库配置(DataSourceConfig)
     *
     * @return
     */
    private static DataSourceConfig dataSourceConfig() {
        return new DataSourceConfig.Builder(URL, USERNAME, PASSWORD)
                .typeConvert(new MySqlTypeConvert())
                .build();
    }

    /**
     * 注入配置(InjectionConfig)
     *
     * @return
     */
    private static InjectionConfig injectionConfig(){
        return new InjectionConfig.Builder()
                .beforeOutputFile((tableInfo, objectMap) -> {
                    System.out.println("tableInfo: " + tableInfo.getEntityName() + " objectMap: " + objectMap.size());
                })
//                .customMap(Collections.singletonMap("test", "baomidou"))
//                .customFile(Collections.singletonMap("test.txt", "/templates/test.vm"))
                .build();
    }

    /**
     * 全局配置(GlobalConfig)
     *
     * @param
     */
    private static GlobalConfig globalConfig() {
        return new GlobalConfig.Builder()
                .fileOverride()
                .outputDir(PROJECT_RESOURCE_PATH)
                .author(AUTHOR)
//                .enableKotlin()
//                .enableSwagger()
                .dateType(DateType.TIME_PACK)
                .commentDate("yyyy-MM-dd HH:ss")
                .build();
    }

    /**
     * 包配置(PackageConfig)
     *
     * @return
     */
    private static PackageConfig packageInfo() {
        return new PackageConfig.Builder()
                .parent(BASE_PACKAGE)
                // BASE_PACKAGE 这个包后面再添加的包名
//                .moduleName("test")
                .entity(ENTITY_PACKAGE)
                .service(SERVICE_PACKAGE)
                .serviceImpl(SERVICE_IMPL_PACKAGE)
                .mapper(MAPPER_PACKAGE)
//                .controller(CONTROLLER_PACKAGE)
                .pathInfo(Collections.singletonMap(OutputFile.mapperXml, MAPPER_OUT_PATH))
                .build();
    }

    /**
     * 模板配置(TemplateConfig)
     *
     * @return
     */
    private static TemplateConfig templateConfig() {
        return new TemplateConfig.Builder()
                .entity("/templates/entity.java")
                .service("/templates/service.java")
                .serviceImpl("/templates/serviceImpl.java")
                .mapper("/templates/mapper.java")
                .mapperXml("/templates/mapper.xml")
                // disable 和 注释掉 controller 就不会生成 Controller类
                .disable(TemplateType.CONTROLLER)
//                .controller("/templates/controller.java")
                .build();
    }



    /**
     * 手动输入需要生成文件的表
     * @param tip
     * @return
     */
    private static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("请输入" + tip + ":");
        System.out.println(help);
        if (scanner.hasNext()) {
            String input = scanner.next();
            if (StringUtils.isNotEmpty(input)) {
                return input;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "!");
    }

}