Mybatis相关知识汇总

1.#{}和${}

在mybatis中#{}与${}的区别
先来看两个例子,假设在数据库中有一个name字段,它的类型是varchar,当通过这个字段进行查询语句时,在xml文件中可以使用where name = #{name}或者where name = “${name}”。
假设在数据库中还有一个id字段,它的类型为int,当通过这个字段进行查询语句时,在xml文件中可以使用where id = #{id}
或者where id = ${id}。
在上面这两个例子中,我们可以看到#{}都没有加上双引号, ${ }有一个加上了双引号。之后通过尝试多种数据类型后发现,当为string类型的时候,#{ }会自动将其加上双引号, ${ }解析的时候不会加上双引号,原来是什么现在还是什么。因此 ${ }容易造成SQL注入,而#{}可以很大程度的防止这种。 KaTeX parse error: Expected 'EOF', got '#' at position 38: …序,因此如果order by #̲{pwd},pwd是strin…{}和#{}不要同时使用,否者会出错,因为 ${}和#{}在一条sql语句中,会立刻执行 ${}, #{}执行不了

2.Mysql中表字段设置了默认值,插入数据后默认字段的值却为null

错误原因
对数据库的操作我使用了mybatis持久化工具,插入数据的时候插入的是整个实体,直接使用的是持久层的insert(实体对象)方法插入到数据库。
这样就会出现一个问题,当实体对象中某个属性值为空时,对应的数据库的字段就会插入null值,而默认值是插入时不指定该字段,该字段的值才会取默认值。
所以这里我是向设置了默认值的字段插入了null,才导致出现上述错误。

mapper.xml中自定义字段,默认值的不要写上去,或者采用mybatis 的insertSelective操作动态生成insert语句

3.mybatis返回map类型数据空值字段不显示

一、查询sql添加每个字段的判断空

IFNULL(rate,'') as rate

二、ResultType利用实体返回,不用map
三、springMVC+mybatis查询数据,返回resultType=”map”时,如果数据为空的字段,则该字段省略不显示,可以通过添加配置文件,规定查询数据为空是则返回null。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD SQL MAP Config 3.1//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <settings>
    <setting name="callSettersOnNulls" value="true"/>
  </settings>
</configuration>

数据库里字段是null,map取出来没有该字段,加上这个就有,但如果是取单个字段,map为null
查询获取的map没有按顺序打印,是因为resultType是hashmap,hash值是乱序的,把resultType改为LinkedHashMap

4.xml中<有特殊含义,是标签的起始符号,所以一般用&lt;代替

5.mybatis mapper.xml中sql常用标签

//参数传空对象,不会报错,所有参数都是null
//bean对象和map都是直接#{}里取值
<select id="list" parameterType="Map" resultType="com.mj.bean.Skill">
select * from skill 
<where>
	<if test="id !=null and ...">
	and id >#{id}
	</if>
	<if test="name!=null and ...">
	and name like #{name}
	</if>
</where>
</select>
//像java中的switch语句,多选一
<select id="list" parameterType="Map" resultType="com.mj.bean.Skill">
select * from skill where stste="active"
<choose>
	<when test="id !=null and ...">
	and id >#{id}
	</when>
	<when test="name!=null and ...">
	and name like #{name}
	</when>
	<otherwise>
	 and name like #{name}
	</otherwise>
</choose>
</select>
//list是new ArrayList<bean>()对象的集合
//不支持selectkey回传主键id,只支持这种模式
<insert id="batchinsert1"
useGenerateKeys="true"
keyProperty="id"
 parameterType="List">
insert into skill values
<foreach collection="list" item="skill" separator=",">
	(#{skill.name},#{skill.level})
</foreach>
</insert>
//list是new ArrayList<Integer>()的集合
<delete id="delete1" parameterType="List">
delete from skill where id in
<foreach collection="list" open="(" close=")"  item="id" separator=",">
	#{id}
</foreach>
</delete>
<insert id="insert1" parameterType="List">
insert into skill values(#{name},#{level})
<slectKey resultType="int" keyProperty="id" order="AFTER">
select LAST_INSERT_ID()
</selectKey>
</insert>
**或者**
//要看数据库驱动支不支持,mysql支持oracle不支持
<insert id="insert1" 
useGenerateKeys="true"
keyProperty="id"
 parameterType="List">
insert into skill values(#{name},#{level})
</insert>

6.mybatis设置了别名是不区分大小写的

7.mysql行号从0开始,oracle从1开始

8.mybatis分页插件pagehelper

导入maven依赖,在mybatis-config.xml中配置插件

<plugins>
	<plugin interceptor="com.github.pagehelper.pageInterceptor">
		//分页合理化,小于0就是第一页,超出最大就是最后一页
		<property name="reasonable" value="true" />
	</plugin>
</plugins>

PageHelper.startPage(1, 10);
PageHelper是MyBatis的一个插件,内部实现了一个PageInterceptor拦截器。Mybatis会加载这个拦截器到拦截器链中。在我们使用过程中先使用PageHelper.startPage这样的语句在当前线程上下文中设置一个ThreadLocal变量,再利用PageInterceptor这个分页拦截器拦截,从ThreadLocal中拿到分页的信息,如果有分页信息拼装分页SQL(limit语句等)进行分页查询,最后再把ThreadLocal中的东西清除掉。
注意线程会造成错误分页 资料:https://blog.csdn.net/fedorafrog/article/details/104412140

9.数据库里存储金额用DECIMAL(18,2),第一个表示有效位数包括小数,第二个表示小数位,或者用BIGINT,保留到分 DECIMAL对应的javatype是BigDecimal

10.MySQL中BIGINT与Java数据类型对应问题

问题背景:
最近在做的工作使用了mybatis框架,前两天有一个需求,中间涉及到一条sql,需要将某表中的最大最小主键取出来,mysql中该主键类型是BIGINT(20),我在xml文件中是这样写的:

<select id="getMinAndMaxId" resultType="java.util.Map">
    select min(id) as minId, max(id) as maxId
    from t
</select>

在DAO层,我将其结果存入一个HashMap<String, Long> resMap,但当我执行resMap.get(“minId”);时,系统报错:

java.math.BigInteger can’t be cast to java.lang.Long
之前对这张表操作的时候,都是用Long对应BIGINT,从来没有问题,不明白为什么这次会有异常,我查了一下MySQL和Java数据类型的对应关系,发现,BIGINT确实是对应java.math.BigInteger,但是此时又出现了另外一个疑点。我在操作另外一张表的时候,同样是取最大最小值,同样是BIGINT(20)的主键,同样是存储进HashMap<String, Long>,为什么两个表有这样的差别呢?

最后,以两个表的结构对比为突破口找到了原因。可以成功映射为Long的表用的是BIGINT(20),但是出问题的表使用的是BIGINT(20) UNSIGNED。如果不是无符号类型,BIGINT(20)的取值范围为-9223372036854775808~9223372036854775807。与Java.lang.Long的取值范围完全一致,mybatis会将其映射为Long;而BIGINT(20) UNSIGNED的取值范围是0 ~ 18446744073709551615,其中一半的数据超出了Long的取值范围,Mybatis将其映射为BigInteger。

最后一个问题,为什么HashMap<String, Long>中会存储成BigInteger呢?当然是因为我开始的sql里面没有指定map的类型,所以,各种类型的数据都可以存储的。

11.mybatis中mapper单个对象映射

如person对象里有Company对象company
1.在sql查询语句中设置字段别名company.name,因为特殊符号要加上``号
2.在resultMap里设置result

//resultMap增加主键<id />可以提高效率
<result property="company.name" column="t2_id" />

12.mybatis延迟加载

延迟加载:执行查询时,关联查询不会立即加载。只有在使用关联数据时才会加载。 优点:按需加载,提高效率。
注意: 要使用关联查询的延迟加载,就必须要使用单独查询形式。并且,需要先启用MyBatis的延迟加载配置(需要配置两项):

  1. lazyLoadingEnabled:延迟加载的全局开关(默认为false)。
  2. aggressiveLazyLoading:延迟加载整个对象(默认为true; false:对象的每个属性都会延迟加载,即属性按需加载)
<!-- 如果aggressiveLazyLoading为true,那么lazyLoadingEnabled即使为true也无效。 -->
<settings>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

可以设置延迟加载全局开关,也可以单独在association或者collection里设置fetchType=“lazy”,
colum=“id”,select=“namespace.id”,设置了全局开关,可以不要fetchType,也可以把fetchType=“eager”,不会延迟加载
不要打印对象,和用debug模式,会触发延迟加载

13.mybatis查询出数据的格式

jdbcTmplate的query返回List,没有数据是空的list,查询单个对象也用这个,因为用queryObject查不到数据会报错
mybatis里的collection,如果没有在resultMap里标识,就是null,只要标识了就是list,可能是空list
association

14.mybatis缓存

mybatis缓存select数据
一级缓存是存放到sqlsession对象中,默认开启一级缓存,sqlsession级别,缓存获取的对象是同一个,关闭sqlsession时缓存也就消失了,执行insert,update,delete,commit等方法时,会自动清理一级缓存
一级缓存存放对象。
sqlSession 关闭后(close) ,一级缓存的数据会保存到二级缓存中,新的相同的查询语句就会去二级缓存中去查询
由于一级缓存命中率不高,可以开启二级缓存,namespace级别(mapper)
同一个namespace下的select共享缓存
默认情况下,namespace下update,insert,delete执行成功时,会自动清理二级缓存
参考文章:https://blog.csdn.net/weixin_45286347/article/details/124149359
一级缓存不能关闭,可以这样设置:
MyBatis 一级缓存无法关闭,但是有两种级别可选: session级别的缓存,在同一个sqlSession内,对同样的查询将不再查询数据库,直接从缓存中取。 statement级别的缓存,为了避免上述问题,可以将一级缓存的级别设为 statement 级别的,这样每次查询结束都会清掉一级缓存。

14.sqlsession可以一直不关闭吗

细究MyBaties中SqlSession使用之后为什么需要close
在Mybatis文档中有这样一句话
打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。
Mybatis文档

可是我们知道SqlSessionFactory相当于一个数据库连接池,而由SqlSessionFactory得到的SqlSession则相当于一条数据库连接.

而在我们学习线程池时,我们明白线程池中线程的寿命是由线程池本身进行调控的,我们申请一个线程之后只管用就好了,线程池自己会在最合适的时候去释放掉这个线程.从而避免频繁申请和销毁线程.

数据库连接池的原理与线程池原理类似,那为什么一个需要即使去释放一个不需要呢?

这主要是因为我们在编写线程池代码时,我们可以’感知’被使用的线程何时该被回收(任务完成时被回收),而一条连接何时被使用完毕作为数据库连接池是无法’感知’的,所以当我们使用完一条连接后我们需要告知数据库连接池我们已使用完毕,可以进行回收了,而告知的操作就是close.也就是说SqlSession的close并不是将连接关闭,而是将Sqlsession回收进数据库连接池即SqlSessionFactory

15.aggressiveLazyLoading 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载

16.开启二级缓存,mapper中的<cache />标签常用属性

size:缓存多少个存储结果(单个对象或一个列表的引用)默认值1024
eviction:当缓存量超过size时的清除策略,LRU,FIFO
flushInterval:每隔多少毫秒清除一次缓存,默认不会定时清除
readOnly:true代表缓存的是对原对象的引用,false代表的是原对象序列化后的拷贝对象
所以false时要求实现Serializable接口,默认值是false

17.注解和xml注意

第一种纯mapper.xml,定义接口和接口实现类,在实现类中用sqlsession操作
第二种定义接口,mapper.xml中namespace等于接口全类名,sqlsession.getMapper动态代理获取接口实现类
第三种全注解
如果xml和注解混合使用,主配置文件的mapper注册只写xml的,xml的namespace匹配了全类名
注解里不用写id resultType parameterType
全注解有不方便的地方,association和collection不能直接用result映射,只能用select等于全类名.方法名
像foreach这种也没有。只能在sql前拼上script
<script>
delete from skill where id in
<foreach collection=“list” open=“(” close=“)” item=“id” separator=“,”>
#{id}
</foreach>
</script>

18.其他相关

https://www.lmlphp.com/user/59232/article/item/2369604/

19.自动映射和一对多查询去重

自动映射有全局配置,有在resultMap里配置

<settings>
    <setting name="autoMappingBehavior" value="自动映射规则(NONE/PARTIAL/FULL)"/>
    //l NONE表示不启用自动映射

	//l PARTIAL表示只对非嵌套的resultMap进行自动映射

	//l FULL表示对所有的resultMap都进行自动映射
</settings>

<resultMap id="orderModelMap2" type="com.javacode2018.chat05.demo7.model.OrderModel" autoMapping="true">
</resultMap>

一对多去重,开启自动映射后在resultMap中可以配置一个主键(一定要有一个,不然不会去重),如果没有主键,就写几个可以唯一标识的result
查询字段是list,没有值就是size=0,对象就是null
jdbcTemplate不要用queryforobject查询,没有对象会报错,这个查sql函数cout等

需要注意的是若定义的类是双向关联,即双方的属性中均有对方对象作为域属性出现,那么它们在定义各自的 toString()方法时需要注意,只让某一方可以输出另一方即可,不要让双方的 toString()方法均可输出对方。这样会造成栈内存溢出的错误。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值