Mybatis笔记
1、#和$的区别
- #{key} :获取参数的值,预编译到 SQL 中。安全。
- ${key} :获取参数的值,拼接到 SQL 中。有 SQL 注入问题。
2、resultType和resultMap区别
- resultType:返回值类型。– 别名或者全类名,如果返回的是集合,定义的是集合中元素的类型。
- resultMap – 自定义结果集映射
- resultType和resultMap不能同时使用。
3、Mybatis缓存机制
-
一级缓存:SqlSession级别、默认支持
每个SqlSession类的实例对象中有一个数据结构(HashMap)可以用来存储缓存数据
缓存的键(key):namespace + sql id +编写的sql查询语句+参数
缓存机制是引用返回,不是深拷贝后返回,所以会存在第一次查询结果修改后,影响从缓存中取出的查询结果
-
二级缓存:Mapper级别、手动开启(在配置文件中设置cacheEnabled属性为true)
多个SqlSession类的实例对象操作同一个Mapper配置文件中的sql语句,多个SqlSession类的实例对象可以共用二级缓存,二级缓存是跨SqlSession的。
一个Mapper有一个自己的二级缓存区域(按照namespace划分),两个Mapper的namespace如果相同,那么这两个Mapper执行的sql查询会被缓存在同一个二级缓存中。
4、动态SQL
- 场景:根据不同条件拼接 SQL 语句
- 解决痛点:添加必要的空格、去掉列表最后一个列名的逗号等拼接sql的麻烦
4.1、if
<select id="selectOptions" parameterType="User" resultMap="User">
select * from user
where
<if test="name!=null">
name like concat('%',#{name},'%')
</if>
<if test="userName!=null">
user_Name like concat('%',#{userName},'%')
</if>
</select>
if 测试如果 name 和 userName 都不为空,会拼接两个if中的语句,形成错误,两个条件之间缺少 and 或 or
select * from user where name like concat('%',?,'%') user_Name like concat('%',?,'%')
4.2、trim(where, set)
where 可解决 if 存在的问题
where标签会自动处理多出来的AND符号。同理 set标签也一样(set标签用于update语句)
<!--与if不同,如果name和userName都不为空,不会发生错误-->
<select id="selectOptions1" parameterType="User" resultMap="User">
select * from user
<where>
<if test="name!=null">
and name like concat('%',#{name},'%')
</if>
<if test="userName!=null">
or user_Name like concat('%',#{userName},'%')
</if>
</where>
</select>
4.3、choose (when, otherwise)
类似于Java 的 switch 语句,choose 为 switch,when 为 case,otherwise 则为 default。
<select id="selectOptions2" parameterType="User" resultMap="User">
select * from user
<where>
<if test="name!=null">
and name like concat('%',#{name},'%')
</if>
<choose>
<when test="age!=null">
and age=#{age}
</when>
<otherwise>
and age=12
</otherwise>
</choose>
</where>
</select>
//不传入年龄时,age自动=12
select * from user WHERE name like concat('%',?,'%') or user_Name like concat('%',?,'%') and age=12
4.4、foreach
<select id="selectOptions3" parameterType="User" resultMap="User">
select * from user
<where>
<if test="name!=null">
and name like concat('%',#{name},'%')
</if>
<if test="idList!=null and idList.size()!=0">
and id in
<foreach collection="idList" open="(" separator="," close=")" index="i" item="item">
#{item}
</foreach>
</if>
</where>
</select>
拼接sql如下
Preparing: select * from user WHERE name like concat('%',?,'%') and id in ( ? , ? )
5、一对一映射和一对多映射
一个用户可以有一个或多个角色
<!--查询用户及其角色,一对一和一对多只需要修改对应的resultMap即可-->
<select id="selectUserRoleById" resultMap="User">
select
u.*,
r.id role_id,
r.role_code,
r.role_name
from user u
left join user_role ur on u.id=ur.user_id
left join role r on ur.role_id=r.id
where u.id=#{id}
</select>
实体类User
public class User {
//省略其他属性
private Role role;
private List<Role> roleList;
//省略set、get
}
public class Role {
private long id;
private String roleCode;
private String roleName;
//省略set、get
}
5.1、一对一映射
<resultMap id="User" type="com.xxxx.mybatis.entity.User">
<id column="id" property="id"/>
<result column="user_name" property="userName"/>
<result column="name" property="name"/>
<result column="birthday" property="birthday"/>
<result column="age" property="age"/>
<association property="role" javaType="com.hand.mybatis.entity.Role">
<id property="id" column="id"/>
<result property="roleCode" column="role_Code"/>
<result property="roleName" column="role_Name"/>
</association>
</resultMap>
5.2、一对多映射
<!--一对多映射-->
<resultMap id="userMap" type="com.xxxx.mybatis.entity.User">
<id column="id" property="id"/>
<result column="user_name" property="userName"/>
<result column="name" property="name"/>
<result column="birthday" property="birthday"/>
<result column="age" property="age"/>
<collection property="roleList" ofType="com.hand.mybatis.entity.Role" foreignColumn="id">
<id property="id" column="role_id"/>
<result property="roleCode" column="role_Code"/>
<result property="roleName" column="role_Name"/>
</collection>
</resultMap>
引用另一个sql进行行数据查询
<resultMap id="BaseResultMap" type="com.inja.mdm.trader.domain.entity.Customer">
<result column="customer_id" property="customerId" jdbcType="DECIMAL"/>
//.......
<collection property="bankList" ofType="com.inja.mdm.trader.domain.entity.CustomerBank"
column="customer_id" foreignColumn="customer_id" javaType="ArrayList"
select="com.inja.mdm.trader.infra.mapper.CustomerBankMapper.selectCustomerBankByHeaderId">
</collection>
<collection property="contactList" ofType="com.inja.mdm.trader.domain.entity.CustomerContact"
column="customer_id" foreignColumn="customer_id" javaType="ArrayList"
select="com.inja.mdm.trader.infra.mapper.CustomerContactMapper.selectCustomerContactByHeaderId">
</collection>
<collection property="invoiceList" ofType="com.inja.mdm.trader.domain.entity.CustomerInvoice"
column="customer_id" foreignColumn="customer_id" javaType="ArrayList"
select="com.inja.mdm.trader.infra.mapper.CustomerInvoiceMapper.selectCustomerInvoiceByHeaderId">
</collection>
</resultMap>
6、对应关系(最基础)
- mybatis-config.xml
<configuration>
<!-- 数据库映射文件-->
<properties resource="db.properties"/>
<!-- Mapper文件 -->
<mappers>
<mapper resource="mapper/User.xml"/>
<mapper resource="mapper/User1.xml"/>
<mapper resource="mapper/User2.xml"/>
</mappers>
</configuration>
- XXMapper.xml文件中namespace与对应Mapper接口的Java全限定路径一致
- MyBatis引用类的方式(全限定名1+别名2)
<!-- 全限定名-->
<select>
<select id="selById2" resultType="com.susu.pojo.People" parameterType="com.susu.pojo.People">
select * from people where id = #{id}
</select>
<!-- 单个类别名-->
<typeAliases>
<package type="com.susu.pojo.People" alias=”People”/>
<package type="com.susu.pojo.People1" alias=”People1”/>
</typeAliases>
<!-- 配置包下所有类-->
<typeAliases>
<package name="com.hand.mybatis.entity"/>
</typeAliases>
单个类别名和配置包之后,即可采用别名进行引用,不必像全限定名一样繁琐
7、其他
- 高版本Mybatis使用#{0}、#{1}接受多个参数,需手动开启
<settings>
<setting name="useActualParamName" value="false" />
</settings>
- Parameter ‘idList’ not found. Available parameters are [collection, list]问题
//参数前面加注解@Param(“idList”)标记所遍历集合的名称
public List<User> selectByIdList(@Param("idList") List<Long> idList);
- 解耦合分离数据库配置文件
问题:failed to parse the connection string near ';characterEncoding=utf8&useSSL=true&serverTimezo
将&替换为&
jdbc:mysql://localhost:3306/xxx?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT
替换后
jdbc:mysql://127.0.0.1:3306/xxx?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT
- source is null for getProperty(null, “id”)
问题:一个类中包含另一个类,Mapper文件判断出错
public class User {
//省略其他属性
private Role role;
}
解决:在出错的判断前,先判断对象是否为空 ,再判断里面的属性,如下
<if test="role!=null">
<if test="role.roleCode!=null">
and role.role_code=#{role.roleCode}
</if>
<if test="role.roleName!=null">
and role.role_name=#{roleName}
</if>
</if>
- Caused by: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named ‘jtFlag’ in ‘class java.lang.String’
强烈注意:@Param(“item”) 不要和 遍历中 collection 的 item=“item” 一样
List<YnjtSmdmItemVO> selectCatgoryChildItems(@Param("item") YnjtSmdmItemDTO itemDTO, @Param("parentCategoryCode") String parentCategoryCode);
<if test="item.jtFlag != null">
AND si.jt_flag = #{item.jtFlag}
</if>
<if test="item.queryItemCodes != null and item.queryItemCodes.size() != 0">
and si.item_code in
<foreach collection="item.queryItemCodes" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</if>
8、配置日志
logging:
level:
com.netflix.discovery: ${LOG_LEVEL:warn}
org.apache.ibatis: ${LOG_LEVEL:debug}
io.choerodon: ${LOG_LEVEL:debug}
org.hzero.boot.platform.lov: ${LOG_LEVEL:warn}
org.hzero: ${LOG_LEVEL:debug}
org.srm: debug
com.inja: ${LOG_LEVEL:debug}