在实际的项目开发中,数据层会使用到 MyBatis的插件来提高开发效率。现在很多人都喜欢使用 MyBatis-Plus插件来做开发。但是我不推荐这么用,因为它和hibernate太像了整个的开发过程和设计理念都是仿照hibernate在做,那这样的话我们不如直接使用hibernate多好,就没有必要用MyBatis 整的里一半外一半的了。我给大家推荐使用MyBatis -Thymeleaf ,它能把 MyBatis 优点发挥到极致。使用Thymeleaf 摸板语法解析SQL文 ,可以进行复杂的业务逻辑动态SQL语句操作 。这样可以使SQL语句的管理与操作变得十分方便与简单,SQL文的动态使用变得非常灵活同时项目结构也会十分简洁清晰。最主要的是能提高开发效率,同时降低后期的维护成本。对新手也十分友好学习成本不高,可以让新人快速融入到项目开发中,达到上手就干活目的 。
在项目Maven中引入下载 MyBatis Thymeleaf 插件jar包,同时也需要导入其他第三方的配置包。
pom.xml
<--------------- druid数据连接池部分 ------------------>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.3.6</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.1</version>
</dependency>
<--------------- spring boot 数据源部分 ------------------>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.2.0</version>
</dependency>
<--------------- mybatis 插件部分 ------------------>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<dependency>
<groupId>org.mybatis.scripting</groupId>
<artifactId>mybatis-thymeleaf</artifactId>
<version>1.0.3</version>
</dependency>
<--------------- 数据库驱动部分 ------------------>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
Spring boot 配置文件中配置数据库连接池与mybatis配置
application.yml
spring:
http:
encoding:
force: true
charset: utf-8
enabled: true
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
initial-size: 20
max-active: 100
min-idle: 10
max-wait: 60000
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
dynamic:
primary: master
strict: false
datasource:
master:
url: jdbc:mysql://localhost:3306/Test?useUnicode=true
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapperLocations: classpath*:/mapper/*.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
default-scripting-language: org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriver
Spring boot 容器中加入 ThymeleafLanguageDriver驱动容器 default-scripting-language: org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriver
对MyBatis 数据源配置不太懂得地方可以看我前面的文章。【Springboot 入门培训】#3 MyBatis 多数据源与缓存和数据连接池设置
基本用法与正常的MyBatis 操作一样,分为XML文件,和类注解,摸板文件中三种操作模式。摸板文件是自己定义一个SQL文本文件,在这个文本文件中SQL语句与Thymeleaf 语法结合,动态生成可执行的SQL语句。这个摸板模式操作是我最喜欢它的地方之一。
| 属性 | 说明 |
|---|---|
| XML文操作方法 | 使用传统方式在xml配置文件中进行动态模板解析SQL文 |
| 注释映射器 | 在@Select 类注解中写入SQL模板,并进行动态解析SQL 文 |
| 注释映射器SQL摸板 | 在@Select 类注解中写入模板文件地址,在模板中解析SQL文 (推荐使用) |
数据库中创建部门表
CREATE TABLE `dept` (
`id` int(11) DEFAULT NULL,
`pid` int(11) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
项目
|--src
| |--cn.core.sys
| | |--OnApp //容器启动类
| | |--MyBatisTest //测试类
| |--dao //数据库操作接口
| |--UserDao //数据操作类
|--application.properties //项目配置文件
|--mapper
| |--dept // SQL摸板文件夹
| |-- SQL摸板文件
| |--UserSql.xml //MyBatis数据库sql文文件
|--lib //项目运行jar包
在mapper目录下创建UserSql.xml 文件,在UserSql.xml中定义一个select 属性节点,这个属性节点中使用 <![CDATA[ ]]>包裹SQL文。文插件会认为这个被包裹的SQL是摸板语言,对它进行语法转换生成可执行的SQL。 注意 <![CDATA[ SQL文 ]]>
UserSql.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="cn.core.sys.dao.UserDao">
<select id="findDeptId" resultType="map">
<![CDATA[ ---------------注意SQL文一定要在<![CDATA[ SQL ]]>
SELECT * FROM dept
WHERE id = /*[# mb:p="id"]*/ 1 /*[/]*/
]]>
</select>
</mapper>
创建UserDao类
@Component
@Mapper
public interface UserDao {
//查询部门信息
List findDeptId(Map map);
}
测试类
@RunWith(SpringRunner.class)
@SpringBootTest(classes=OnApp.class)
public class TestMain {
@Autowired//引入业务接口类
UserDao userdao;
@Test
public void TestUserList(){
Map map= new HashMap();
map.put("id","1");
List<Map> list=userdao.findDeptId(map);
}
}
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@7b451bf4] will not be managed by Spring
==> Preparing: SELECT * FROM dept WHERE id = ?
==> Parameters: 1(String)
<== Columns: id, pid, name
<== Row: 1, 0, 部门一
<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7f12094d]
使用类注解的方式引入摸板语言,动态解析可执行的SQL文。
UserDao类
@Mapper
public interface UserDao {
@Select("SELECT * FROM dept WHERE id = /*[# mb:p='id']*/ 1 /*[/]*/")
List<Map> DeptId(Map map);
List findDeptId(Map map);
}
测试类
@RunWith(SpringRunner.class)
@SpringBootTest(classes=OnApp.class)
public class TestMain {
@Autowired//引入业务接口类
UserDao userdao;
@Test
public void TestUserList(){
Map map= new HashMap();
map.put("id","1");
List<Map> list=userdao.DeptId(map);
}
}
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@59baf2c7] will not be managed by Spring
==> Preparing: SELECT * FROM dept WHERE id = ?
==> Parameters: 1(String)
<== Columns: id, pid, name
<== Row: 1, 0, 部门一
<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@57e03347]
使用 MyBatis SQL注解指定摸板文件路径,在对应的摸板文件中写入动态SQL文。插件通过注解指定的摸板文件路径找到这个摸板,读取上面的摸板内容,进行解析生成可执行的SQL文。
例如所示:mapper/dept/deptList.sql
在mapper目录下创建一个dept文件夹,在dept文件夹中创建一个deptList.sql文件。注意 在引用的时候需要在mapper 路径下 @Select(“mapper/dept/deptList.sql”)。

**创建 deptList.sql **
SELECT * FROM dept WHERE pid = /*[# mb:p='id']*/ [(${id})] /*[/]*/
UserDao类
@Mapper
public interface UserDao {
//文件路径 注意 mapper 是XML文件下
@Select("mapper/dept/deptList.sql")
List<Map> DeptList(Map map);
@Select("SELECT * FROM dept WHERE id = /*[# mb:p='id']*/ 1 /*[/]*/")
List<Map> DeptId(Map map);
List<Map> findDeptId(Map map);
}
测试类
@RunWith(SpringRunner.class)
@SpringBootTest(classes=OnApp.class)
public class TestMain {
@Autowired//引入业务接口类
UserDao userdao;
@Test
public void TestUserList(){
Map map= new HashMap();
map.put("id","2");
List<Map> list=userdao.DeptList(map);
}
}
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@4b98225c] will not be managed by Spring
==> Preparing: SELECT * FROM dept WHERE pid = ?
==> Parameters: 1(String)
<== Columns: id, pid, name
<== Row: 3, 1, 部门3
<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@733f1395]
SELECT * FROM dept
WHERE 1 = 1
/*[# th:if="${name} != null"]*/
AND name = '[(${name})]'
/*[/]*/
ORDER BY id
th:switch 判断使用th:case 判断使用SELECT * FROM dept
WHERE id =[(${id})]
/*[# th:if="${not #lists.isEmpty(ids)}"]*/
/*[# th:with="zht=22"]*/ //局部变量赋值
and name='[(${zht})]'
/*[/]*/
/*[/]*/
使用th:eachSELECT * FROM dept
WHERE 1 = 1
/*[# th:if="${not #lists.isEmpty(ids)}"]*/
AND id IN (
/*[# th:each="id : ${ids}"]*/
/*[# mb:p="id"]*/ 1 /*[/]*/
/*[(${idStat.last} ? '' : ',')]*/
/*[/]*/
)
/*[/]*/
ORDER BY id
测试类
Map map= new HashMap();
List ids=new ArrayList();
ids.add("1");
ids.add("2");
map.put("ids",ids);
List<Map> list=userdao.DeptList(map);
----------------------------- 测试结果 ---------------------------
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@38022758] will not be managed by Spring
==> Preparing: SELECT * FROM dept WHERE 1 = 1 AND id IN ( ? , ? ) ORDER BY id
==> Parameters: 1(String), 2(String)
<== Columns: id, pid, name
<== Row: 1, 0, 部门一
<== Row: 2, 0, 部门2
<== Total: 2
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1132baa3]
mb 标签 是mybatis-thymeleaf 提供的特殊属性标签。摸板解析后生成 #{…}可以被 MyBatis 核心模块的进行数据值绑定。
#{id} 可以被 MyBatis 核心模块与参数进行绑定执行。SELECT * FROM dept
WHERE id = /*[# mb:p="id"]*/ 1 /*[/]*/
------- 执行结果 -------
SELECT * FROM dept WHERE id = ?
集合和数组的用法
SELECT * FROM dept
WHERE id IN (/*[# mb:p="ids"]*/ 1 /*[/]*/)
------- 传入参数 -------
Map map= new HashMap();
List ids=new ArrayList();
ids.add("1");
ids.add("2");
map.put("ids",ids);
List<Map> list=userdao.DeptList(map);
------- 执行结果 -------
==> Preparing: SELECT * FROM dept WHERE id IN (?, ?)
==> Parameters: 1(String), 2(String)
<== Columns: id, pid, name
<== Row: 1, 0, 部门一
<== Row: 2, 0, 部门2
<== Total: 2
对数据的增删修改操作中也可以使用Thymeleaf 摸板语法功能,分别对应三种注解。
@Mapper
public interface UserDao {
@Insert("INSERT INTO dept values(4,0,'部门') ")
@Update("update pid=1 where id='[(${id})]'")
@Delete("update pid=1 where id='[(${id})]'")
}
这三种操作也可以用一个注解全部代替。
在mapper目录dept文件夹中创建一个功能添加摸板comsave.sql,我们可以更具摸板语言自己写一个简单的添加动态SQL,通用与所有表的添加操作。
comsave.sql
INSERT INTO [(${tabelName})] (
/*[# th:each="userKey,userStat:${userMap}"]*/
/*[# th:if="${userKey.key}!='IdValue'"]*/
/*[# th:if="${userStat.index!=0}"]*/
,
/*[/]*/
[(${userKey.key})]
/*[/]*/
/*[/]*/
)
VALUES(
/*[# th:each="userKey,userStat:${userMap}"]*/
/*[# th:if="${userKey.key}!='IdValue'"]*/
/*[# th:if="${userStat.index!=0}"]*/
,
/*[/]*/
/*[# th:if="${userKey.key}!=${IdName}"]*/
/*[# th:if="${userKey.value==null}"]*/
''
/*[/]*/
/*[# th:if="${userKey.value!=null}"]*/
'[(${userKey.value})]'
/*[/]*/
/*[/]*/
/*[# th:if="${userKey.key}==${IdName}"]*/
[(${userKey.value})]
/*[/]*/
/*[/]*/
/*[/]*/
)
UserDao业务类
@Mapper
public interface UserDao {
//添加数据
@Insert("mapper/dept/comsave.sql")
int ComSave(Map map);
}
测试类
@RunWith(SpringRunner.class)
@SpringBootTest(classes=OnApp.class)
public class TestMain {
@Autowired//引入业务接口类
UserDao userdao;
@Test
public void TestUserList(){
Map map= new HashMap();
//数据库表内容
Map userMap= new HashMap();
userMap.put("id",4);
userMap.put("pid",0);
userMap.put("name","部门4");
map.put("userMap",userMap);//添加内容
//表名称
map.put("tabelName","dept");
System.out.println(userdao.ComSave(map));
}
}
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@247bbfba] will not be managed by Spring
==> Preparing: INSERT INTO dept ( name , pid , id ) VALUES( '部门4' , '0' , '4' )
==> Parameters:
<== Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@733f1395]
1
参考 Thymeleaf Map数据结构遍历
/*[# th:each="userKey,userStat:${userMap}"]*/
[(${userKey.key})]
'[(${userKey.value})]'
/*[/]*/
在mapper目录中在dept文件夹中创建一个功能修改摸板comupdate.sql。
comupdate.sql
/*[# th:if="${whereMap!=null}"]*/
update [(${tabelName})] set
/*[# th:each="userKey,userStat:${userMap}"]*/
/*[# th:if="${userStat.index!=0}"]*/
,
/*[/]*/
/*[# th:if="${userKey.value!=null}"]*/
[(${userKey.key})] ='[(${userKey.value})]'
/*[/]*/
/*[/]*/
where
/*[# th:each="userKey,userStat:${whereMap}"]*/
/*[# th:if="${userKey.value!=null}"]*/
[(${userKey.key})] ='[(${userKey.value})]'
/*[/]*/
/*[/]*/
/*[/]*/
UserDao业务类
@Mapper
public interface UserDao {
//修改
@Update("mapper/dept/comupdate.sql")
int ComUpate(Map map);
@Insert("mapper/dept/comsave.sql")
int ComSave(Map map);
}
测试类
@RunWith(SpringRunner.class)
@SpringBootTest(classes=OnApp.class)
public class TestMain {
@Autowired//引入业务接口类
UserDao userdao;
@Test
public void TestUserList(){
Map map= new HashMap();//操作数据结构
//修改内容
Map userMap= new HashMap();
userMap.put("pid",0);
userMap.put("name","部门4");
map.put("userMap",userMap);
//修改条件
Map whereMap= new HashMap();
whereMap.put("id",4);
map.put("whereMap",whereMap);
//修改表
map.put("tabelName","dept");
System.out.println(userdao.ComUpate(map));
}
}
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@49c83262] will not be managed by Spring
==> Preparing: update dept set name ='部门4' , pid ='0' where id ='4'
==> Parameters:
<== Updates: 2
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@a21c74]
在mapper目录中在dept文件夹中创建一个功能删除摸板comdelete.sql。
comdelete.sql
/*[# th:if="${whereMap!=null}"]*/
delete from [(${tabelName})]
where
/*[# th:each="userKey,userStat:${whereMap}"]*/
/*[# th:if="${userKey.value!=null}"]*/
[(${userKey.key})] ='[(${userKey.value})]'
/*[/]*/
/*[/]*/
/*[/]*/
UserDao业务类
@Mapper
public interface UserDao {
//删除
@Delete("mapper/dept/comdelete.sql")
int ComDelete(Map map);
@Insert("mapper/dept/comsave.sql")
int ComSave(Map map);
@Update("mapper/dept/comupdate.sql")
int ComUpate(Map map);
}
测试类
@RunWith(SpringRunner.class)
@SpringBootTest(classes=OnApp.class)
public class TestMain {
@Autowired//引入业务接口类
UserDao userdao;
@Test
public void TestUserList(){
Map map= new HashMap();
//删除条件设置
Map whereMap= new HashMap();
whereMap.put("id",4);
map.put("whereMap",whereMap);
//删除数据表
map.put("tabelName","dept");
System.out.println(userdao.ComDelete(map));
}
}
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@44eb2452] will not be managed by Spring
==> Preparing: delete from dept where id ='4'
==> Parameters:
<== Updates: 2
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@21dea711]
Insert批量操作的时候,表的ID是处理关键,很多情况是设置为自动增长,重实际得到的经验最好是UUID。我一般都是使用UUID为主键这样的好处是冲突少,跨库表迁移的时候兼容性好后续的麻烦少一点,便于维护与数据变迁。批量操作的原理很简单,将数据转入到List中,用摸板动态循环提交。
改造一下批量查询摸板
/*[# th:each="listKey,userStat:${list}"]*/
/*[# th:if="${userStat.index==0}"]*/
INSERT INTO [(${tabelName})] (
/*[# th:each="userKey,userStat:${listKey}"]*/
/*[# th:if="${userKey.key}!='IdValue'"]*/
/*[# th:if="${userStat.index!=0}"]*/
,
/*[/]*/
[(${userKey.key})]
/*[/]*/
/*[/]*/
) VALUES
/*[/]*/
(
/*[# th:each="userKey,userStat:${listKey}"]*/
/*[# th:if="${userKey.key}!='IdValue'"]*/
/*[# th:if="${userStat.index!=0}"]*/
,
/*[/]*/
/*[# th:if="${userKey.key}!=${IdName}"]*/
/*[# th:if="${userKey.value==null}"]*/
''
/*[/]*/
/*[# th:if="${userKey.value!=null}"]*/
'[(${userKey.value})]'
/*[/]*/
/*[/]*/
/*[# th:if="${userKey.key}==${IdName}"]*/
[(${userKey.value})]
/*[/]*/
/*[/]*/
/*[/]*/
)[(${userStat.size==u
serStat.count?";":","})]
/*[/]*/
参考 th:each=“userKey,userStat:${listKey}”] userStat属性值
index属性。count属性。size物业。current物业。even/odd布尔属性。first布尔属性。last布尔属性。测试类
@RunWith(SpringRunner.class)
@SpringBootTest(classes=OnApp.class)
public class TestMain {
@Autowired//引入业务接口类
UserDao userdao;
@Test
public void TestUserList(){
Map map= new HashMap();
List list=new ArrayList();
//循环加入数据
for(int i=0;i<=1000;i++){
Map userMap= new HashMap();
userMap.put("id",i);
userMap.put("pid",0);
userMap.put("name","部门"+i);
list.add(userMap);
}
//添加数据设置
map.put("list",list);
//表名
map.put("tabelName","dept");
System.out.println(userdao.ComSave(map));
}
}
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@5d221b20] will not be managed by Spring
==> Preparing: INSERT INTO dept ( name , pid , id )
VALUES ( '部门4' , '0' , '4' ), ( '部门5' , '0' , '5' ),
( '部门6' , '0' , '6' ), ( '部门7' , '0' , '7' ),
( '部门8' , '0' , '8' ),( '部门9' , '0' , '9' ), ( '部门10' , '0' , '10' );
==> Parameters:
<== Updates: 7
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5fb8dc01]
希望通过上面的例子,大家可以根据自己的具体业务逻辑来使用模板写自己的业务逻辑。要懂得灵活应用,把模板的功能发挥出来可以提高不少开发效率,同时也能解决不少负责的业务逻辑问题。