# LDP Mybatis-Plus 脚手架介绍

本文档介绍基于Mybatis-Plus的LDP脚手架开发流程，主要包含如何进行增删改查操作，配置文件部分配置介绍，以及mybaitis集成包包含内容以及替换方式。



## 一、代码拉取

拉取最新代码:

```shell
git clone http://gitlab.dev.shxrtech.com/ldp/ldp-app-example.git -b dev_mybatis
```

拉取指定版本LDP分支：

```shell
git clone http://gitlab.dev.shxrtech.com/ldp/ldp-app-example.git -b LDP版本号_mybatis
```

**PS：mybatis版本脚手架不支持引用原脚手架base-database包（hibernate、jdbc 接口）。**

## 二、工程结构

工程一共分为4个模块：

- app-common

  应用标准功能模块（主要是一些标准功能，例如附件上传功能等）

- example-api

  样例工程api模块（主要存放entity以及一些公共api）

- example-biz

  样例工程业务模块（主要是业务代码，包含mapper、xml、service、controller）

- example-startup

  样例工程启动模块（启动类、相关配置文件）

此版本脚手架依然使用flyway来做数据库初始化sql存放目录：

- mysql：example-startup/resources/db/mysql
- oracle：example-startup/resources/db/oracle

当在本地开发、测试环境、生产环境时，有许多配置需要修改，服务分组也不一致，在example-startup模块resources目录通过分环境启动文件，在启动时传入参数，使用不同的配置文件

- bootstrap.yml

  默认配置文件，服务端口、服务名等

- bootstrap-dev.yml

  线上开发环境配置， nacos 注册 group为dev_group, weight = 1

- bootstrap-local.yml

  本地本地环境配置， nacos 注册 group为dev_group, weight = 0

- bootstrap-prod.yml

  生产环境配置， nacos 注册 group为prod_group

可以通过修改bootstrap中的spring.profiles.active使用不同配置，也可以在启动命令中传入参数指定配置：

```shell
# 使用dev配置
java –jar example-startup-1.0-SNAPSHOT.jar –-spring.profiles.active=dev
```

## 三、依赖与配置

LDP中封装了mybatis-plus集成依赖包，mybatis版本3.5.6，mybatis-plus版本3.4.1

```xml
       <!--   mybatis-plus集成依赖包     -->
        <dependency>
            <artifactId>common-mybatisplus-integration</artifactId>
            <groupId>com.sinra.ldp</groupId>
            <version>${ldp.version}</version>
        </dependency>
```

集成包中主要包含以下内容，基本上每个组件都可以被自行替换：

| **类名**                             | **说明**                                                     | **替换方式**                                                 |
| ------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
| MybatisPlusCustomIdGenerator         | Entity  Id生成实现                                           | 实现  IdentifierGenerator，并注册bean                        |
| MybatisPlusIPageCovertHandler        | 将返回到前端的数据结构  IPage 转换为 LDP  框架分页数据结构   | 实现  IRestResultCovertHandler，并注册bean                   |
| MybatisMapWrapper  MapWrapperFactory | mybatis-plus 查询 Map  将表字段下划线转换为驼峰命名          | 实现ObjectWrapperFactory，并设置到ConfigurationCustomizer，将ConfigurationCustomizer注册为bean |
| MybatisPlusMetaObjectHandler         | 新增及更新的自动填充                                         | 实现MetaObjectHandler，并注册bean                            |
| mybatis-plus  分页插件               | 在mapper方法添加  IPage 参数，自动实现分页                   |                                                              |
| mybatis-plus  乐观锁插件             | 带有 @Version  注解的字段，更新时自动带上乐观锁实现          |                                                              |
| MybatisPlusCustomAutoConfiguration   | 以上bean的注入，以及注解@MapperScan("com.sinra.ldp.**.mapper*") |                                                              |

新增或更新时填充字段如下：

| **填充字段名** | **填充值**                                   | **填充操作** |
| -------------- | -------------------------------------------- | ------------ |
| createId       | 当前登录用户account，如果没有，则填充develop | 新增         |
| createTime     | 当前时间                                     | 新增         |
| createDate     | 当前时间                                     | 新增         |
| updateId       | 当前登录用户account，如果没有，则填充develop | 新增、更新   |
| updateTime     | 当前时间                                     | 新增、更新   |
| updateDate     | 当前时间                                     | 新增、更新   |
| version        | 0                                            | 新增         |

mybatis-plus部分配置：

```yml
mybatis-plus:
  # 配置xml文件路径
  mapper-locations: classpath*:com/sinra/ldp/**/mapper/xml/*.xml
  configuration:
    # 开启sql打印
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl


# 数据库方言（分页插件用）
ldp:
  query:
    dialect: mysql
```

更多配置请参照：

[https://mybatis.plus/config/](https://mybatis.plus/config/)

## 四、代码生成插件及代码生成内容

LDP IEDA代码生成插件使用文档：[LDP代码生成IDEA插件安装和使用说明](http://gitlab.dev.shxrtech.com/doc/ldp-docs/blob/master/%E5%BC%80%E5%8F%91%E6%96%87%E6%A1%A3/LDP%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90IDEA%E6%8F%92%E4%BB%B6%E5%AE%89%E8%A3%85%E5%92%8C%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E.md)

默认生成代码接口方法：

```java
	/**
     * 新增地区
     *
     * @param area
     */
    public String add(LdpArea area);

    /**
    * 保存地区
    *
    * @param ldpArea
    */
    public boolean saveEntity(LdpArea ldpArea);

    /**
     * 批量新增地区
     *
     * @param list
     */
    public void addList(List<LdpArea> list);

    /**
     * 通过Id查询地区
     *
     * @param id
     * @return
     */
    public LdpArea findById(String id);

    /**
     * 根据条件查询地区
     *
     * @param area
     * @return
     */
    public LdpArea find(LdpArea area);

    /**
     * 根据ID更新地区
     *
     * @param area
     */
    public LdpArea update(LdpArea area);

    /**
     * 批量更新地区
     *
     * @param list
     */
    public void updateList(List<LdpArea> list);

    /**
     * 根据ID删除地区
     *
     * @param id
     */
    public void deleteById(String id);

    /**
     * 根据ID批量删除地区
     *
     * @param list
     */
    public void deleteList(List<LdpArea> list);

    /**
     * 根据条件批量删除地区
     *
     * @param columnMap
     */
    public void deleteByMap(Map<String, Object> columnMap);

    /**
     * 通过map条件查询列表
     *
     * @param paramMap
     * @return
     */
    public List<LdpArea> findListByMap(Map<String, Object> paramMap);

    /**
     * 通过map条件查询分页
     *
     * @param paramPage
     * @param paramMap
     * @return
     */
    public Page<LdpArea> findPageByMap(Page paramPage, Map<String, Object> paramMap);

    /**
     * 通过wrapper查询列表
     *
     * @param area
     * @return
     */
    public List<LdpArea> findList(LdpArea area);

    /**
     * 通过wrapper查询分页
     *
     * @param paramPage
     * @parm area
     * @return
     */
    public Page<LdpArea> findPage(Page paramPage, LdpArea area);

	/**
    * 将新数据集合与通过外键查询出的数据库数据集合进行差异化处理
    *
    * @param fpkFieldName  属性名
    * @param fpkColumnName 表字段名
    * @param fpkValue      外键值
    * @param newList       新数据集合
    */
    void saveOrDeleteByCollection(String fpkFieldName, String fpkColumnName, Object fpkValue, Collection<LdpArea> newList);
```

## 五、增删改查样例

在Service实现类中直接调用以下方式则可以实现简单的增删改查:

```java
//新增
save(area);

//新增或更新
saveOrUpdate(area);

//删除
removeById(area.getId());

//更新
updateById(area);

//查询
getById(id);

//查询列表
list();

//查询分页
page(page);

```

更多CURD接口参照：[https://mybatis.plus/guide/crud-interface.html#service-crud-%E6%8E%A5%E5%8F%A3](https://mybatis.plus/guide/crud-interface.html)

**关联处理**

这里以城市、地区为样例，一个城市存在多个地区。

首先在城市的entity中添加地区列表字段，并用@TableField注解，标识为不存在表中

```java

    @ApiModelProperty(value = "地区列表")
    @TableField(exist = false)
    public List<LdpArea> ldpAreas;
```

关联查询，在mapper.xml中先声明一个resultMap，再写查询语句，返回指定为对应的resultMap，由于这里是一对多，所以使用`collection`作为关联填充，如果是一对一，则使用`association`

```xml
<resultMap id="BaseResultMapToOne" type="com.sinra.ldp.example.entity.LdpCity">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="create_id" property="createId"/>
        <result column="create_time" property="createTime"/>
        <result column="update_id" property="updateId"/>
        <result column="update_time" property="updateTime"/>
        <collection property="ldpAreas" ofType="com.sinra.ldp.example.entity.LdpArea">
            <id column="area_id" property="id"/>
            <result column="area_name" property="name"/>
            <result column="c_id" property="cityId"/>
        </collection>
    </resultMap>
```

查询语句

```xml
    <select id="selectListByParam" resultMap="BaseResultMapToOne">
        SELECT city.*,
               area.id   as area_id,
               area.name as area_name,
               c_id
        FROM ldp_city city
                 LEFT JOIN ldp_area area ON area.c_id = city.id
    </select>

```

如果是级联新增，需要自己去service层写实现

```java
 @Transactional(rollbackFor = Exception.class)
    @Override
    public String add(LdpCity city) {
        save(city);
        // 处理地区列表
        List<LdpArea> ldpAreas = city.getLdpAreas();
        if (!CollectionUtils.isEmpty(ldpAreas)) {
            ldpAreas.forEach(i -> i.setCityId(city.getId()));
            ldpAreaService.saveBatch(city.ldpAreas);
        }
        return city.getId();
    }
```

级联更新同理，只需要在主表更新的时候，对子表也进行更新操作即可。

