【mybatis-使用篇】

mybatis-使用篇

前言:本文依照视频学习做的总结,视频地址:b站_尚硅谷_MyBatis

一、基本使用

mybatis官方_入门

文件说明
全局配置文件:mybatis-config.xml
映射文件:XxxMapper.xml
接口:XxxMapper.java

类说明
SqlSessionFactory:SqlSession工厂,根据配置信息生成对应的SqlSession工厂,以生产SqlSession
SqlSession:底层与Connection一致,每次操作需要使用新的一个SqlSession进行,是线程不安全的

1、全局配置文件

mybatis-config.xml为全局配置文件,该文件是可选的,配置文件添加数据库连接信息,映射文件地址等
<mappers>应用于接口式编程,指定接口所在的位置

<?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>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="org/mybatis/example/BlogMapper.xml"/>
  </mappers>
</configuration>

2、映射文件

namespace(命名空间):可以随意指定或者在接口式编程中指定接口路径
id:sql语句的唯一标识,如果是在接口式编程中应该与接口对应的方法名称一致,达到绑定效果

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mybatis.example.BlogMapper">
  <select id="selectBlog" resultType="Blog">
    select * from Blog where id = #{id}
  </select>
</mapper>

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 元素的子元素中设置。例如:

<properties resource="org/mybatis/example/config.properties">
  <property name="username" value="dev_user"/>
  <property name="password" value="F2Fa3!33TYyg"/>
</properties>

设置好的属性可以在整个配置文件中用来替换需要动态配置的属性值。比如:

<dataSource type="POOLED">
  <property name="driver" value="${driver}"/>
  <property name="url" value="${url}"/>
  <property name="username" value="${username}"/>
  <property name="password" value="${password}"/>
</dataSource>

2、Setting

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
</settings>

3、typeAliases

typeAliases(类型别名)可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:

<typeAliases>
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
  <typeAlias alias="Comment" type="domain.blog.Comment"/>
  <typeAlias alias="Post" type="domain.blog.Post"/>
  <typeAlias alias="Section" type="domain.blog.Section"/>
  <typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>

当这样配置时,Blog 可以用在任何使用 domain.blog.Blog 的地方。

也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:

<typeAliases>
  <package name="domain.blog"/>
</typeAliases>

每一个在包 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 来阻止默认的关闭行为。
    例如:
<transactionManager type="MANAGED">
  <property name="closeConnection" value="false"/>
</transactionManager>

5、databaseIdProvider

数据库厂商标识(databaseIdProvider)

MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载带有匹配当前数据库 databaseId 属性和所有不带 databaseId 属性的语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。 为支持多厂商特性,只要像下面这样在 mybatis-config.xml 文件中加入 databaseIdProvider 即可:

<databaseIdProvider type="DB_VENDOR" />

databaseIdProvider 对应的 DB_VENDOR 实现会将 databaseId 设置为 DatabaseMetaData#getDatabaseProductName() 返回的字符串。 由于通常情况下这些字符串都非常长,而且相同产品的不同版本会返回不同的值,你可能想通过设置属性别名来使其变短:

<databaseIdProvider type="DB_VENDOR">
 <property name="SQL Server" value="sqlserver"/>
 <property name="DB2" value="db2"/>
 <property name="Oracle" value="oracle" />
</databaseIdProvider>

使用在映射文件上,表示在当前连接符合时调用,调用顺序,有指定优先默认没有指定。

   <select id="getSellById" resultType="com.example.mybatistest.bean.Sell" databaseId="mysql">
        select * from t_sell where id = #{id}
    </select>

6、mappers

映射器(mappers):找到映射文件,

  • resource:xml文件
  • class:接口类,需要xml命名与接口相同,并且编译时在同一包下
  • package:接口所在包,需要xml命名与接口相同,并且编译时在同一包下

IDEA中,class和packge情况需要resource和main具有同一的目录结果,编译时才能编译到相同的包下:运行错误修改

<!-- 使用相对于类路径的资源引用 -->
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
  <mapper url="file:///var/mappers/BlogMapper.xml"/>
  <mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
  <package name="org.mybatis.builder"/>
</mappers>

三、映射文件

mybatis官方_xml映射器

1、parameterType

指定传入参数类型,是可选的

2、增删改

  • <insert> 增加
  • <update> 修改
  • <delete> 删除

返回类型:
mybatis 允许增删改有返回值Integer/Long/Boolean/void包装类基本类
mybatis支持自增主键,自增主键的获取,也是利用statement.getGeneratedKeys()

  <insert id="" useGeneratedKeys="true" keyProperty="将键值返回的属性">
        
    </insert>

Oracle自增处理

    <insert id="" >
        keyProperty:查出的主键值封装给javabean的哪一个属性
        <selectKey keyProperty="" resultType="Integer" order="BEFORE">
            编写查询主键的sql语句
        </selectKey>
        xxx
    </insert>

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<Integer> 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<Integer,Employee>

// 1、在接口方法上声明key的属性
 @MapKey("id")    
Sell getSellById(int i);
// 2、resultType="employee"

7、自动映射

1、sql语句中将变量修改为符合javabean成员变量标识
2、全局配置mapUnderscoreToCamelCase=true
3、自定义resultMap

<!-- id是唯一标识,type是javabean类 -->
<resultMap id="" type="">
    <!-- 主键 colum是数据库列,property是类属性 -->
    <id colum="id" property="id"/>
    <!-- 普通列 colum是数据库列,property是类属性 -->
    <result column="last_name" property="lastName"/>
</resultMap>

8、 resultMap <association>

场景一:
Employee含有Department对象,查询Employee同时把Department查询赋值。

1、property=类.成员

<resultMap id="" type="">
    <id colum="id" property="id"/>
    <result column="last_name" property="lastName"/>
    <result column="dept_id" property="dept.id"/>
    <result column="dept_name" property="dept.deptName"/>
</resultMap>

2、使用简单的<association>

<!-- property指定哪个属性是联合对象,javaType指定联合对象是哪个类 -->
<association property="" javaType="">
    <id column="dept_id" property="id"/>
    <result column="dept_name" property="deptName"/>
</association>

3、升级的<association>(分步查询)

查询employee,然后根据得到的dept_id去dept表中查询department类赋值给当前employee。

<association property="" select="命名空间.方法" column="">
</association>

流程:使用select指定的方法(column指定参数的列值)执行select查出对象,封装到property指定的属性

延迟加载
在分步查询的基础上,当使用第二步的属性时才执行第二步查询
方法:
1、全局配置:分段查询的基础上加上两个配置lazyLoadingEnabled=trueaggressiveLazyLoading=false
2、当前生效:fetchType="lazy":表示使用延迟加载。lazy:延迟;eager:不延迟

9、 resultMap <collection>

场景二:
Department含有Employee列表,查询Department同时把Employee列表查询赋值。

1、简单的<collection>

<!-- property是成员变量,ofType是对象的类 -->
<collection property="" ofType="">
    ...
</collection>

2、升级的<collection>(分步查询)

<!-- 
  property:成员变量;
  select:第二步执行的查询sql;
  column:传入第二步执行sql的参数
 -->
<collection property="" select="" column="">
</collection>

延迟效果如同<association>

拓展:分步查询传递多参数
将多列的值封装成map传递:colunm="{key1=column1,key2=column2}"

10、 resultMap <discriminator >

鉴别器 - discriminator
可以使用discriminator判断某列的值,然后根据某列的值改变封装的行为


<!-- 
  column:指定判断的列
  javaType:列值对应的java类型
-->
<resultMap id="" type="">
  xxx
  <discriminator javaType="">
    <!-- resultType指定封装结果的类型,不能缺省-->
    <case value="0" resultType=""> xxx </case>
    <case value="1" resultType=""> xxx </case>
 </discriminator>
</resultMap>

11、动态sql

<if>
<if test=""></if>
test:判断表达式(OGNL)根据条件动态的预处理Sql语句

和:and;&& 请用转义字符
或:or;|| 不支持,请用转义字符
字符串:字符串单引号 ' '
OGNL会进行字符串与数字的转换判断'0'==0

拼装and问题
select * from where a = a and b = b

  • 1、给where后面加上1=1,以后的条件都是and xxx.
  • 2、使用<where>标签,只会去掉第一个多出来的and和or
  • 3、使用<trim>标签,
    prefix="" :前缀:trim标签体中时整个字符串拼串后端结果。prefix给拼串后的整个字符串加一个前缀
    prefixOverrides="":前缀覆盖:去掉整个字符串前面多余的字符
    suffix="":后缀:给拼串后的整个字符串加一个后缀
    suffixOverrides="":前缀覆盖:去掉整个字符串前面多余的字符

<choose>
按条件进入,类似于带break的switch-cash

<choose>
    <when test="id!=null">
        id=#{id}
    </when>
    <when test="lastName!=null">
        last_name like #{lastName}
    </when>
    <otherwise>
        xxx
    </otherwise>
</choose>

<set>
类似于<where>,但是用于更新操作以减少多出来的“,”

<foreach>
用于查询多个返回 where id in (1,2,3);用于批量保存

<foreach collection="ids" item="item_id" separator="," open="(" close=")">
     #{item_id}
</foreach>

<bind>
可以将OGNL表达式的值绑定到一个变量中,方便后来应用这个值
<bind name="_lastName" value="'%'+lastName+'%'"/>

<sql>
抽取可重用的sql片段,方便后面引用
1、进程简要查询的列名,或者插入用的列名抽取出来方便引用
2、<inclue>拿出引用,在<inclue>中可以另外声明变量,<sql>中使用${}取值

<sql id="">
</sql>

<include refid="">
    <property name="" value=""/>
</inclue>

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存放在自己对应的缓存;

效果:数据会从二级缓存中获取,查出的数据都会被默认先放在一级缓存中;只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中

image.png

使用:

  • 1)开启全局配置:cacheEnabled=true
  • 2)mapper.xml中配置使用二级缓存
/**
eviction:缓存回收策略;
    LRU - 最近最少使用的:移除最长时间不被使用的对象
    FIFO - 先进先出:按对象进入缓存的顺序移除
    SOFT  - 软引用:移除基于垃圾回收器状态和软引用规则的对象
    WEAK - 弱引用:更积极地移除基于垃圾回收器状态和弱引用规则的对象
    默认是LRU
flushInterval:缓存刷新间隔
    缓存多长时间清空一次,默认不清空,设置一个毫秒值
readOnly:是否只读
    true:只读;mybatis认为用户不会修改数据。为了加快获取数据,直接会将数据在缓存中的引用交给用户
    false:非只读:mybatis认为用户可能修改数据。mybatis会利用序列号&反序列化的技术克隆数据。安全,速度慢。默认。
size:缓存存放多少个元素
type:指定自定义缓存的全类名;实现Cache接口即可
*/
<cache eviction=""></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中使用自定义缓存
    <cache type=""></cache>
image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351