mybatis-使用篇
前言:本文依照视频学习做的总结,视频地址:b站_尚硅谷_MyBatis
一、基本使用
mybatis官方_入门
文件说明
全局配置文件:mybatis-config.xml
映射文件:XxxMapper.xml
接口:XxxMapper.java
类说明
SqlSessionFactory:SqlSession工厂,根据配置信息生成对应的SqlSession工厂,以生产SqlSession
SqlSession:底层与Connection一致,每次操作需要使用新的一个SqlSession进行,是线程不安全的
1、全局配置文件
mybatis-config.xml为全局配置文件,该文件是可选的,配置文件添加数据库连接信息,映射文件地址等
2、映射文件
namespace(命名空间):可以随意指定或者在接口式编程中指定接口路径
id:sql语句的唯一标识,如果是在接口式编程中应该与接口对应的方法名称一致,达到绑定效果
3、生成SqlSessionFactory
将全局配置文件中输入流出入到SqlSessionFactoryBuilder以生成SqlSessionFactory
String resource = "xx/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
4、获取SqlSession
通过SqlSessionFactory生产SqlSession
SqlSession session = sqlSeionFactory.openSession();
...
// 注意执行完成后关闭SqlSession
session.close();
两种获取方式:
SqlSession session = SqlsessionFactory.openSession()
需要手动提交:session.commit()
SqlSession session = SqlsessionFactory.openSession(true)
将自动提交
5、执行SqlSession
/*
selectOne(str,obj)
str:指定执行的是哪条sql语句,命名空间+标识
obj:传入参数
*/
Blog blog = (Blog) session.selectOne("xx.selectBlog", 101);
/*
接口式编程,通过接口调用绑定的映射配置,getMapper返回接口的代理类
*/
XxxMapper mapper = session.getMapper(XxxMapper.class);
mapper.xxx();
二、全局配置文件
mybatis官方_配置说明
1、properties
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。例如:
设置好的属性可以在整个配置文件中用来替换需要动态配置的属性值。比如:
2、Setting
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
3、typeAliases
typeAliases(类型别名)可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
当这样配置时,Blog
可以用在任何使用 domain.blog.Blog
的地方。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
每一个在包 domain.blog
中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author;若有注解,则别名为其注解值。见下面的例子:
@Alias("author")
public class Author {
...
}
4、transactionManager
事务管理器(transactionManager)
在 MyBatis 中有两种类型的事务管理器:type="[JDBC|MANAGED]"
- JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
- MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。
例如:
5、databaseIdProvider
数据库厂商标识(databaseIdProvider)
MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载带有匹配当前数据库 databaseId 属性和所有不带 databaseId 属性的语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。 为支持多厂商特性,只要像下面这样在 mybatis-config.xml 文件中加入 databaseIdProvider 即可:
databaseIdProvider 对应的 DB_VENDOR 实现会将 databaseId 设置为 DatabaseMetaData#getDatabaseProductName() 返回的字符串。 由于通常情况下这些字符串都非常长,而且相同产品的不同版本会返回不同的值,你可能想通过设置属性别名来使其变短:
使用在映射文件上,表示在当前连接符合时调用,调用顺序,有指定优先默认没有指定。
6、mappers
映射器(mappers):找到映射文件,
- resource:xml文件
- class:接口类,需要xml命名与接口相同,并且编译时在同一包下
- package:接口所在包,需要xml命名与接口相同,并且编译时在同一包下
IDEA中,class和packge情况需要resource和main具有同一的目录结果,编译时才能编译到相同的包下:运行错误修改
三、映射文件
mybatis官方_xml映射器
1、parameterType
指定传入参数类型,是可选的
2、增删改
增加 修改 删除
返回类型:
mybatis 允许增删改有返回值Integer/Long/Boolean/void包装类基本类
mybatis支持自增主键,自增主键的获取,也是利用statement.getGeneratedKeys()
Oracle自增处理
keyProperty:查出的主键值封装给javabean的哪一个属性
编写查询主键的sql语句
xxx
3、查询
单个参数:
mybatis不会做特殊处理
#{参数名称}
或者#{任意标识}
:取出参数值
多个参数:
mybatis会做特殊处理
多个参数会封装成一个map
key: param1...paramN
value:传入的参数值
#{}
就是从map中获取指定的key值,#{param1}
命名参数:
明确的指定封装参数值时map的key
@Param("id")指定key,如下:
Sell getSellById(@Param("i") int i);
在映射中获取参数#{i}
POJO:
如果多个参数正好事业务逻辑的数据模型,可以直接传入pojo
#{属性名}
:取出传入的pojo的属性值
Map:
如果多个参数不是业务模型中的数据,没有对应的pojo,不经常使用,为了方便,我们也可以传入map
#{key值}
:获取key-value的值
TO:
如果业务中的数据,但是经常使用,推荐编写一个TO
Page{
int index;
int size;
}
拓展:
1、(int id, A a) => 如果想获取a中的某个成员,可以采用#{param2.xx}
2、如果是Collection(List、Set)类型或者是数组,也会特殊处理、也是把传入的list或者数组封装在map中。
key:Collection(collection),如果是List还可以使用这个key(list),数组(array),例如:(List
ids) => #{list[0]}
4、#{key}和${key}的区别
#{}
:以预编译的形式,将参数设置到sql语句中:PreparedStament;防止sql注入
${}
:取出的值直接拼装在sql语句中;会有安全问题
建议:
大多情况下,我们取参数的值应该使用#{}
原生jdbc不支持占位符的地方可以使用${}
进行取值,比如分表${year}_salary
,排序order by s ${_order}
等
5、查询null属性时报错
mysql支持成员null,但是oracle不支持,不支持时无法将Null放进数据库,会报错。取以下任意解决方法。
1、指定参数编译入数据库使用Null处理器:#{email, jdbcType=NULL}
2、全局配置jdbcTypeForNull=Null
6、resultType
返回元素是List
resultType=元素类型
返回一条记录的Map
resultType="map"
返回多条记录的Map
// 1、在接口方法上声明key的属性
@MapKey("id")
Sell getSellById(int i);
// 2、resultType="employee"
7、自动映射
1、sql语句中将变量修改为符合javabean成员变量标识
2、全局配置mapUnderscoreToCamelCase=true
3、自定义resultMap
8、 resultMap
场景一:
Employee含有Department对象,查询Employee同时把Department查询赋值。
1、property=类.成员
2、使用简单的
3、升级的
查询employee,然后根据得到的dept_id去dept表中查询department类赋值给当前employee。
流程:使用select指定的方法(column指定参数的列值)执行select查出对象,封装到property指定的属性
延迟加载:
在分步查询的基础上,当使用第二步的属性时才执行第二步查询
方法:
1、全局配置:分段查询的基础上加上两个配置lazyLoadingEnabled=true
、aggressiveLazyLoading=false
2、当前生效:fetchType="lazy"
:表示使用延迟加载。lazy:延迟;eager:不延迟
9、 resultMap
场景二:
Department含有Employee列表,查询Department同时把Employee列表查询赋值。
1、简单的
...
2、升级的
延迟效果如同
拓展:分步查询传递多参数
将多列的值封装成map传递:colunm="{key1=column1,key2=column2}"
10、 resultMap
鉴别器 - discriminator
可以使用discriminator判断某列的值,然后根据某列的值改变封装的行为
xxx
xxx
xxx
11、动态sql
test:判断表达式(OGNL)根据条件动态的预处理Sql语句
和:and;&& 请用转义字符
或:or;|| 不支持,请用转义字符
字符串:字符串单引号 ' '
OGNL会进行字符串与数字的转换判断'0'==0
拼装and问题
select * from where a = a and b = b
- 1、给where后面加上1=1,以后的条件都是and xxx.
- 2、使用
标签,只会去掉第一个多出来的and和or - 3、使用
标签,
prefix="" :前缀:trim标签体中时整个字符串拼串后端结果。prefix给拼串后的整个字符串加一个前缀
prefixOverrides="":前缀覆盖:去掉整个字符串前面多余的字符
suffix="":后缀:给拼串后的整个字符串加一个后缀
suffixOverrides="":前缀覆盖:去掉整个字符串前面多余的字符
按条件进入,类似于带break的switch-cash
id=#{id}
last_name like #{lastName}
xxx
类似于
用于查询多个返回 where id in (1,2,3);用于批量保存
#{item_id}
可以将OGNL表达式的值绑定到一个变量中,方便后来应用这个值
抽取可重用的sql片段,方便后面引用
1、进程简要查询的列名,或者插入用的列名抽取出来方便引用
2、
12、内置参数
- _parameter:代表整个参数
单个参数:_parameter就是这个参数
多个参数:参数会被封装为一个map:_parameter就是代表这个map - _databaseId:如果配置了DatabaseIdProvider标签,_databaseId就是代表当前数据库的别名
四、缓存机制
缓存包含两级缓存
1、一级缓存(本地缓存)
sqllSession级别的缓存(Map)
一级缓存时一直开启的
与数据库同一次会话期间查询到的数据会放在本地缓存中,以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库;
一级缓存失效的情况:
- 1)sqlSession不同
- 2)sqlSession相同,查询条件不同(当前一级缓存中没有该条件)
- 3)sqlSession相同,两次查询直接执行了增删改操作(这次增删改可能对当前缓存有改,一级缓存失效)
- 4)sqlSession相同,收到清除一级缓存(缓存清空)
session.clearCache()
2、二级缓存(全局缓存)
基于namespace级别的缓存:一个namespace对应一个二级缓存
工作机制:
- 1)一个会话,查询一条数据,这个数据挥别仿真当前会话的以及缓存中;
- 2)如果会话关闭;一级缓存中的数据会被保存到二级缓存中;新的会话查询信息,就可以参照二级缓存
- 3)sqlSession不同的mapper存放在自己对应的缓存;
效果:数据会从二级缓存中获取,查出的数据都会被默认先放在一级缓存中;只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中
使用:
- 1)开启全局配置:cacheEnabled=true
- 2)mapper.xml中配置使用二级缓存
/**
eviction:缓存回收策略;
LRU - 最近最少使用的:移除最长时间不被使用的对象
FIFO - 先进先出:按对象进入缓存的顺序移除
SOFT - 软引用:移除基于垃圾回收器状态和软引用规则的对象
WEAK - 弱引用:更积极地移除基于垃圾回收器状态和弱引用规则的对象
默认是LRU
flushInterval:缓存刷新间隔
缓存多长时间清空一次,默认不清空,设置一个毫秒值
readOnly:是否只读
true:只读;mybatis认为用户不会修改数据。为了加快获取数据,直接会将数据在缓存中的引用交给用户
false:非只读:mybatis认为用户可能修改数据。mybatis会利用序列号&反序列化的技术克隆数据。安全,速度慢。默认。
size:缓存存放多少个元素
type:指定自定义缓存的全类名;实现Cache接口即可
*/
- 3)我们的POJO需要实现序列化接口
public class A implements Serializable{
private static final long serialVersionUID = 1L;
...
}
和缓存有关的配置
- 1)
cacheEnabled=true;
false:关闭缓存(二级缓存关闭,一级缓存一直可用) - 2)映射文件中,每个select标签都有
useCache="true";
false:一级缓存一直在,不使用二级缓存 - 3)映射文件中,每个增删改标签的:
flushCache="true";
增删改完成后就会清除缓存(一级缓存二级缓存都会清空); - 4)
sqlSession.clearCache();
只清空当前session一级缓存 - 5)
localCacheScope
:设置参数,本地缓存作用域(一级缓存SESSIOIN:当前会话的所以数据保存在会话缓存中;STATEMENT:可以禁用一级缓存)
3、第三方缓存
- 1)导入第三方缓存包即可
- 2)导入第三方缓存整合的适配包;官方有
- 3)mapper.xml中使用自定义缓存