當我們使用
MyBatis
的時候,需要在mapper.xml
中書寫大量的SQL
語句。當我們使用MyBatis Generator(MBG)
作為代碼生成器時,也會生成大量的mapper.xml
文件。其實從MBG 1.3.6
版本以后,MyBatis
官方已經推薦使用Dynamic SQL
,使用這一新特性基本就不用寫mapper.xml
文件了,使用起來非常方便,推薦給大家!
Dynamic SQL簡介
在我們使用Spring
的時候,有XML
和Java
兩種配置方式。在使用SpringBoot
時,已經推薦使用Java
配置,基本不用xml
配置了。使用Dynamic SQL
就好比是使用Java
的方式來操作MyBatis
。Dynamic SQL
是用于生成動態SQL
語句的框架,提倡使用Java API
的方式來實現SQL
操作,支持復雜查詢和多表查詢。
Dynamic SQL具有如下特性:
- 類型安全:可以確保參數類型和數據庫字段類型相匹配;
- 富有表現力:語句的構建方式可以清楚地傳達其含義;
- 使用靈活:可以使用
and
,o
r和nested
條件的任意組合來構建where
子句; - 擴展性強:可以同時為
MyBatis3
,Spring JDBC
和純JDBC
框架生成SQL
語句; - 輕量級:只需添加一個小的依賴項,沒有傳遞依賴。
開始使用
首先我們通過一個入門示例將
Dynamic SQL
用起來,該示例會包含基礎的CRUD
操作。
集成Dynamic SQL
- 在
pom.xml
中添加如下依賴,對比之前使用MBG
,僅僅多添加了MyBatis
的動態SQL
依賴;
<dependencies>
<!--SpringBoot整合MyBatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!--MyBatis分頁插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<!--集成druid連接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!-- MyBatis 生成器 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.4.0</version>
</dependency>
<!-- MyBatis 動態SQL支持 -->
<dependency>
<groupId>org.mybatis.dynamic-sql</groupId>
<artifactId>mybatis-dynamic-sql</artifactId>
<version>1.2.1</version>
</dependency>
<!--Mysql數據庫驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
</dependencies>
- 在
application.yml
中對數據源和MyBatis
的mapper.xml
文件路徑進行配置,只需配置自定義mapper.xml
路徑即可;
spring:
datasource:
url: jdbc:mysql://localhost:3306/mall?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: root
mybatis:
mapper-locations:
- classpath:dao/*.xml
- 添加
Java
配置,用于掃描Mapper
接口路徑,MBG
生成的放在mapper
包下,自定義的放在dao
包下。
/**
* MyBatis配置類
* Created by macro on 2019/4/8.
*/
@Configuration
@MapperScan({"com.macro.mall.tiny.mbg.mapper","com.macro.mall.tiny.dao"})
public class MyBatisConfig {
}
使用代碼生成器
- 在使用
MBG
生成代碼前,我們還需要對其進行一些配置,首先在enerator.properties
文件中配置好數據庫連接信息;
jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.connectionURL=jdbc:mysql://localhost:3306/mall?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
jdbc.userId=root
jdbc.password=root
- 然后在
generatorConfig.xml
文件中對MBG
進行配置,配置屬性說明直接參考注釋即可;
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<properties resource="generator.properties"/>
<context id="MySqlContext" targetRuntime="MyBatis3DynamicSQL">
<property name="beginningDelimiter" value="`"/>
<property name="endingDelimiter" value="`"/>
<property name="javaFileEncoding" value="UTF-8"/>
<!-- 為模型生成序列化方法-->
<plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
<!-- 為生成的Java模型創建一個toString方法 -->
<plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
<!--可以自定義生成model的代碼注釋-->
<commentGenerator type="com.macro.mall.tiny.mbg.CommentGenerator">
<!-- 是否去除自動生成的注釋 true:是 :false:否 -->
<property name="suppressAllComments" value="true"/>
<property name="suppressDate" value="true"/>
<property name="addRemarkComments" value="true"/>
</commentGenerator>
<!--配置數據庫連接-->
<jdbcConnection driverClass="${jdbc.driverClass}"
connectionURL="${jdbc.connectionURL}"
userId="${jdbc.userId}"
password="${jdbc.password}">
<!--解決mysql驅動升級到8.0后不生成指定數據庫代碼的問題-->
<property name="nullCatalogMeansCurrent" value="true" />
</jdbcConnection>
<!--指定生成model的路徑-->
<javaModelGenerator targetPackage="com.macro.mall.tiny.mbg.model" targetProject="mall-tiny-dynamic-sql\src\main\java"/>
<!--指定生成mapper接口的的路徑-->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.macro.mall.tiny.mbg.mapper"
targetProject="mall-tiny-dynamic-sql\src\main\java"/>
<!--生成全部表tableName設為%-->
<table tableName="ums_admin">
<generatedKey column="id" sqlStatement="MySql" identity="true"/>
</table>
<table tableName="ums_role">
<generatedKey column="id" sqlStatement="MySql" identity="true"/>
</table>
<table tableName="ums_admin_role_relation">
<generatedKey column="id" sqlStatement="MySql" identity="true"/>
</table>
</context>
</generatorConfiguration>
- 與之前使用MBG有所不同,
targetRuntime
需要改為MyBatis3DynamicSql
,用于配置生成mapper.xml
路徑的sqlMapGenerator
標簽也不需要配置了; - 之前使用
MBG
時自定義了實體類注解的生成,寫了個類CommentGenerator
繼承DefaultCommentGenerator
,在addFieldComment
方法中將Swagger
注解寫入到了實體類的屬性上;
/**
* 自定義注釋生成器
* Created by macro on 2018/4/26.
*/
public class CommentGenerator extends DefaultCommentGenerator {
/**
* 給字段添加注釋
*/
@Override
public void addFieldComment(Field field, IntrospectedTable introspectedTable,
IntrospectedColumn introspectedColumn) {
String remarks = introspectedColumn.getRemarks();
//根據參數和備注信息判斷是否添加備注信息
if(addRemarkComments&&StringUtility.stringHasValue(remarks)){
//數據庫中特殊字符需要轉義
if(remarks.contains("\"")){
remarks = remarks.replace("\"","'");
}
//給model的字段添加swagger注解
field.addJavaDocLine("@ApiModelProperty(value = \""+remarks+"\")");
}
}
}
- 在使用
Dynamic SQL
的時候,這種方法已經無用,需要在addFieldAnnotation
中將Swagger
注解寫入到了實體類的屬性上;
/**
* 自定義注釋生成器
* Created by macro on 2018/4/26.
*/
public class CommentGenerator extends DefaultCommentGenerator {
@Override
public void addFieldAnnotation(Field field, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn, Set<FullyQualifiedJavaType> imports) {
if (!addRemarkComments || CollUtil.isEmpty(imports)) return;
long count = imports.stream()
.filter(item -> API_MODEL_PROPERTY_FULL_CLASS_NAME.equals(item.getFullyQualifiedName()))
.count();
if (count <= 0L) {
return;
}
String remarks = introspectedColumn.getRemarks();
//根據參數和備注信息判斷是否添加備注信息
if (StringUtility.stringHasValue(remarks)) {
//數據庫中特殊字符需要轉義
if (remarks.contains("\"")) {
remarks = remarks.replace("\"", "'");
}
//給model的字段添加swagger注解
field.addJavaDocLine("@ApiModelProperty(value = \"" + remarks + "\")");
}
}
}
- 一切準備就緒,執行
Generator
類的main
方法,生成代碼結構信息如下,可以發現已經不再生成mapper.xml
文件和Example
類,取而代之的是生成了DynamicSqlSupport
類。image.png
實現基本的CRUD操作
這里使用的是mall-tiny項目中權限管理功能相關表,具體可以參考《還在從零開始搭建項目?手擼了款快速開發腳手架》。
- 查看下
MBG
生成的Mapper
接口,比之前使用MBG
時增加了很多方法,并且有了一些默認的方法實現,可見之前在mapper.xml
中的實現都已經轉移到Mapper
接口中去了,單表CRUD
直接調用對應方法即可;
@Mapper
public interface UmsAdminMapper {
@Generated("org.mybatis.generator.api.MyBatisGenerator")
BasicColumn[] selectList = BasicColumn.columnList(id, username, password, icon, email, nickName, note, createTime, loginTime, status);
@Generated("org.mybatis.generator.api.MyBatisGenerator")
@SelectProvider(type=SqlProviderAdapter.class, method="select")
long count(SelectStatementProvider selectStatement);
@Generated("org.mybatis.generator.api.MyBatisGenerator")
@DeleteProvider(type=SqlProviderAdapter.class, method="delete")
int delete(DeleteStatementProvider deleteStatement);
@Generated("org.mybatis.generator.api.MyBatisGenerator")
@InsertProvider(type=SqlProviderAdapter.class, method="insert")
@SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=false, resultType=Long.class)
int insert(InsertStatementProvider<UmsAdmin> insertStatement);
@Generated("org.mybatis.generator.api.MyBatisGenerator")
@SelectProvider(type=SqlProviderAdapter.class, method="select")
@ResultMap("UmsAdminResult")
Optional<UmsAdmin> selectOne(SelectStatementProvider selectStatement);
@Generated("org.mybatis.generator.api.MyBatisGenerator")
@SelectProvider(type=SqlProviderAdapter.class, method="select")
@Results(id="UmsAdminResult", value = {
@Result(column="id", property="id", jdbcType=JdbcType.BIGINT, id=true),
@Result(column="username", property="username", jdbcType=JdbcType.VARCHAR),
@Result(column="password", property="password", jdbcType=JdbcType.VARCHAR),
@Result(column="icon", property="icon", jdbcType=JdbcType.VARCHAR),
@Result(column="email", property="email", jdbcType=JdbcType.VARCHAR),
@Result(column="nick_name", property="nickName", jdbcType=JdbcType.VARCHAR),
@Result(column="note", property="note", jdbcType=JdbcType.VARCHAR),
@Result(column="create_time", property="createTime", jdbcType=JdbcType.TIMESTAMP),
@Result(column="login_time", property="loginTime", jdbcType=JdbcType.TIMESTAMP),
@Result(column="status", property="status", jdbcType=JdbcType.INTEGER)
})
List<UmsAdmin> selectMany(SelectStatementProvider selectStatement);
@Generated("org.mybatis.generator.api.MyBatisGenerator")
@UpdateProvider(type=SqlProviderAdapter.class, method="update")
int update(UpdateStatementProvider updateStatement);
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default long count(CountDSLCompleter completer) {
return MyBatis3Utils.countFrom(this::count, umsAdmin, completer);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default int delete(DeleteDSLCompleter completer) {
return MyBatis3Utils.deleteFrom(this::delete, umsAdmin, completer);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default int deleteByPrimaryKey(Long id_) {
return delete(c ->
c.where(id, isEqualTo(id_))
);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default int insert(UmsAdmin record) {
return MyBatis3Utils.insert(this::insert, record, umsAdmin, c ->
c.map(username).toProperty("username")
.map(password).toProperty("password")
.map(icon).toProperty("icon")
.map(email).toProperty("email")
.map(nickName).toProperty("nickName")
.map(note).toProperty("note")
.map(createTime).toProperty("createTime")
.map(loginTime).toProperty("loginTime")
.map(status).toProperty("status")
);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default int insertSelective(UmsAdmin record) {
return MyBatis3Utils.insert(this::insert, record, umsAdmin, c ->
c.map(username).toPropertyWhenPresent("username", record::getUsername)
.map(password).toPropertyWhenPresent("password", record::getPassword)
.map(icon).toPropertyWhenPresent("icon", record::getIcon)
.map(email).toPropertyWhenPresent("email", record::getEmail)
.map(nickName).toPropertyWhenPresent("nickName", record::getNickName)
.map(note).toPropertyWhenPresent("note", record::getNote)
.map(createTime).toPropertyWhenPresent("createTime", record::getCreateTime)
.map(loginTime).toPropertyWhenPresent("loginTime", record::getLoginTime)
.map(status).toPropertyWhenPresent("status", record::getStatus)
);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default Optional<UmsAdmin> selectOne(SelectDSLCompleter completer) {
return MyBatis3Utils.selectOne(this::selectOne, selectList, umsAdmin, completer);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default List<UmsAdmin> select(SelectDSLCompleter completer) {
return MyBatis3Utils.selectList(this::selectMany, selectList, umsAdmin, completer);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default List<UmsAdmin> selectDistinct(SelectDSLCompleter completer) {
return MyBatis3Utils.selectDistinct(this::selectMany, selectList, umsAdmin, completer);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default Optional<UmsAdmin> selectByPrimaryKey(Long id_) {
return selectOne(c ->
c.where(id, isEqualTo(id_))
);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default int update(UpdateDSLCompleter completer) {
return MyBatis3Utils.update(this::update, umsAdmin, completer);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
static UpdateDSL<UpdateModel> updateAllColumns(UmsAdmin record, UpdateDSL<UpdateModel> dsl) {
return dsl.set(username).equalTo(record::getUsername)
.set(password).equalTo(record::getPassword)
.set(icon).equalTo(record::getIcon)
.set(email).equalTo(record::getEmail)
.set(nickName).equalTo(record::getNickName)
.set(note).equalTo(record::getNote)
.set(createTime).equalTo(record::getCreateTime)
.set(loginTime).equalTo(record::getLoginTime)
.set(status).equalTo(record::getStatus);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
static UpdateDSL<UpdateModel> updateSelectiveColumns(UmsAdmin record, UpdateDSL<UpdateModel> dsl) {
return dsl.set(username).equalToWhenPresent(record::getUsername)
.set(password).equalToWhenPresent(record::getPassword)
.set(icon).equalToWhenPresent(record::getIcon)
.set(email).equalToWhenPresent(record::getEmail)
.set(nickName).equalToWhenPresent(record::getNickName)
.set(note).equalToWhenPresent(record::getNote)
.set(createTime).equalToWhenPresent(record::getCreateTime)
.set(loginTime).equalToWhenPresent(record::getLoginTime)
.set(status).equalToWhenPresent(record::getStatus);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default int updateByPrimaryKey(UmsAdmin record) {
return update(c ->
c.set(username).equalTo(record::getUsername)
.set(password).equalTo(record::getPassword)
.set(icon).equalTo(record::getIcon)
.set(email).equalTo(record::getEmail)
.set(nickName).equalTo(record::getNickName)
.set(note).equalTo(record::getNote)
.set(createTime).equalTo(record::getCreateTime)
.set(loginTime).equalTo(record::getLoginTime)
.set(status).equalTo(record::getStatus)
.where(id, isEqualTo(record::getId))
);
}
@Generated("org.mybatis.generator.api.MyBatisGenerator")
default int updateByPrimaryKeySelective(UmsAdmin record) {
return update(c ->
c.set(username).equalToWhenPresent(record::getUsername)
.set(password).equalToWhenPresent(record::getPassword)
.set(icon).equalToWhenPresent(record::getIcon)
.set(email).equalToWhenPresent(record::getEmail)
.set(nickName).equalToWhenPresent(record::getNickName)
.set(note).equalToWhenPresent(record::getNote)
.set(createTime).equalToWhenPresent(record::getCreateTime)
.set(loginTime).equalToWhenPresent(record::getLoginTime)
.set(status).equalToWhenPresent(record::getStatus)
.where(id, isEqualTo(record::getId))
);
}
}
- 生成代碼中有一些
DynamicSqlSupport
類,比如UmsAdminDynamicSqlSupport
,主要是把數據庫表和字段抽象成了SqlTable和SqlColumn
對象,估計是為了防止我們硬編碼;
public final class UmsAdminDynamicSqlSupport {
@Generated("org.mybatis.generator.api.MyBatisGenerator")
public static final UmsAdmin umsAdmin = new UmsAdmin();
public static final SqlColumn<Long> id = umsAdmin.id;
public static final SqlColumn<String> username = umsAdmin.username;
public static final SqlColumn<String> password = umsAdmin.password;
public static final SqlColumn<String> icon = umsAdmin.icon;
public static final SqlColumn<String> email = umsAdmin.email;
public static final SqlColumn<String> nickName = umsAdmin.nickName;
public static final SqlColumn<String> note = umsAdmin.note;
public static final SqlColumn<Date> createTime = umsAdmin.createTime;
public static final SqlColumn<Date> loginTime = umsAdmin.loginTime;
public static final SqlColumn<Integer> status = umsAdmin.status;
@Generated("org.mybatis.generator.api.MyBatisGenerator")
public static final class UmsAdmin extends SqlTable {
public final SqlColumn<Long> id = column("id", JDBCType.BIGINT);
public final SqlColumn<String> username = column("username", JDBCType.VARCHAR);
public final SqlColumn<String> password = column("password", JDBCType.VARCHAR);
public final SqlColumn<String> icon = column("icon", JDBCType.VARCHAR);
public final SqlColumn<String> email = column("email", JDBCType.VARCHAR);
public final SqlColumn<String> nickName = column("nick_name", JDBCType.VARCHAR);
public final SqlColumn<String> note = column("note", JDBCType.VARCHAR);
public final SqlColumn<Date> createTime = column("create_time", JDBCType.TIMESTAMP);
public final SqlColumn<Date> loginTime = column("login_time", JDBCType.TIMESTAMP);
public final SqlColumn<Integer> status = column("status", JDBCType.INTEGER);
public UmsAdmin() {
super("ums_admin");
}
}
}
- 利用好
MBG
生成的代碼即可完成單表的CRUD
操作了,比如下面最常見的操作。
/**
* 后臺用戶管理Service實現類
* Created by macro on 2020/12/8.
*/
@Service
public class UmsAdminServiceImpl implements UmsAdminService {
@Autowired
private UmsAdminMapper adminMapper;
@Override
public void create(UmsAdmin entity) {
adminMapper.insert(entity);
}
@Override
public void update(UmsAdmin entity) {
adminMapper.updateByPrimaryKeySelective(entity);
}
@Override
public void delete(Long id) {
adminMapper.deleteByPrimaryKey(id);
}
@Override
public UmsAdmin select(Long id) {
Optional<UmsAdmin> optionalEntity = adminMapper.selectByPrimaryKey(id);
return optionalEntity.orElse(null);
}
@Override
public List<UmsAdmin> listAll(Integer pageNum, Integer pageSize) {
PageHelper.startPage(pageNum, pageSize);
return adminMapper.select(SelectDSLCompleter.allRows());
}
}
進階使用
想要用好
Dynamic SQL
,上面的基礎操作是不夠的,還需要一些進階的使用技巧。
SqlBuilder
SqlBuilder
是一個非常有用的類,使用它可以靈活地構建SQL語句的條件,一些常用的條件構建方法如下。
StatementProvider
回想一下之前我們在mapper.xml
中定義select
標簽的方式,各個select
標簽相當于Statement
。而這里的StatementProvider
好比是Statement
中參數和SQL
語句的封裝,方便以Java
的方式創建Statement
。
條件查詢
使用
SqlBuilder
類構建StatementProvider
,然后調用Mapper
接口中的方法即可。
- 這里以按用戶名和狀態查詢后臺用戶并按創建時間降序排列為例,
SQL
實現如下;
SELECT
id,
username,
PASSWORD,
icon,
email,
nick_name,
note,
create_time,
login_time,
STATUS
FROM
ums_admin
WHERE
( username = 'macro' AND STATUS IN ( 0, 1 ) )
ORDER BY
create_time DESC;
- 使用
Dynamic SQL
對應的Java
代碼實現如下,使用SqlBuilder
的select
方法可以指定查詢列,使用from
方法可以指定查詢表,使用where
方法可以構建查詢條件,使用orderBy
方法可以指定排序。
/**
* 后臺用戶管理Service實現類
* Created by macro on 2020/12/8.
*/
@Service
public class UmsAdminServiceImpl implements UmsAdminService {
@Override
public List<UmsAdmin> list(Integer pageNum, Integer pageSize, String username, List<Integer> statusList) {
PageHelper.startPage(pageNum, pageSize);
SelectStatementProvider selectStatement = SqlBuilder.select(UmsAdminMapper.selectList)
.from(UmsAdminDynamicSqlSupport.umsAdmin)
.where(UmsAdminDynamicSqlSupport.username, isEqualToWhenPresent(username))
.and(UmsAdminDynamicSqlSupport.status, isIn(statusList))
.orderBy(UmsAdminDynamicSqlSupport.createTime.descending())
.build()
.render(RenderingStrategies.MYBATIS3);
return adminMapper.selectMany(selectStatement);
}
}
Lambda條件查詢
- 使用
Lambda
表達式實現單表條件查詢更加簡單,實現上面的條件查詢,對應Java
代碼實現如下。
/**
* 后臺用戶管理Service實現類
* Created by macro on 2020/12/8.
*/
@Service
public class UmsAdminServiceImpl implements UmsAdminService {
@Override
public List<UmsAdmin> lambdaList(Integer pageNum, Integer pageSize, String username, List<Integer> statusList) {
PageHelper.startPage(pageNum, pageSize);
List<UmsAdmin> list = adminMapper.select(c -> c.where(UmsAdminDynamicSqlSupport.username, isEqualToWhenPresent(username))
.and(UmsAdminDynamicSqlSupport.status, isIn(statusList))
.orderBy(UmsAdminDynamicSqlSupport.createTime.descending()));
return list;
}
}
子查詢
之前使用
MBG
需要在mapper.xml
中手寫SQL
才能實現子查詢,使用Dynamic SQL
可以直接在Java
代碼中實現。
這里以按角色ID查詢后臺用戶為例,SQL
實現如下;
SELECT
*
FROM
ums_admin
WHERE
id IN ( SELECT admin_id FROM ums_admin_role_relation WHERE role_id = 1 )
- 使用
Dynamic SQL
對應的Java
代碼實現如下,可以發現SqlBuilder
的條件構造方法isIn
中還可以嵌套SqlBuilder
的查詢。
/**
* 后臺用戶管理Service實現類
* Created by macro on 2020/12/8.
*/
@Service
public class UmsAdminServiceImpl implements UmsAdminService {
@Override
public List<UmsAdmin> subList(Long roleId) {
SelectStatementProvider selectStatement = SqlBuilder.select(UmsAdminMapper.selectList)
.from(UmsAdminDynamicSqlSupport.umsAdmin)
.where(UmsAdminDynamicSqlSupport.id, isIn(SqlBuilder.select(UmsAdminRoleRelationDynamicSqlSupport.adminId)
.from(UmsAdminRoleRelationDynamicSqlSupport.umsAdminRoleRelation)
.where(UmsAdminRoleRelationDynamicSqlSupport.roleId, isEqualTo(roleId))))
.build()
.render(RenderingStrategies.MYBATIS3);
return adminMapper.selectMany(selectStatement);
}
}
Group和Join查詢
涉及到多表查詢,之前使用
MBG
的時候基本只能在mapper.xml
中手寫SQL
實現,使用Dynamic SQL
可以支持多表查詢。
- 這里以按角色統計后臺用戶數量為例,
SQL
實現如下;
SELECT
ur.id AS roleId,
ur.NAME AS roleName,
count( ua.id ) AS count
FROM
ums_role ur
LEFT JOIN ums_admin_role_relation uarr ON ur.id = uarr.role_id
LEFT JOIN ums_admin ua ON uarr.admin_id = ua.id
GROUP BY
ur.id;
- 先在
Dao
中添加一個groupList
方法,然后使用@Results
注解定義好resultMap
;
/**
* Created by macro on 2020/12/9.
*/
public interface UmsAdminDao {
@SelectProvider(type = SqlProviderAdapter.class, method = "select")
@Results(id = "RoleStatResult", value = {
@Result(column = "roleId", property = "roleId", jdbcType = JdbcType.BIGINT, id = true),
@Result(column = "roleName", property = "roleName", jdbcType = JdbcType.VARCHAR),
@Result(column = "count", property = "count", jdbcType = JdbcType.INTEGER)
})
List<RoleStatDto> groupList(SelectStatementProvider selectStatement);
}
- 然后在
Service
中調用groupList
方法傳入StatementProvider
即可,對應的Java
代碼實現如下。
/**
* 后臺用戶管理Service實現類
* Created by macro on 2020/12/8.
*/
@Service
public class UmsAdminServiceImpl implements UmsAdminService {
@Override
public List<RoleStatDto> groupList() {
SelectStatementProvider selectStatement = SqlBuilder.select(UmsRoleDynamicSqlSupport.id.as("roleId"), UmsRoleDynamicSqlSupport.name.as("roleName"), count(UmsAdminDynamicSqlSupport.id).as("count"))
.from(UmsRoleDynamicSqlSupport.umsRole)
.leftJoin(UmsAdminRoleRelationDynamicSqlSupport.umsAdminRoleRelation)
.on(UmsRoleDynamicSqlSupport.id, equalTo(UmsAdminRoleRelationDynamicSqlSupport.roleId))
.leftJoin(UmsAdminDynamicSqlSupport.umsAdmin)
.on(UmsAdminRoleRelationDynamicSqlSupport.adminId, equalTo(UmsAdminDynamicSqlSupport.id))
.groupBy(UmsRoleDynamicSqlSupport.id)
.build()
.render(RenderingStrategies.MYBATIS3);
return adminDao.groupList(selectStatement);
}
}
條件刪除
使用
Dynamic SQL
實現條件刪除,直接調用Mapper
接口中生成好的delete
方法即可。
- 這里以按用戶名刪除后臺用戶為例,
SQL
實現如下;
DELETE
FROM
ums_admin
WHERE
username = 'andy';
- 使用
Dynamic SQL
對應Java
中的實現如下。
/**
* 后臺用戶管理Service實現類
* Created by macro on 2020/12/8.
*/
@Service
public class UmsAdminServiceImpl implements UmsAdminService {
@Override
public void deleteByUsername(String username) {
DeleteStatementProvider deleteStatement = SqlBuilder.deleteFrom(UmsAdminDynamicSqlSupport.umsAdmin)
.where(UmsAdminDynamicSqlSupport.username, isEqualTo(username))
.build()
.render(RenderingStrategies.MYBATIS3);
adminMapper.delete(deleteStatement);
}
}
條件修改
使用
Dynamic SQL
實現條件修改,直接調用Mapper
接口中生成好的update
方法即可。
- 這里以按指定ID修改后臺用戶的狀態為例,
SQL
實現如下;
UPDATE ums_admin
SET STATUS = 1
WHERE
id IN ( 1, 2 );
- 使用
Dynamic SQL
對應Java
中的實現如下。
/**
* 后臺用戶管理Service實現類
* Created by macro on 2020/12/8.
*/
@Service
public class UmsAdminServiceImpl implements UmsAdminService {
@Override
public void updateByIds(List<Long> ids, Integer status) {
UpdateStatementProvider updateStatement = SqlBuilder.update(UmsAdminDynamicSqlSupport.umsAdmin)
.set(UmsAdminDynamicSqlSupport.status).equalTo(status)
.where(UmsAdminDynamicSqlSupport.id, isIn(ids))
.build()
.render(RenderingStrategies.MYBATIS3);
adminMapper.update(updateStatement);
}
}
一對多查詢
使用
Dynamic SQL
也可以實現一對多查詢,只是由于Java
注解無法實現循環引用,所以一對多的resultMap
只能在mapper.xml
來配置,這可能是唯一需要使用mapper.xml
的地方。
- 這里以按ID查詢后臺用戶信息(包含對應角色列表)為例,
SQL
實現如下;
SELECT
ua.*,
ur.id AS role_id,
ur.NAME AS role_name,
ur.description AS role_description,
ur.create_time AS role_create_time,
ur.STATUS AS role_status,
ur.sort AS role_sort
FROM
ums_admin ua
LEFT JOIN ums_admin_role_relation uarr ON ua.id = uarr.admin_id
LEFT JOIN ums_role ur ON uarr.role_id = ur.id
WHERE
ua.id = 1
- 然后在
Dao
接口中添加selectWithRoleList
方法,這里使用@ResultMap
注解引用mapper.xml
中定義的resultMap
;
/**
* Created by macro on 2020/12/9.
*/
public interface UmsAdminDao {
@SelectProvider(type = SqlProviderAdapter.class, method = "select")
@ResultMap("AdminRoleResult")
AdminRoleDto selectWithRoleList(SelectStatementProvider selectStatement);
}
- 在
mapper.xml
中添加名稱為AdminRoleResult
的resultMap
,這里有個小技巧,可以直接引用在Mapper
接口中定義好的resultMap
;
<resultMap id="AdminRoleResult" type="com.macro.mall.tiny.domain.AdminRoleDto"
extends="com.macro.mall.tiny.mbg.mapper.UmsAdminMapper.UmsAdminResult">
<collection property="roleList" resultMap="com.macro.mall.tiny.mbg.mapper.UmsRoleMapper.UmsRoleResult" columnPrefix="role_">
</collection>
</resultMap>
- 然后在
Service
實現類中調用即可,為了方便結果集映射給查詢列取了別名。
/**
* 后臺用戶管理Service實現類
* Created by macro on 2020/12/8.
*/
@Service
public class UmsAdminServiceImpl implements UmsAdminService {
@Override
public AdminRoleDto selectWithRoleList(Long id) {
List<BasicColumn> columnList = new ArrayList<>(CollUtil.toList(UmsAdminMapper.selectList));
columnList.add(UmsRoleDynamicSqlSupport.id.as("role_id"));
columnList.add(UmsRoleDynamicSqlSupport.name.as("role_name"));
columnList.add(UmsRoleDynamicSqlSupport.description.as("role_description"));
columnList.add(UmsRoleDynamicSqlSupport.createTime.as("role_create_time"));
columnList.add(UmsRoleDynamicSqlSupport.status.as("role_status"));
columnList.add(UmsRoleDynamicSqlSupport.sort.as("role_sort"));
SelectStatementProvider selectStatement = SqlBuilder.select(columnList)
.from(UmsAdminDynamicSqlSupport.umsAdmin)
.leftJoin(UmsAdminRoleRelationDynamicSqlSupport.umsAdminRoleRelation)
.on(UmsAdminDynamicSqlSupport.id, equalTo(UmsAdminRoleRelationDynamicSqlSupport.adminId))
.leftJoin(UmsRoleDynamicSqlSupport.umsRole)
.on(UmsAdminRoleRelationDynamicSqlSupport.roleId, equalTo(UmsRoleDynamicSqlSupport.id))
.where(UmsAdminDynamicSqlSupport.id, isEqualTo(id))
.build()
.render(RenderingStrategies.MYBATIS3);
return adminDao.selectWithRoleList(selectStatement);
}
}
總結
當我們使用MyBatis
官方代碼生成器MBG
時,配置的targetRuntime
決定了使用它的使用方式。Dynamic SQL
更傾向于使用Java API
來實現SQL
操作,傳統的方式更傾向于在mapper.xml
中手寫SQL
來實現SQL
操作。雖然MyBatis
官方推薦使用Dynamic SQL
,但選擇那種方式全看個人習慣了!
參考資料
官方文檔:https://mybatis.org/mybatis-dynamic-sql/docs/introduction.html