提交 a58449d0 编写于 作者: templmaster's avatar templmaster

添加 README.md

上级
流水线 #111 已取消 ,包含阶段
# iBiz Mybatis-Plus模板
(技术引用链接)
`Mybatis-Plus官方文档`[:arrow_upper_right:](https://mp.baomidou.com/)
`iBizSys模型api文档`[:arrow_upper_right:](https://modelapi.ibizlab.cn/)
## 介绍说明
`Mybatis-Plus`
MyBatis Plus 是国内人员开发的 MyBatis 增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
`iBizSys`
iBizSys 是以 `Model Driven Development(MDD)`作为核心理论依据的开发框架,iBizSys从`2009年`诞生至今已经经历数个版本,构建出一套完整的系统构建模型,包含: `DevOps模型``文档模型``流程模型``测试模型`等。 iBizSys 提倡以`模型`作为主要内容的高级别抽象的开发方法,并结合市面上较为流行的技术框架形成技术模板,通过`模型`+`模板`的方式快速构建出一套生产级并且具备`复杂性``变更能力`的系统。
## 快速开始
## MyBatis-Plus实现CURD
我们将通过搭建一个简单的Demo来阐述 iBizSys 整合 mybatis-plus 的过程,在此之前,我们假设您已经:
- **熟悉iBizSys实体建立**
- **熟悉MyBatis-Plus**
- **熟悉FreeMaker**
## 建立实体
iBizSys将通过管理数据模型的方式来实现对业务表的管理 [查看更多实体建立小知识](http://bbs.ibizlab.cn/)
![produce](img/quickstart/createEntity.png)
## 建立模板
由于MyBatis-Plus 示例中使用Spring Boot作为工程项目,所以本示例中也将使用Spring Boot作为工程项目。
搭建 SpringBoot 项目环境可通过以下两种方式进行:
>在iBizSys模板仓库中fork SpringBoot模板,基于SpringBoot的基础上增加 Mybatis-Plus 的模板配置 `推荐使用`
>自行搭建 SpringBoot 模板环境,并引入 Mybatis-Plus 相关的模板配置
本示例采用fork的方式获取SpringBoot的模板 [查看更多模板仓库的小知识](http://bbs.ibizlab.cn/)
`编写模板方式`
>在线编辑:`iBizSys` 提供模板在线编辑工具,供大家更高效、便捷的编写模板 [查看iBizSys在线编辑工具](http://bbs.ibizlab.cn/)
>离线编辑:由于`iBizSys`的模板是放在`git`仓库中进行托管,用户可在离线环境下编写模板,编辑完成后再将模板上传至`git`中即可。
### 依赖模板:
`pom.xml.ftl` 中引入 Mybatis-Plus 所需的相关依赖
```java
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.6</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
```
### 配置模板:
`application.yml.ftl`中添加 H2 数据库的相关配置
```java
spring:
datasource:
username: test
password: 'test'
url: jdbc:h2:mem:demo;
driver-class-name: org.h2.Driver
schema: classpath:db/schema-h2.sql
data: classpath:db/data-h2.sql
```
### 启动类模板:
在 启动类` %PUBPRJ%Main.java.ftl `中,添加 `@MapperScan` 注解,扫描 Mapper 文件夹。模板中含 ` % % 、${ } `均为动态参数 [查看模板参数](http://bbs.ibizlab.cn/)
```java
@SpringBootApplication
@MapperScan("${pub.getPKGCodeName()}.*.mapper")
public class ${pub.getCodeName()?lower_case}Main{
public static void main(String[] args) {
SpringApplication.run(${pub.getCodeName()?lower_case}Main.class, args);
}
}
```
### 实体类模板:
实体类模板将循环输出该实体的所有属性,配置 `@TableName` `@TableId` 注解,用于指定该实体所映射的数据库表及主键属性,以下为调整后的实体类相关的模板代码:
```java
@TableName(value = "${item.getTableName()}")
@Data
public class ${item.codeName}{
<#list item.getPSDEFields() as defield>
<#if defield.isKeyDEField()>
@TableId(value= "${defield.getName()?lower_case}",type=IdType.UUID)//指定主键生成策略
</#if>
private ${srfjavatype(defield.stdDataType)} ${defield.codeName?lower_case};
</#list>
}
```
### Mapper类模板:
创建一个Mapper类的模板文件【%DE%Mapper.java.ftl】
```java
public interface ${item.getCodeName()}Mapper extends BaseMapper<${item.getCodeName()}>{
}
```
## 预览成果物
iBizSys提供了即时预览,可以让您在编写模板的同时预览到最终成果物 [查看更多模板编写小技巧](http://bbs.ibizlab.cn/)
>图中左侧为模板的成果物
>图中间为模板代码
>图右侧为动态运行时,可通过动态运行时查看模板所需参数
![produce](img/quickstart/result_preview_de.png)
## 发布模板
您在iBizSys模板工具编写模板后,模板处于暂存状态,若您需要在项目中应用该模板,需要发布该模板。
![produce](img/quickstart/publishFtl.png)
## 发布模型
发布模型的过程即为从模板仓库中获取最新模板合成模型,生产代码的过程。
![produce](img/quickstart/publishSystem.png)
## 查看最终成果物
### 依赖
![produce](img/quickstart/result_pom.png)
### 配置
![produce](img/quickstart/result_yml.png)
### 启动类
![produce](img/quickstart/result_appMain.png)
### 实体类
![produce](img/quickstart/result_de.png)
### Mapper类
![produce](img/quickstart/result_mapper.png)
### 开始使用
添加`DBET`实体建表的schema脚本:
```sql
create table IF NOT EXISTS T_DBET
(
dbetid VARCHAR2(100) not null,
createman VARCHAR2(60),
updateman VARCHAR2(60),
createdate DATE,
dbetname VARCHAR2(200),
updatedate DATE
);
```
添加`DBET`实体测试数据的schema脚本
```sql
INSERT INTO T_DBET (dbetid, dbetname) VALUES
(1, '这是第一条实体数据'),
(2, '这是第二条实体数据'),
(3, '这是第三条实体数据'),
(4, '这是第四条实体数据'),
(5, '这是第五条实体数据');
```
添加测试类,进行功能测试,测试类相关代码如下:
```java
@RunWith(SpringRunner.class)
@SpringBootTest(classes = demoMain.class)
public class QuickStartTest {
@Autowired
private DBETMapper dbetMapper;
@Test
public void testMain() {
System.out.println(("----- selectAll method test ------"));
List<DBET> userList = dbetMapper.selectList(null);
Assert.assertEquals(5, userList.size());
userList.forEach(System.out::println);
}
}
```
控制台输出:
```
----- selectAll method test ------
DBET(dbetname=这是第一条实体数据, createdate=null, createman=null, dbetid=1, updateman=null, updatedate=null)
DBET(dbetname=这是第二条实体数据, createdate=null, createman=null, dbetid=2, updateman=null, updatedate=null)
DBET(dbetname=这是第三条实体数据, createdate=null, createman=null, dbetid=3, updateman=null, updatedate=null)
DBET(dbetname=这是第四条实体数据, createdate=null, createman=null, dbetid=4, updateman=null, updatedate=null)
DBET(dbetname=这是第五条实体数据, createdate=null, createman=null, dbetid=5, updateman=null, updatedate=null)
```
## 小结
通过以上几个简单的步骤,您已经成功搭建一个(SpringBoot+MyBatis-Plus)的项目,实现了对数据表的CURD功能。并且您也成功了搭建了一套属于您个人的技术模板,您可以在以后的任何项目当中,使用您搭建的这套技术模板。
iBizSys可以帮助您快速搭建并使用您所搭建的技术模板来生产您的项目,想要了解更多iBizSys模板生产体系?那就继续往下看吧!
## MyBatis-Plus实现关系映射
通过第一章的学习,您已经成功搭建了一个基于(SpringBoot+MyBatis-Plus)的项目,实现了对数据表的CURD功能。但我们日常项目经常使用到关系型数据库,经常会遇到(1:N、N:N)关系场景,本章就向大家介绍iBizSys实体关系。
### 建立实体1:N关系
建立`ONEET``MANYET`实体,实体关系为: `ONEET 1:N MANYET`
实体关系如下图所示:[了解更多实体关系建立小知识](http://bbs.ibizlab.cn/)
![produce](img/relations/der1-n.png)
### 模板支持1:N关系
#### Mapper文件模板
增加Mapper.xml.ftl,用于映射该实体`主从关系`的查询结果集`resultMap`,相关模板代码如下:
```java
<#ibiztemplate>
TARGET=PSDATAENTITY
</#ibiztemplate>
<?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="${pub.getPKGCodeName()}.${item.getPSSystemModule().codeName?lower_case}.mapper.${item.codeName}Mapper">
<#comment>暴露明细数据查询mapper给主关系调用</#comment>
<#if item.getMinorPSDERs?? && item.getMinorPSDERs()??>
<#list item.getMinorPSDERs() as MinorPSDER>
<#assign MajorEntity = MinorPSDER.getMajorPSDataEntity()>
<#assign MajorkeyField = MajorEntity.getKeyPSDEField()>
<#assign MajorField = MinorPSDER.getPSPickupDEField()>
<#list item.getAllPSDEDataQueries() as singleQuery>
<#list singleQuery.getAllPSDEDataQueryCodes() as dedqcode>
<select id="selectBy${MajorField.getName()?lower_case}" resultMap="${de.codeName}ResultMap">
<![CDATA[ select m1.* from( select t1.* from (
${srfjavasqlcode('${dedqcode.getQueryCode()}')}
)t1 ) m1 where ${MajorField.getName()?lower_case}=<#noparse>#{</#noparse>${MajorkeyField.codeName?lower_case}<#noparse>}</#noparse>
]]>
</select>
</#list>
<#break>
</#list>
</#list>
</#if>
<#comment>mybatis返回结果映射</#comment>
<resultMap id="${de.codeName}ResultMap" type="${pub.getPKGCodeName()}.${item.getPSSystemModule().codeName?lower_case}.domain.${item.codeName}" autoMapping="true">
<id property="${item.getKeyPSDEField().codeName?lower_case}" column="${item.getKeyPSDEField().getName()?lower_case}" /><!--主键字段映射-->
<#if item.getMinorPSDERs?? && item.getMinorPSDERs()??>
<#list item.getMinorPSDERs() as MinorPSDER>
<#assign MajorField = MinorPSDER.getPSPickupDEField()>
<result property="${MajorField.codeName?lower_case}" column="${MajorField.getName()?lower_case}" /><!--关系字段映射-->
</#list>
</#if>
<#comment>1N关系中,在子实体中创建父实体的实例对象</#comment>
<#if item.getMinorPSDERs?? && item.getMinorPSDERs()??>
<#list item.getMinorPSDERs() as MinorPSDER>
<#assign MajorEntity = MinorPSDER.getMajorPSDataEntity()>
<association property="${MinorPSDER.getCodeName()?lower_case}" javaType="${pub.getPKGCodeName()}.${MajorEntity.getPSSystemModule().codeName?lower_case}.domain.${MajorEntity.codeName}" column="${MajorField.getName()?lower_case}" select="${pub.getPKGCodeName()}.${MajorEntity.getPSSystemModule().codeName?lower_case}.mapper.${MajorEntity.codeName}Mapper.selectById" fetchType="lazy"></association>
</#list>
</#if>
<#comment>1N关系中,在父实体中创建子实体的List集合</#comment>
<#if item.getMajorPSDERs?? && item.getMajorPSDERs()??>
<#list item.getMajorPSDERs() as MajorPSDER>
<#assign MinorEntity = MajorPSDER.getMinorPSDataEntity()>
<#assign MajorDerField = MajorPSDER.getPSPickupDEField()>
<collection property="${MajorPSDER.getMinorCodeName()?lower_case}" ofType="${pub.getPKGCodeName()}.${MinorEntity.getPSSystemModule().codeName?lower_case}.domain.${MinorEntity.codeName}" column="${item.getKeyPSDEField().getName()?lower_case}" select="${pub.getPKGCodeName()}.${MinorEntity.getPSSystemModule().codeName?lower_case}.mapper.${MinorEntity.codeName}Mapper.selectBy${MajorDerField.getName()?lower_case}" fetchType="lazy"></collection>
</#list>
</#if>
</resultMap>
</mapper>
```
#### 实体类模板
在实体类中关联 `resultMap`,并发布出该实体主从关系对象,相关模板代码如下:
```java
@TableName(value = "${item.getTableName()}",resultMap = "${item.codeName}ResultMap")
@Data
public class ${item.codeName}{
<#comment>输出当前实体1:N主实体</#comment>
<#if item.getMinorPSDERs?? && item.getMinorPSDERs()??>
<#list item.getMinorPSDERs() as MinorPSDER>
<#assign MajorEntity = MinorPSDER.getMajorPSDataEntity()>
@TableField(exist = false)//关系主表数据
private ${MajorEntity.getCodeName()} ${MinorPSDER.getCodeName()?lower_case};
</#list>
</#if>
<#comment>输出当前实体1:N子实体</#comment>
<#if item.getMajorPSDERs?? && item.getMajorPSDERs()??>
<#list item.getMajorPSDERs() as MajorPSDER>
<#assign MinorEntity = MajorPSDER.getMinorPSDataEntity()>
@TableField(exist = false)//关系子表数据
private List <${MinorEntity.getCodeName()}> ${MajorPSDER.getMinorCodeName()?lower_case};
</#list>
</#if>
}
```
## 开始使用
添加主实体`ONEET`、关系实体`MANYET`实体建表的schema脚本
```sql
create table IF NOT EXISTS T_ONEET
(
oneetid VARCHAR2(100) not null,
oneetname VARCHAR2(200),
createman VARCHAR2(60),
updateman VARCHAR2(60),
createdate DATE,
updatedate DATE
);
create table IF NOT EXISTS T_MANYET
(
manyetid VARCHAR2(100) not null,
manyetname VARCHAR2(200),
oneetid VARCHAR2(200),
createman VARCHAR2(60),
updateman VARCHAR2(60),
createdate DATE,
updatedate DATE
);
```
添加主实体`ONEET`、关系实体`MANYET`实体测试数据的schema脚本
```sql
INSERT INTO T_ONEET (oneetid, oneetname) VALUES
(1, '主实体1'),
(2, '主实体2');
INSERT INTO T_MANYET (manyetid, manyetname,oneetid) VALUES
(1, '关系实体数据1',1),
(2, '关系实体数据2',1),
(3, '关系实体数据3',2);
```
添加测试类进行功能测试,测试类代码如下:
```java
@RunWith(SpringRunner.class)
@SpringBootTest(classes = demoMain.class)
public class RelationsTest {
@Autowired
private ONEETMapper oneetMapper;
@Test
public void testMain() {
System.out.println(("----- selectAll method test ------"));
List<ONEET> oneets = oneetMapper.selectList(null);
for(ONEET oneet: oneets){
System.out.println(String.format("[%s]关联的关系实体数量为[%s]",oneet.getOneetname(),oneet.getManyetobjs().size()));
}
}
}
```
控制台输出:
从测试结果可以看出,在查询主数据时,可以获取到子数据`Manyetobjs`对象,从而实现关联查询。
```
----- selectAll method test ------
[主实体1]关联的关系实体数量为[2]
[主实体2]关联的关系实体数量为[1]
```
## MyBatis-Plus实现动态查询
通过前两章节的学习,相信你已经初步了解iBizSys的模型发布体系,在第三章,将会向大家介绍iBizSys如何通过MyBatis-Plus实现动态查询。
在项目里可能会经常遇到查询同一个数据表,在不同的地方,需要根据不同的条件进行过滤。此时,查询条件是不固定的,需要动态拼接查询条件进行查询。
技术实现:
可以使用iBizSys为每个实体模型建立搜索项
可以使用MyBatis-Plus提供[条件构造器](https://mp.baomidou.com/guide/wrapper.html),根据实体搜索项生成条件构造器,在查询时使用条件构造器,MyBatis-Plus将会解析构造器中的条件来动态拼接where条件,从而实现动态查询。
## 配置实体搜索项
建立实体搜索项,支持通过SEX作为条件进行查询[查看更多实体搜索项配置小技巧](http://bbs.ibizlab.cn/)
![produce](img/dycond/search_cond.png)
## 模板支持实体搜索项
### 实体搜索项模板
模板中为每个实体发布实体搜索项 `searchfilter`,搜索条件包含:`模糊匹配``等于``大于等于``小于等于`···,模板代码如下:
```java
@Data
public class ${item.getCodeName()}SearchFilter{
private QueryWrapper<${item.getCodeName()}> selectCond;
public ${item.getCodeName()}SearchFilter(){
this.selectCond=new QueryWrapper<${item.getCodeName()}>();
}
/**
* 输出实体搜索项
*/
<#list item.getPSDEFields() as defield>
<#list defield.getAllPSDEFSearchModes() as formitem>
private ${srfjavatype(formitem.getPSDEField().stdDataType)} ${formitem.getName()?lower_case};//[${defield.getLogicName()}]
public void set${formitem.getName()?lower_case?cap_first}(${srfjavatype(formitem.getPSDEField().stdDataType)} ${formitem.getName()?lower_case}) {
this.${formitem.getName()?lower_case} = ${formitem.getName()?lower_case};
if(!StringUtils.isEmpty(this.${formitem.getName()?lower_case})){
<#if formitem.getValueOp() == "LIKE">
this.selectCond.like("${formitem.getPSDEField().getName()?lower_case}", ${formitem.getName()?lower_case});
<#elseif formitem.getValueOp() == "LEFTLIKE">
this.selectCond.likeLeft("${formitem.getPSDEField().getName()?lower_case}", ${formitem.getName()?lower_case});
<#elseif formitem.getValueOp() == "RIGHTLIKE">
this.selectCond.likeRight("${formitem.getPSDEField().getName()?lower_case}", ${formitem.getName()?lower_case});
<#elseif formitem.getValueOp() == "EQ">
this.selectCond.eq("${formitem.getPSDEField().getName()?lower_case}", ${formitem.getName()?lower_case});
<#elseif formitem.getValueOp() == "NOTEQ">
this.selectCond.ne("${formitem.getPSDEField().getName()?lower_case}", ${formitem.getName()?lower_case});
<#elseif formitem.getValueOp() == "GT">
this.selectCond.gt("${formitem.getPSDEField().getName()?lower_case}", ${formitem.getName()?lower_case});
<#elseif formitem.getValueOp() == "GTANDEQ">
this.selectCond.ge("${formitem.getPSDEField().getName()?lower_case}", ${formitem.getName()?lower_case});
<#elseif formitem.getValueOp() == "LT">
this.selectCond.lt("${formitem.getPSDEField().getName()?lower_case}", ${formitem.getName()?lower_case});
<#elseif formitem.getValueOp() == "LTANDEQ">
this.selectCond.le("${formitem.getPSDEField().getName()?lower_case}", ${formitem.getName()?lower_case});
</#if>
}
}
</#list>
</#list>
}
```
### 实体服务对象模板
实体服务类对象中,发布`实体数据集`相关模板代码,数据集接收`searchfilter`作为过滤参数,并将`searchfilter`作为参数传递 `mapper` 实现动态查询
```java
@Service
public class ${item.codeName}ServiceImpl extends ServiceImpl<${item.codeName}Mapper, ${item.codeName}> implements ${item.codeName}Service{
@Resource
private ${item.codeName}Mapper ${item.codeName?lower_case}Mapper;
<#if item.getAllPSDEDataSets()??>
<#list item.getAllPSDEDataSets() as dedataset>
public List<${item.codeName}> list${dedataset.getCodeName()}(${item.codeName}SearchFilter filter) {
return ${item.codeName?lower_case}Mapper.search${dedataset.getCodeName()}(filter,filter.getSelectCond());
}
</#list>
</#if>
}
```
### Mapper类模板
Mapper类提供接口供service调用,调用mapper.xml
```java
public interface ${item.getCodeName()}Mapper extends BaseMapper<${item.getCodeName()}>{
<#if item.getAllPSDEDataSets()??>
<#list item.getAllPSDEDataSets() as dedataset>
List<${item.getCodeName()}> search${dedataset.getCodeName()}(@Param("srf") ${item.getCodeName()}SearchFilter searchfilter,@Param("ew") Wrapper<${item.getCodeName()}> wrapper) ;
</#list>
</#if>
}
```
### Mapper文件模板
根据 `Mapper类` 中传递过来的搜索条件,动态拼接查询条件
```java
<#comment>实体数据查询</#comment>
<select id="search${singleQuery.getCodeName()}" resultMap="${de.codeName}ResultMap">
<![CDATA[select t1.* from (
${srfjavasqlcode('${dedqcode.getQueryCode()}')}
)t1]]>
<where><if test="ew!=null and ew.sqlSegment!=null and !ew.emptyOfWhere"><#noparse>${ew.sqlSegment}</#noparse></if></where>
<if test="ew!=null and ew.sqlSegment!=null and ew.emptyOfWhere"><#noparse>${ew.sqlSegment}</#noparse></if>
</select>
```
## 开始使用
添加`DBET`实体建表的schema脚本:
```sql
create table IF NOT EXISTS T_DBET
(
dbetid VARCHAR2(100) not null,
dbetname VARCHAR2(200),
sex VARCHAR2(200),
createman VARCHAR2(60),
updateman VARCHAR2(60),
createdate DATE,
updatedate DATE
);
```
添加`DBET`实体测试数据的schema脚本
```sql
INSERT INTO T_DBET (dbetid, dbetname,sex) VALUES
(1, '这是第一条实体数据','男'),
(2, '这是第二条实体数据','男'),
(3, '这是第三条实体数据','男'),
(4, '这是第四条实体数据','女'),
(5, '这是第五条实体数据','女');
```
添加测试类,测试类相关代码如下:
```java
@RunWith(SpringRunner.class)
@SpringBootTest(classes = demoMain.class)
public class DyCondTest {
@Autowired
private DBETService dbetService;
@Test
public void testMain() {
System.out.println(("----- selectAll method test ------"));
DBETSearchFilter dbetSearchFilter=new DBETSearchFilter();
dbetSearchFilter.setN_sex_eq("男");
List<DBET> dbets = dbetService.listDefault(dbetSearchFilter);
dbets.forEach(System.out::println);
}
}
```
控制台输出:
可以看出已经能够根据实体搜索项【searchfilter】中设定的条件,过滤出 SEX=“男” 的数据
```
----- selectAll method test ------
DBET(dbetname=这是第一条实体数据, createdate=null, createman=null, dbetid=1, updateman=null, updatedate=null, sex=男)
DBET(dbetname=这是第二条实体数据, createdate=null, createman=null, dbetid=2, updateman=null, updatedate=null, sex=男)
DBET(dbetname=这是第三条实体数据, createdate=null, createman=null, dbetid=3, updateman=null, updatedate=null, sex=男)
```
\ No newline at end of file
Markdown 格式
0% or
您添加了 0 到此讨论。请谨慎行事。
先完成此消息的编辑!
想要评论请 注册