参考

官方:https://mybatis.org/mybatis-3/zh/index.html

mybatis – MyBatis 3 | 动态 SQL

学习视频

https://www.bilibili.com/video/BV1NE411Q7Nx

缓存

  1. Spring 整合 MyBatis 时一级缓存失效问题

    • 在未开启事务的情况之下,每次查询,spring 都会关闭旧的 sqlSession 而创建新的 sqlSession, 因此此时的一级缓存是没有启作用的
    • 在开启事务的情况之下,spring 使用 threadLocal 获取当前资源绑定同一个 sqlSession,因此此时一级缓存是有效的
  2. 当开启缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库

  3. spring整合 mybatis 中,一级缓存失效及带来脏数据,应该使用STATEMENT级别缓存

    示例:会话1 获取数据,会话 2 修改数据。会话 1 再次获取,返回缓存数据,与实际数据不一致

  4. 对于频繁变动的数据,没必要缓存。对于变化较慢但经常访问的数据,建议缓存

databaseIdProvider

根据不同的数据库厂商执行不同的语句

https://www.jianshu.com/p/b21a8b3fbd53

1. mybatis-config.xml

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- 缓存开关-->
<setting name="cacheEnabled" value="true"/>
<!-- 使用STATEMENT级别缓存 -->
<setting name="localCacheScope" value="STATEMENT"/>
</settings>
</configuration>

2. spring-mybatis.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!--	多数据库支持-->
<bean id="vendorProperties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="Oracle">oracle</prop>
<prop key="Dm">dm</prop>
</props>
</property>
</bean>

<bean id="databaseIdProvider" class="org.apache.ibatis.mapping.VendorDatabaseIdProvider">
<property name="properties" ref="vendorProperties" />
</bean>

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath:mapping/*.xml" />

<!--mybatis全局配置文件 -->
<property name="configLocation" value="classpath:mybatis-config.xml" />

<!-- 数据库厂商标识 -->
<property name="databaseIdProvider" ref="databaseIdProvider" />
</bean>

2. Mapper.xml

方式一

1
2
3
4
5
6
7
8
9
<mapper namespace="com.hengzhe.hz.manager.data.biz.dao.TestDBMapper">
<select id="selectTime" resultType="String" databaseId="mysql">
select now() from dual
</select>

<select id="selectTime" resultType="String" databaseId="oracle">
select sysdate from dual
</select>
</mapper>

方式二

1
2
3
4
5
6
7
8
9
10
11
12
<mapper namespace="com.hengzhe.hz.manager.data.biz.dao.TestDBMapper">
<select id="selectTime2" resultType="String">
select
<if test="_databaseId == 'mysql'">
now()
</if>
<if test="_databaseId == 'oracle'">
sysdate
</if>
from dual
</select>
</mapper>
1
2
3
4
5
6
7
8
<choose>
<when test="_databaseId == 'oracle'">
NLS_UPPER(LOGINNAME) &lt;&gt; 'ROOT'
</when>
<otherwise>
UCASE(LOGINNAME) &lt;&gt; 'ROOT'
</otherwise>
</choose>

参数处理

https://www.cnblogs.com/mingyue1818/p/3714162.html

参数为表名

使用 ${tablename} 代替使用 #{tablename}

1
select * from ${tablename} where eventcode = #{eventcode}

单个参数

1
2
3
4
5
6
7
public List<XXBean> getXXBeanList(String id);  

<select id="getXXXBeanList" parameterType="java.lang.String" resultType="XXBean">
  select t.* from tableName t where t.id= #{id}
</select>

//参数任意命名也可以

多参数

1
2
3
4
5
6
7
public List<XXXBean> getXXXBeanList(String xxId, String xxCode); 

<select id="getXXXBeanList" resultType="XXBean">
  select t.* from tableName where id = #{0} and name = #{1}
</select>

//由于是多参数那么就不能使用parameterType, 改用#{index}是第几个就用第几个的索引,索引从0开始

多参数注解

1
2
3
4
5
public int getXXXBeanList(@Param("score")int score ,@Param("name")String name);

<select id="getXXXBeanList" resultType="XXBean">
  select * from tableName where score = #{score} and name = #{name}
</select>

Map 封装多参数

1
2
3
4
5
6
7
8
map.put("score", 90);
map.put("name", "hello");

public List<XXXBean> getXXXBeanList(HashMap map);

<select id="getXXXBeanList" parameterType="java.util.Map" resultType="XXBean">
  select * from tableName where id = #{score} and name = #{name}
</select>

Array 集合

1
2
3
4
5
6
7
8
public List<XXXBean> getXXXBeanList(String[] ids);  

<select id="getXXXBeanList" resultType="XXBean" parameterType="java.lang.String">
  select * from tableName where id in
  <foreach item="item" index="index" collection="array" open="(" separator="," close=")">
    #{item}
  </foreach>
</select>

List 集合

1
2
3
4
5
6
7
8
9
10
11
public List<XXXBean> getXXXBeanList(List<String> list);  

<select id="getXXXBeanList" resultType="XXBean">
  select * from tableName where 1==1
<if test="list != null and list.size() &gt; 0" >
and id in
<foreach item="item" index="index" collection="list" open="(" separator="," close=")">
     #{item}
   </foreach>
</if>
</select>

多参数(String list )

步骤一

方式 1:参数封装到 map 中,Dao 接口为 map
1
2
3
4
map.put("score", 90);
map.put("list", list);

public int operateBatch(Map<String, Object> map);
方式 2:@param 注解 Dao 参数
1
public int operateBatch(@Param("score")int score ,@Param("list")List<Student> list);

步骤二

1
2
3
4
5
6
7
8
<update id="operateBatch">
update student
set store=#{score}
where id in
<foreach collection="list" index="index" item="item" open="(" separator="," close=")">
#{item.id}
</foreach>
</delete>

自定义mapper示例

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface BaseProMapper<T> extends BaseMapper<T> {
@Update({"update ${tableName} set ${fieldName} = ST_GeometryFromText(#{geoData}) where id=#{id}"})
void updateGeoById(@Param("tableName") String tableName, @Param("fieldName") String fieldName, @Param("geoData") String geoData, @Param("id") String id);

@Select({"select * from ${tableName} where ST_Contains(${fieldName}, ST_GeometryFromText(#{point}))"})
List<T> getDataListContainsPoint(@Param("tableName") String tableName, @Param("fieldName") String fieldName, String point);

@Select({"select * from ${tableName} where ST_Contains(ST_GeometryFromText(#{polygon}), ${fieldName})"})
List<T> getDataListInPolygon(@Param("tableName") String tableName, @Param("fieldName") String fieldName, String polygon);

@Select({"select * from ${tableName} where #{distance} > st_distance_sphere(ST_POINTFROMTEXT(#{point}), ${fieldName})"})
List<T> getDataListInDistanceSphere(@Param("tableName") String tableName, @Param("fieldName") String fieldName, Integer distance, String point);
}

exists 是否存在于大集合中

1
2
3
4
5
6
7
8
9
10
11
select distinct PROC_INST_ID_ from act_hi_taskinst t
<where>
exists(select TASK_ID_ from act_hi_identitylink
where TYPE_ = 'participant' and USER_ID_ = #{username} and t.ID_=TASK_ID_)
<if test="participateTime_begin != null and participateTime_begin != ''">
and t.END_TIME_ >= STR_TO_DATE(#{participateTime_begin},'%Y-%m-%d %H:%i:%s')
</if>
<if test="participateTime_end != null and participateTime_end != ''">
and STR_TO_DATE(#{participateTime_end},'%Y-%m-%d %H:%i:%s') >= t.END_TIME_
</if>
</where>

注意事项

批处理 rewriteBatchedStatements=true

1
2
3
4
MySQL的JDBC连接的url中要加rewriteBatchedStatements参数,并保证5.1.13以上版本的驱动,才能实现高性能的批量插入。
MySQL JDBC驱动在默认情况下会无视executeBatch()语句,把我们期望批量执行的一组sql语句拆散,一条一条地发给MySQL数据库,批量插入实际上是单条插入,直接造成较低的性能。
只有把rewriteBatchedStatements参数置为true, 驱动才会帮你批量执行SQL
另外这个选项对INSERT/UPDATE/DELETE都有效

Mapper xml文件中 SQL 语句不能以分号结尾「;」

参数名区分大小写,一定注意不要写错

1
select * from SGP_INFORMPERSON where INFORMID = #{informid} and TOPERSON = #{toperson}

in 一个集合中为数字,可以通过 concat 拼接字符串,将数字转换为字符串

1
A.DEPTCODE in (concat('', ${house.deptcode}))

数字判断,不要加单引号

noticeperson.readflag == 1

1
2
3
4
<if test="noticeperson.readflag != null and noticeperson.readflag != '' and noticeperson.readflag == 1">
and P.TOPERSON = #{noticeperson.toperson,jdbcType=VARCHAR}
and P.READFLAG = #{noticeperson.readflag,jdbcType=VARCHAR}
</if>