mybatis笔记(未整理)
https://www.cnblogs.com/alexanderthegreat/p/7583529.html 例子
https://www.cnblogs.com/gcm688/p/9024655.html
mybatis-jar 3.4.1
搭建mybatis https://www.cnblogs.com/wvae/p/9607684.html
url: jdbc:mysql://localhost:3306/yinliu?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
driver: com.mysql.cj.jdbc.Driver
不使用接口式编程
但Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101); 必须和这能对应
缺点:通过字符串去调用标签定义的SQL,第一容易出错,第二是当XML当中的id修改过以后你不知道在程序当中有多
少个地方使用了这个ID,需要手工查找并一一修改。
接口式编程
什么是Mapper的动态代理
采用Mapper动态代理方法只需要编写相应的Mapper接口(相当于Dao接口),那么Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同Dao接口实现类方法。
Mapper接口开发需要遵循以下规范:
1、Mapper.xml文件中的namespace与mapper接口的全类名相同。
2、Mapper接口方法名和Mapper.xml中定义的每个statement的id相同。
3、Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同。
4、Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同。
优点:这样以来当我们修改了XML的ID以后,只需要修改接口中的方法就可以了,编译器会在其他使用该接口的地方报错,
很容易进行修改。当然好处还不只这些,还可以通过与spring进行无缝集成,动态注入 等等。
--UserMapper是一个接口 它并没有实现类,为什么接口可以直接使用呢? 那是因为MyBbatis使用了JDK动态代理机制动态生成了代理类
总结:
SqlSession代表和数据库的一次会话,用完必须关闭
SqlSession和connection一样都是非线程安全,每次使用都应该去获取新的对象
mapper接口没有实现类,但是mybatis会为这个接口生成一个代理对象
两个重要的配置文件:
mybatis的全局配置文件,包含数据库连接池信息,事务管理器细腻些等 系统运行
使用dbconfig.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
jdbc.username=root
jdbc.password=root
注意 &和直接写在里面的不同
setting 里开启驼峰命名mapUnderscoreToCamelCase
sql语句中as
用resultmap
注解
@Alias("emp") 要和
搭配才能用
==================================================================
增删改返回值默认为影响的行数,若自己在接口定义为boolean(无需在mapper配置文件写resulttype,也写不了) 则为true或folse
可以有 void boolean long intger
提交
sqlSessionFactory.openSession();=>手动提交
sqlSessionFactory.openSession(true);=>自动提交
获取自增主键的方法
mysql
标签里加 useGeneratedKeys="true" keyProperty="id" 然后在代码里就可以得到id的值 不然获取id的值为null
oracle
使用序列来模拟自增
select EMPLOYEES_SEQ.nextval from dual
//before 运行sql语句前先执行此sql 查出的id值封装给javabean的id属性
多个参数取值
public Employee getEmpByIdAndLastName(Integer id,String LastName) 取值#{param1} #{param2} 或者#{1} #{1}
命名参数:public Employee getEmpByIdAndLastName( @Param("id") Integer id , @Param("LastName)") String LastName)
明确指定封装参数时map的key:@Param("id") 多个参数会被封装成一个map key:使用@Param注解指定的值 value:参数值
{指定的key} 取值对应的参数值
pojo:
如果多个参数正好是我们业务逻辑的数据模型,我们就可以直接传入pojo #{属性名} :取出传入的pojo的属性值
Map:
如果多个参数不是业务模型中的数据,没有对应的pojo,(不经常使用,因为每次都要定义一个map)为了方便,我们也可以传入map
sql中用#{key值}去除对应的value值
map<String,object> map = new HashMap<>();
map.put("id",1);
map.put("lastname","tom"); //只能select
public employee getempbymap(map<string,object> map);
TO:
如果多个参数不是业务模型中的数据,但是经常要使用,推荐来编写一个TO(transfer object)数据传输对象
page{ //分页
int index;
int size;
}
思考
public employee getemp( @param("id") integer id, String lastname);
取值 id>#{id/param1} lastname>#{param2}
public employee getemp(integer id, @param("e") employee emp);
取值 id>#{param1} lastname>#{param2.lastname/e.lastname}
特别注意:如是Collection(List,Set)类型或者是数组也会特殊处理,它是把传入的list或者数组封装在map中
key:Collection(collection),如果是List还可以使用key(list) 数组(array)
public employee getempbyid(List
取值:取出第一个id的值: #{liist[0]}
=======================================================
{}:可以获取map中的值或pojo对象属性的值
${}:可以获取map中的值或pojo对象属性的值
select * from emp where id=${id} and last_name=#{lastname}
perparing:select * from emp where id=2 and last_name=?
区别:
#{}:是以预编译的形式,将参数设置到sql语句中;perparedstatement;防止sql注入
${}:取出的值直接拼接在sql语句中,会有安全问题;
大多数情况下,我们去参数的值都应该去使用#{};
原生jdbc不支持占位符的地方我们就可以使用${}进行取值
比如分表,按照年份分表拆分
select * from ${year}_salary where ***;
select * from employee order by ${f_name} ${order}
select * from ${tablename} where id=${id} and name=#{name} ${tablename}在test时给map中加入这个key 结果是可以取出来的
{lastname} 传参时 ("%e%") 即是模糊查询 sql用like
@MapKey("id")
public Map<Integer,Employee> get(Integer id); sql的resultType="pojo"
==========================
resultMap 自定义结果集
联合查询:级联属性封装结果集dept.id dept.name
association可以指定联合的javabean对象
property="dept":指定哪个属性是联合对象
javatype:指定这个属性对象的类型【不能省略】
分布查询 用查出的部门id再去通过部门的mapper里的方法查
association定义关联对象的封装规则
select:表明当前属性是调用select指定的方法查出的结果
column:指定将哪一列的值传给这个方法
流程:使用select指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的属性
<association property="dept" select="部门mapper的namespace.select的id”colum="d_id">
===========================================
注意:
Mybatis中是否需要依赖配置文件的名称要和mapper接口的名称一致
一:当核心配置文件mapper标签下以resource形式指向依赖配置文件时,不需要
这样就可以加载到其相应的依赖配置文件通过namespace找到其相应的方法
二:如果mapper标签下以package包扫描形式时,需要。
原因如下:
1.包扫描形式时。实体类+Mapper接口通过动态代理调用方法
2.调用方法时会找其相应的映射配置文件
3.当多个mapper接口和mapper.xml同时存在,如果没有相同的名称,则动态代理就不能通过其一一对应的依赖配置文件创建其相应的实现方法
================================================
开启延迟加载
collection定义关联集合类型的属性封装规则
ofType:指定集合里面元素的类型
多列的值传递过去:
将多列的值封装成map传递
column="{key1=column1,key2=column2}"
fetchType="lazy":表示使用延迟加载
-lazy:延迟
-eager:立即
column:指定要判别的列明
javaType:列值对应的java类型
女生 resultType:指定封装的结果类型
<association ...
鉴别器:mybatis可以使用discriminator判别某列的值,然后根据某列的值
改变封装行为
===============================================
OGNL
if标签
查询的时候如果某些条件没带可能sql拼装会有问题
1.给where后面加上1+1,以后的条件都and XXX
2.mybatis使用where标签来将所有的查询条件包括在内,mybatis就会将where标签中拼装的sql,多出来的and或者or去掉
注意:where只会去掉第一个多出来的and或者or
例子:
and last_name like #{lastname}
trim标签
后面多出的and或者or where不能解决
prefix:前缀,trim标签体中是整个字符串拼接后的结果
prefix给拼串后的整个结果加一个前缀
prefixOverrides:前缀覆盖,去掉整个前面多余的字符
suffix:后缀
suffixOverrides:后缀覆盖,
例子:<trim prefix="where" suffixOverrides='and">
choose ( when, otherwise): 分类选择,带了break的switch-case
如果带了id就用id查,如果带了lastname就用lastname查,只会进入第一个满足的
例子:
select * from tbl_employee
set标签 (更新操作)可以用trim代替这个作用
可以去除没用的逗号
====================================================
mybatis-缓存机制
mybatis系统中默认定义了两级缓存
一级缓存和二级缓存
1.默认情况下,只有一级缓存(sqlsession级别的缓存,也称本地缓存)开启
2.二级缓存需要手动开启和配置他是基于namespace级别的缓存
3.为了提高扩展性,mybatis定义了缓存接口cache,我们可以通过实现cache
接口来自定义二级缓存
一级缓存:(本地缓存)相同的sql就只发一次
sqlSession级别的缓存,一级缓存是一直开启的;sqlSession级别的一个map
与数据库同一次会话期间查询到的数据会放在本地缓存中
以后如果需要获取相同的数据,直接从缓存中拿,没必要再去数据库查
一级缓存失效情况(没有使用到当前一级缓存的情况,效果就是,还需要再向数据库发出查询)
1.sqlSession不同
2.sqlSession相同,条件不同(缓存中没有这个数据)
3.sqlSession相同,两次查询之间执行了增删改操作(这次增删改可能对当前数据有影响)
4.sqlSession相同,手动清除了一级缓存(缓存清空)openSession.clearCache();
二级缓存:(全局缓存):基于namespace级别的缓存,一个namespace对应一个二级缓存
工作机制:
1.一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中
2.如果会话关闭,一级缓存中的数据会被保存到二级缓存中,新的会话查询信息就可以参照二级缓存
3.不同namespace查出的数据会放在自己对应的缓存中(map)
使用:
1. 开启全局二级缓存配置:<setting name="cacheEnables" value="true"/>
2.去mapper.xml中配置二级缓存
<cache ></cache>属性
eviction:缓存的回收策略
flushinterval:缓存刷新间隔
readonly:是否只读
size:缓存存放多少元素
type=“”指定自定义缓存的全类名 实现cache接口即可
3.Employee implements Serializable
和缓存有关的这只/属性
1.cacheEnable=true false:关闭缓存(二级 缓存关闭)(一级缓存一直可用)
2.每个select标签都有usercache=“true”
false:不适应缓存(一级缓存依然可用)(二级缓存关闭(全局不能false))
3.每个增删改标签的flushcache=“true” 增删改执行完成后就会清空缓存 一级二级都被清空
查询标签:默认 flushcache=“false” 如果flushcache=“true” 每次查询之后都会清空缓存,缓存没有被使用
4.sqlsession.clearcache()只清空当前session的一级缓存
5.localcachescope本地缓存作用域(一级缓存session) 当前会话的所有数据保存在会话缓存中
statement:可以禁用一级缓存