MyBatis?
archetypeCatalog = internal
本文档单独出现的_parameter都标识为变量名
一.三个基本要素:
核心接口和类
MyBatis 核心配置文件
SQL映射文件
二.核心接口和类:
结构图:
(1)每个MyBatis的哟ing有都以一个SqlSessionFactory对象的实例为核心
(2)首先获取SqlSessionFactoryBuilder对象,可以根据XML配置文件或Configuration类的实例构建该对象
(3)然后获取SqlSessionFactory对象,该对象实例可以通过SqlSessionFactoryBuilder对象来获得
(4)有了SqlSessionFactory对象之后,就可以进而获取SqlSession实例,SqlSession对象中完全包含以数据库为背景的所有执行SQL操作方法。可以用该实例来直接执行已映射的SQL语句
1. SqlSessionFactoryBuilder
作用:
构建SqlSessionFactory
Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); SqlSessionFactory sfb = new SqlSessionFactoryBuilder().build(reader);
特点:
用过即丢
声明周期和作用域:
SqlSessionFactoryBuilder是利用XML或者Java编码获得资源来购进啊SqlSessionFactory的,通过它可以构建多个SessionFactory。它的作用就是一个构建器,一旦我们构建了SqlSessionFactory,它的作用就已经完结,失去了存在的意义,这时我们就应该毫不犹豫的废弃它,将它回收。所有它的生命周期只存在与方法的局部,它的作用就是生成SqlSessionFactory对象
2. SqlSessionFactory
作用:
创建SqlSession实例
SqlSession sqlSession = sfb.openSession();
生命周期和作用域
SqlSessionFactory对象一旦创建,就会在整个应用过程中始终存在。没有理由去销毁或再创建它,并且应用运行中也不建议多次创建实例,因此,最佳作用域是Application
创建SqlSessionFactory实例时一般放在静态代码块中
而,最佳方案是使用依赖注入---Spring框架来管理SqlSessionFactory的单例生命周期
3. SqlSession
作用:
是用于执行持久化操作的对象,类似于JDBC终端Connection。提供了面向数据库执行SQL命令所需的所有方法,可以通过SqlSession实例直接运行已映射的SQL语句
生命周期和作用域:
一个SqlSession对象对应着一次会话。非永久,用完即关,及时释放资源(close()方法)
线程方面:
每个线程都有自己的SqlSession实例,SqlSession实例不能被共享,也不是线程安全的。因此最佳的作用域范围是request作用域或者方法体作用域
两种使用方式:
1. 通过SqlSession实例来直接执行已映射的SQL语句
(1) 添加xx.xml 文件 SQL映射节点
(2) 根据需求利用SqlSession实例直接点出需要使用的方法,(方法传参SQL映射节点的id值)
2. 基于mapper接口方式操作数据
(1) 添加xx.xml 文件 操作SQL节点
(2) 创建绑定映射语句的接口xxx.java 并提供---> 和SQL映射节点id值相同的接口方法
(3) 利用SqlSession实例调用getMapper()方法 ,传参为接口名.class 然后点出相应接口方法
注意:SqlSession实例用完即关
三.核心配置文件:
Mybatis-config.xml
文档结构:
注意先后顺序!!!
Configuration 配置 (根节点)
Properties 可以配置在Java属性配置文件中
Settings 修改MyBatis在运行时的行为方式
TypeAliases 为Java类型命名一个别名
TypeHandlers 类型处理器
ObjectFactory 对象工厂
Plugins 插件
Environments 环境
Environment 环境变量
transactionManager 事物处理器
dataSource 数据源
Mappers 映射器
1. configuration元素:
整个xml文件的根节点,相当于MyBatis的总管,所有配置信息都会存放在它里面,MyBatis还提供了设置这些配置信息的方法:
(1) 可以从配置文件李获取属性值
(2) 通过程序直接设置
2. Properties元素:
其描述都是外部化,可替代的属性
属性获取方式:
1. 引入外部properties文件
<properties resource=”数据源路径” />
引入外部文件后,在本xml文件中可直接使用${key值}来获取对应的value值
2. 内部配置 --->元素中直接配置property属性
<properties> <property name=”key值” value=”value值” /> ...... </properties>
配置节点完成后,在本xml文件中可直接使用${key值}来获取对应的value值
3. 内外部结合使用
<properties resource=”外部数据源路径”> <property name=”key值” value=”value值” /> ...... </properties>
配置节点完成后,在本xml文件中可直接使用${key值}来获取对应的value值
如果外部配置和内部配置 配置了相同的key 那么:resource属性值的优先级高于property子节点配置的值
3. Settings元素
作用是设置一些非常重要的设置选项,用于设置和改变MyBatis运行中的行为
设置项 |
描述 |
允许值 |
默认值 |
cacheEnabled |
对在此配置文件下的所有cache进行全局性开/关设置 |
True|false |
True |
lazyLoadingEnabled |
全局性设置懒加载。如果设为false,则所有相关联的都会被初始化加载 |
True|false |
True |
autoMappingBehavior |
MyBatis对于resultMap自动映射的匹配级别 |
NONE|PARTIAL|FULL |
PARTIAL |
Settings标签配置的是配置MyBatis框架运行时的一些行为,例如:
(1)缓存
(2)延迟加载
(3)结果集控制
(4)执行器
(5)分页设置
(6)命名规则
等一系列控制性参数,其所有的settings配置都放在父标签settings标签中
4. typeAliases元素
作用是配置类型别名,通过与MyBatis的SQL映射文件相关联,减少输入多余的完整类名
两种方式:
1. 直接指定到类
<typeAliases> <typeAlias alias=”别名” type=”类的全路径” /> ...... </typeAliases>
弊端:
如果一个项目有多个POJO的时候需要一一配置
2. 指定包,检索类
解决上面直接指定到类的弊端
通过package的name属性直接指定包名,MyBatis会自动扫描指定包下的JavaBean,并默认设置一个别民,默认名称为JavaBean的非限定类名
<typeAliases> <typeAlias alias=”别名” type=”某包的全路径” /> </typeAliases>
Xx.xml配置:
直接写包下的某个类名即可
另外:
对于基础数据类型等,Mybatis已经为许多常见的Java类型内建了相应的类型别名,一般都是与其映射类型一致,并且它们都是大小写不敏感的
5. Environments元素:
MyBatis可以配置多套运行环境,如:
(1)开发环境
(2)测试环境
(3)生成环境
等,我们可以灵活选择不同的配置,从而将SQL映射应用到不同的数据库环境上。
这些不同的运行环境,就可以听过environments元素来配置,但是不管增加几套运行环境,都必须要明确选择出当前的唯一一个运行环境。
Because:
每个数据库都是对应一个SqlSessonFactory实例,需要指明哪个运行环境将被创建,并把运行环境中设置的参数传递给SqlSessionFactoryBuilder。
<environments default="development"> <!-- 开发环境 --> <environment id="development"> <transactionManager type="JDBC"> <property name="" value=""/> </transactionManager> <dataSource type="UNPOOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${user}"/> <property name="password" value="${pwd}"/> </dataSource> </environment> <!-- 测试环境 --> <environment id="test"> <transactionManager type=""></transactionManager> <dataSource type=""></dataSource> </environment> </environments>
几个关键点:
(1)默认的运行ID
通过environments节点的default属性来指定当前的运行环境ID,对于子节点environment环境ID的命名要确保唯一性
(2)TransactionManager事物管理器
设置其类型为JDBC(MyBatis有两种事物管理类型,即JDBC,MANAGED),直接使用JDBC的提交和回滚功能,依赖于数据源获得连接来管理事物的声明周期
(3)配置数据库连接对象
DataSource元素使用标准的JDBC数据源接口来配置JDBC连接对象的资源。
MyBatis提供了三种数据源类型
(1)UNPOOLED
(2)POOLED
(3)JNDI
一般使用POOLED数据源类型。该类型的实现利用“池”的概念将JDBC连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间,是MyBatis实现的简单的数据库连接池类型,它使数据库的连接可被复用,不必在每次请求时都去创建一个物理连接
对于高并发的Web应用是一种流行的处理方式,有利于快速响应请求
6. Mappers元素:
映射器,用来定义SQL的映射语句,我们只需要告诉MyBatis去哪里找到这些SQL语句即可,即去哪里找相应的SQL映射文件(可以使用类资源或者URL资源等方式)
仅只是找到指定映射文件,其更详细的信息配置在每个SQL映射文件中
常用的两种映射方式:
1. 使用类资源路径获取路径
<mappers> <mapper resource="本项目中SQL映射文件 资源路径"/> </mappers>
2. 使用URL获取资源
<mappers> <mapper url="本地或者网络上的SQL映射文件 资源路径"/> </mappers>
四.DTD文件的引入:
用IDEA 不需要手动引入DTD文件 略略略!!!
五.SQL映射文件:
MyBatis强大之处:SQL映射语句 魅力所在 非常简单
使用SQL映射文件配置可减少50%以上的代码量
MyBatis专注于SQL 极大限度地进行SQL调优,保证性能
几个顶级元素配置:
1. mapper:
映射文件的根元素节点,只有一个属性namespace(命名空间)
namespace作用:
1. 区分不同的mapper,全局唯一
2. 保定DAO接口,即面向接口。
① 当namespace绑定一个接口后,可以不用写该接口的实现类,MyBatis会通过接口的完整限定名查找到对应的mapper配置来执行SQL语句。因此namespace的命名必须要跟接口同名(包结构+接口名)
namespace命名要求:
1. 命名必须跟某个DAO接口命名,同属于DAO层,故代码结构上,映射文件与该DAO接口放置在同一package下,如果不放在同一package下,需指定其对应接口的资源路径,,接口命名,配置文件命名习惯上以Mapper结尾
2. 在不同的mapper文件中,子元素的id可以相同,MyBatis通过namespace和子元素的id进行联合区分。接口中的方法与映射文件中SQL语句id应一一对应
2. chche:
配置给定命名空间的缓存
3. cache-ref:
从其他命名空间引用的缓存配置
4. resultMap:
用来描述数据库结果集和对象的对应关系
5. Sql:
通过sql片段达到代码重复利用
可以重用的SQL块,也可以被其他语句使用
我一般用来封装常用的表字段
如:
<sql id="co"> `SUBWAYNAME`,`STARTSTATION`,`ENDSTATION`,`STATIONNUM`,`STARTIME`,`PRICE` </sql>
使用时:
在标签内插入语句
<include refid="co"/>
6. Insert
映射插入语句
<insert id="add" parameterType="com.metro.entity.SubwayInfo"> INSERT INTO `subwayInfo`( <include refid="co"/> ) VALUES (#{subwayName},#{startStation},#{endStation},#{stationNum},#{starTime},#{price}) </insert>
7. Update:
映射更新语句
<update id="up" parameterType="com.metro.entity.SubwayInfo"> UPDATE `subwayInfo` <set> <if test="subwayName!=null and "".equals(subwayName.trim())">SUBWAYNAME=#{subwayName},</if> <if test="startStation!=null and "".equals(startStation.trim())">STARTSTATION=#{startStation},</if> </set> WHERE id=#{id} </update>
8. Delete:
映射删除语句
<delete id="del" parameterType="INTEGER"> DELETE from subwayInfo where id=#{id} </delete>
9. Select:
映射查询语句
<select id="listAll" resultMap="subwayInfo"> SELECT id,<include refid="co"/> FROM subwayInfo </select>
别名与Java类型映射:
别名 |
映射类型 |
string |
String |
byte |
Byte |
long |
Long |
short |
Short |
int |
Integer |
integer |
Integer |
arraylist |
Arraylist |
double |
Double |
float |
Float |
boolean |
Boolean |
date |
Date |
map |
Map |
hashmap |
HashMap |
list |
List |
SQL映射标签详解:
1. resultMap
映射查询结果,两种方案:
1)使用resultType做自动映射
<!--resultType 大小写不敏感:不需resultMap标签映射,,依靠实体类字段名与数据库相应字段名相同进行映射
严重受限
-->
字段名与POJO的属性名必须一致。若不一致,则需要给字段起别名,保证别名与属性名一致
不推荐使用
2)通过resultMap来映射自定义结果
使用resultMap标签做自定义结果映射,字段名可以不一致,并且还可以指定要显示的列,比较灵活,应用广泛
<resultMap type="com.metro.entity.SubwayInfo" id="subwayInfo"> <!--typeHandler="" 可做数据类型转换--> <!--column="" 数据库字段property=”” 实体类字段--> <id column="ID" jdbcType="INTEGER" property="id" /> <result column="SUBWAYNAME" jdbcType="VARCHAR" property="subwayName"/> <result column="STARTSTATION" jdbcType="VARCHAR" property="startStation"/> <result column="ENDSTATION" jdbcType="VARCHAR" property="endStation"/> <result column="STATIONNUM" jdbcType="INTEGER" property="stationNum"/> <result column="STARTIME" jdbcType="VARCHAR" property="starTime"/> <result column="PRICE" jdbcType="INTEGER" property="price"/> </resultMap>
jdbcType:插入空值时,需要指定jdbcType
Mybatis insert空值报空值异常,但是pl/sql不会提示错误,主要原因是mybayis无法进行转换
各属性标签:
Id:唯一标识,此id用于sql映射元素 resultMap属性的引用
Type:表示该resultMap的映射结果类型
result子节点:用于标识一些简单属性
返回类型resultType 与 resultMap?
这里的resultType 与 resultMap跟上面所说的那个不一样,,这里探讨的是Sql映射标签 返回类型
1. resultType:
直接表示返回类型,包括基础数据类型和复杂数据类型
2. resultMap:
对外部resultMap定义的引用,对应外部resultMap的id,表示返回的结果集映射到哪一个resultMap上。应用场景:数据库字段信息与对象属性不一致或者需要做复杂的联合查询以便自由控制映射结果
3. resultType和resultMap的关联
无论是resultType还是resultMap,其实查询出来的每个字段值都放在一个对应的Map里面,其键是字段名,值是其对应的值
然后呢,当select元素提供的返回值类型是resultType的时候,MuBatis会将Map里面的键值对取出赋给resultType所指定的对象对应的属性(即调用对应的对象里的属性的setter方法进行填充)
··所以呢,其实每个查询的返回类型都是resultMap
1)只是当我们提供的返回值类型属性是resultType 的时候,会自动把对应的赋值resultType 所指定对象的属性
2)我们提供的返回类型是resultMap的时候,因为Map不能很好的表示领域模型,我们就需要通过进一步的定义把它转化为对应的实体对象
开发经验:
当返回类型是resultMap时,主要用在进行复杂联合查询上
进行简单查询时,使用resultType 足以
注意:在select元素中,resultType 和resultMap本质上是一样的,都是Map数据结构
明确一点。。。两种绝对不能同时存在,只能二者选其一使用
4. resultMap的自动映射级别
resultMap自动映射级别默认的是PARTIAL,为自动装配,他会自动装配所有查询出来并且实体类里拥有的字段
如果想要选择部分字段进行映射,希望没有映射的字段是不能在后台查询并输出的,则需要在核心配置文件settings中设置resultMap的自动映射级别(autoMappingBehavior)为NONE,即禁止自动匹配
<settings> <setting name="autoMappingBehavior" value="NONE"/> </settings>
注意点:
在MyBatis中,使用resultMap能够进行自动映射匹配的前提是字段名和属性名需要一致,在默认映射级别(PARTIAL)情况下:
1)若一致,即使没有做属性名和字段名的匹配映射,也可以在后台获取到未匹配过的属性值
2)若不一致,且在resultMap里没有做映射,那么就无法在后台获取并输出
2. Select
属性:
1)Id
命名空间中唯一的标识符,可以被用来引用这条语句
由于我们常用的映射方法是基于Mapper接口,所有id值需跟对应的接口方法名一致
2)ParameterType:
标识查询语句传入参数的类型的完全限定名或别名。
支持继承数据类型和复杂数据类型
MyBatis有内建的类型别名,,上面有!!!
除了内建的类型别名外,还可以为自定义的类设置别名。。。在核心配置文件的typeAliases元素中设置
在映射文件中可直接使用别名,以减少配置文件的代码量
注:
1. 基本情况下只允许传入一个参数,如果要传入多个参数,,就要对多个参数进行封装后续还可以使用@Param注解实现多参数入参
2. Mybatis传入参数类型可以是Java继承数据类型,但是只适用于一个参数的情况,通过#{参数名}即可获取传入的值。若是多参数入参,需要复杂数据类型来支持,包括Java实体类,map,通过#{属性名}或者#{Map的key}来获取传入的参数值
#{值}:写法是OGNL表达式
如果是实体类对象和基本数据类型多参数传参
取值时:实体类对象.属性名
3)ResultType:
查询语句返回结果类型的完全限定名或别名,命名与ParameterType大体一致
3. Insert,Update,Delete
属性:
1)id:
与select元素的id一样,是命名空间中唯一的标识符,可以呗用来引用这条语句
2)ParameterType:
与select元素其属性一样,是传入参数的类型的完全限定名或别名
注意点:
对于增删改(insert,update,delete)这类数据库更新操作
1)该类型的操作本身默认返回执行SQL影响的行数,所以DAO层的接口方法的返回值一般设置为int类型。最好不要返回boolean类型
2)Insert,update,delete元素中均没有resultType属性,只有查询操作需要对返回结果类型(resultType/resultMap)进行相应的指定
3)在测试类中,当sqlSession执行完sql操作之后,需要进行commit,完成数据的插入操作。若在执行的过程中抛了异常,那么就必须在catch中进行回滚,以此来保证数据的一致性,同时设置count为0
六.使用@param注解实现多参数入参
int del(@Param("uid")Integer id,@Param("xxx")String x);
解析:
使用注解@Param来传入多个参数,并且注解内可以将变量名重命名为其他名字,,只会影响SQL映射文件的使用,不会影响java类中的真实使用。。在映射文件中使用#{注解内的值}来获取其传入的值,,,
还有啊,使用注解,装配的入参,参数类型必须是引用类型(复杂类型或String,或者基本数据类型的包装类,对象等)使用int等值值类型,会报错
如果是实体类对象和基本数据类型多参数传参
取值时:
实体类:#{注解内的值.属性名}
基本:#{名}
扩展:
使用多参数入参,必须使用@Param注解方式,若不使用,则会报错,报错信息类似于Pararmeter‘参数名’ not found。
Reason:
深入MyBatis源码,我们可以发现,MyBatis的参数类型为Map
1)若使用@Param注解参数,那么就会记录指定的参数名为key
2)若参数前没有加@Param,那么就会使用“param”+它的序号座位Map的key。
故而进行多参数入参时,若没有使用@Param指定参数key,那么在映射的SQL语句中获取不到#{参数名},从而报错
经验:
相信大家学了@Param注解入参就会有疑惑,既然有高级货,为什么不早拿出来???
其实呢,普通入参方式也不是一无是处,下面我们来谈论一下《在MyBatis中参数入参,何时需要封装成对象入参,何时又需要使用多参数入参???》
来,let’s go :
1)一般情况下呢,超过4个参数最好封装成对象入参(特别是在常规的增加和修改操作时,字段较多,封装成对象比较方便,也省的一个个记参数名了,也不是)
2)对于参数固定的业务方法呢,最好使用多参数入参,原因是这种方法比较灵活,代码的可读性高,可以清晰地看出接口方法中所需的参数是什么。并且对于固定的接口方法,参数一般是固定的,所以直接多参数入参即可,无需封装对象(比如修改个人密码,根据用户id删除用户,根据用户id查看用户明细,都可以采用这种方式)
需要注意的是:
当参数为基础数据类型时,不管是多参数入参还是单独一个参数,都需要使用@Param注解来进行参数的传递
还有一定记住一点。。使用多参数入参时。在SQL映射文件中使用OGNL表达式获取传开的值的时候 其key值是@Param()括号中的值
七.ResultMap实现高级结果映射
我们先回顾一下前面提到的resultMap的基本配置
基本配置:
1> 属性
1)id:resultMap的唯一标识
2)Type:标识该resultMap的映射结果类型(通常时Java实体类)
2> 字节点
1)id:一般对应数据库中该行的主键id,设置此项可以提升MyBatis性能
2)Result:映射到JavaBean的某个“简单类型”属性,如:基础数据类型,包装类等
子节点id和result均可实现最基本的结果集映射,将列映射到简单数据类型的属性。
这两者唯一不同的是:在比较对象实例时id将作为结果集的标识属性。有助于提高整体性能,特别是应用缓存和潜逃结果映射的时候
高级结果映射,两个配置项:
1> Association
映射到JavaBean的某个“复杂类型”属性,比如JavaBean类,即JavaBean内部嵌套一个复杂数据类型(JavaBean)属性,这种情况就属于复杂类型的关联。
需要注意的是:
Association仅处理一对一的关联关系(比如,每个用户都会有一个角色,两表关系一对一)
<resultMap type="com.metro.entity.User" id="user"> <id column="ID" jdbcType="INTEGER" property="id" /> <result column="NAME" jdbcType="VARCHAR" property="name"/> <result column="PWD" jdbcType="VARCHAR" property="pwd"/> <association property="role" javaType="Roel"> <id property="rid" column="RID"/> <result property="rname" column="RNAME"/> </association> </resultMap> <select id="listAll" resultMap="user" parameterType="Integer"> SELECT u.* r.id AS RID,r.name AS RNAME FROM user u,role r WHERE u.rid=r.id AND u.id=#{uid} </select>
分析其属性:
Association元素:
1> javaType:
完整Java类名或者别名。
1)若映射到一个JavaBean,则MyBatis通常会自行检测到其类型;
2)若映射到一个HashMap,则应该明确指定JavaType,来确保所需行为。此处为Role
2> Property:
映射数据库列的实体对象的属性。此处为在User里定义的属性role
其子节点元素:
1> Id:
不多说,看下面
2> Result:
不多说,看下面
上两者共同属性:
1)property:
映射数据库列的实体对象的属性,上面的例子为Role的属性
2)colunm:
数据库对象的列名或别名
注意:
Id子元素在嵌套结果映射中扮演了一个非常重要的角色,应该指定一个或者多个属性来唯一标识这个结果集。实际上,即使没有值得id,MyBatis也会工作,但是会导致严重的性能开销,所以最好选择尽量少的属性来唯一标识结果,主键或者联合主键均可
最后:
了解了关于association的基本用法以及使用场景,一些复用大佬,就会想到这个东东可不可以复用呢,,
答案是肯定的,association提供了另一个属性:resultMap。(相信说到这里大家已经指定怎么做了,每次就是跟select等映射标签一样,,搞呗,resultMap=“某resultMap元素id值”)通过这个属性可以扩展一个resultMap来进行联合映射,这样就可以使其结果映射重复使用。当然,若不需要复用,也可安装之前的写法,直接嵌套这个联合结果映射,根据具体业务而定
<resultMap type="com.metro.entity.User" id="user"> <id column="ID" jdbcType="INTEGER" property="id" /> <result column="NAME" jdbcType="VARCHAR" property="name"/> <result column="PWD" jdbcType="VARCHAR" property="pwd"/> <association property="role" javaType="Roel" resultMap="role"/> </resultMap> <resultMap id="role" type="Role"> <id property="rid" column="RID"/> <result property="rname" column="RNAME"/> </resultMap>
引用外部resultMap的好处:可以达到复用的效果,并且整体的结构较为清晰,特别适合association的结果映射比较多的情况
返回对象
Association可以处理一对一的关联关系,那么对于一对多的关联关系的处理,它就心有余而力不足了,,,,,怎么办呢?嗯,用collection元素
2> Collection
作用和association元素的作用差不多一样,事实上,它们非常类似,也是映射到JavaBean的某个“复杂类型”属性,只不过这个属性是一个集合列表,即JavaBean内部嵌套一个复杂数据类型(集合)属性。和使用association元素一样,我们使用嵌套查询,或者从连接中嵌套结果集
<resultMap id="role" type="Role"> <id property="rid" column="RID"/> <result property="rname" column="RNAME"/> <collection property="juris" ofType="Juris" resultMap="ju"/> </resultMap> <resultMap id="ju" type="Juris"> <id property="jid" column="JID"/> <result property="jname" column="JNAME"/> </resultMap>
与association用法基本一致,就是多出来了一个ofType属性
ofType属性呢:代表的是Java类名或者别名,即集合所包含的类型
返回集合
八.ResultMap自动映射级别和MyBatis缓存
(一)resultMap自动映射级别
resultMap自动映射的三个匹配级别:
1> NONE:
禁止自动匹配
2> PARTIAL:
默认自动映射级别,自动匹配所以属性,有内部嵌套的(association、collection)的除外
3> FULL:
自动匹配所有,所有所有!!!
详解:
用法:
在核心配置中配置:
<settings> <setting name="autoMappingBehavior" value="匹配级别"/> </settings>
NONE禁止自动匹配不用多说,就是不自动匹配嘛,手动映射啥就匹配啥并且赋值,否则没值,并且上面也举过例子,所有不再做过多阐述
主要讲解PARTIAL与FULL的区别
可以私下里做个例子,发现没有设置autoMappingBehavior的时候,也就是默认情况下(PARTIAL),若是普通数据类型的属性,会自动匹配所有,,,,,但是呢若是内部嵌套(association或collection)(集合或实体对象),那么输出结果就是null。也就是说它不会自动匹配,除非手工设置autoMappingBehavior的value为FULL(自动匹配所有)
修改后你会发现autoMappingBehavior的value为FULL(自动匹配所有)之后,未作映射的字段(任何类型)均有值输出
(二)MyBatis缓存
如大多数持久化框架一样,MyBatis提供了一级缓存和二级缓存的支持
一级缓存缓存的是SQL语句,二级缓存缓存的是结果对象
1. 一级缓存
一级缓存是基于PerpetualCache(MyBatis自带)的HashMap本地缓存,作用范围为session域内,当session flush或者close之后,该session中所有的cache就会清空
配置:
因为MyBatis默认就是一级缓存,所以删除所有关于缓存的配置,就是一级缓存
2. 二级缓存
二级缓存就是global caching,它超出session范围之外,可以被所有的SqlSession共享,开启它只需要在MyBatis的核心文件settings中设置即可
三种配置方法:
为cache赋值:true为二级缓存。false为一级缓存
1)MyBatis的全局cache配置
在核心文件配置:
<settings> <setting name="cacheEnabled" value="false"/> </settings>
2)针对mapper的namespace
1)在mapper文件(也就是SQL映射文件)中设置缓存,默认情况下是没有开启缓存
2)需要注意的是,global caching发作用域是针对mapper的namespace而言的,即只有namespace内的查询才能共享这个cache
<mapper namespace="SubwayInfo"> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> 。。。。 </mapper>
代表创建了一个LRU缓存,并每隔60秒刷新,最大存储512个对象,而且返回的对象被认为是只读的。
evicition收回策略,默认是LRU
(1)LRU最近最少使用策略,一处做长时间不被使用的对象。
(2)FIFO先进先出策略,按对象进入缓存的顺序来移除它们。
(3)SOFT软引用策略,移除基于垃圾回收器状态和软引用规则的对象。
(4)WEAK弱引用策略,更积极地移除基于垃圾收集器状态和弱引用规则的对象。
3)针对个别查询进行调整
配置这个的前提是必须配置好上面2)针对mapper的namespace
如果需要对个别查询进行调整,可以单独设置cache:
<select id="listAll" resultMap="subwayInfo" useCache="true"> 。。。 </select>
详解一二级缓存的区别:
一级缓存基于sqlSession默认开启,在操作数据库时需要构造SqlSession对象,在对象中有一个HashMap用于存储缓存数据。不同的SqlSession之间的缓存数据区域是互相不影响的。
一级缓存的作用域是SqlSession范围的,当在同一个sqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据库中查询的数据写到缓存(内存),
第二次查询时会从缓存中获取数据,不再去底层数据库查询,从而提高查询效率。
需要注意的是,如果SqlSession执行了DML操作(增删改),并且提交到数据库,MyBatis则会清空SqlSession中的一级缓存,这样做的目的是为了保证缓存中存储的是最新的信息,避免出现脏读现象。
当一个SqlSession结束后该SqlSession中的一级缓存也就不存在了。
关闭一级缓存后,再次访问,需要再次获取一级缓存,然后才能查找数据,否则会抛出异常。
二级缓存是mapper级别的缓存。使用二级缓存时,多个SqlSession使用同一个Mapper的sql语句去操作数据库,得到的数据会存在二级缓存区域,它同样是使用HashMap进行数据存储。相比一级缓存SqlSession,二级缓存的范围更大,多个Sqlsession可以共用二级缓存,二级缓存是跨SqlSession的。
二级缓存的作用域是mapper的同一个namespace。不同的sqlSession两次执行相同的namespace下的sql语句,且向sql中传递的参数也相同,即最终执行相同的sql语句,则第一次执行完毕会将数据库中查询的数据写到缓存,第二次查询会从缓存中获取数据,不再去底层数据库查询,从而提高效率。
最后:
对于MyBatis缓存的内容仅做了解即可,因为面对一定的数据量,内置的Cache方式就派不上用场了
并且对查询结果集做缓存并不是MyBatis框架擅长的,它专心做的应该是SQL映射,所有我们一般采用 OSCache、Memcached等专门的缓存服务器来做更为合理
九.动态SQL咯
很简单。。也就是标签元素,,没什么技术含量
记住拿过来用就好、
各标签元素
为了支持用户多样化操作
1. If
<if test="subwayName!=null and "".equals(subwayName.trim())">AND SUBWAYNAME=#{subwayName}</if>
"" 代表空字符串 某些原因,“”不可用
test内 的返回结果为true走标签内部结构
否则不进,注意,这里面没有else标签,要想实现if-else结构标签,后面有新引入的标签,这里不做详细讲解,请继续去阅读
2. Where
可以智能 的处理where和and所处的位置
因为我们要动态拼接,where语法:where xxx=x and yyy=y
有时呢,我们拼接sql的时候where子句不好拼接,因为我们确定不了到底哪里是第一个条件的开头,where到底拼接到哪个语句前面,,所以就诞生了这个where标签:
<select id="listAll" resultMap="subwayInfo" > SELECT id,<include refid="co"/> FROM subwayInfo <where> <if test="subwayName!=null and "".equals(subwayName.trim())">AND SUBWAYNAME=#{subwayName}</if> <if test="startStation!=null and "".equals(startStation.trim())">AND STARTSTATION=#{startStation},</if> </where> </select>
在这里我们会发现sql因为有两个if条件不清楚到底会符合一条还是两条,或者说都不符合,where将要放的位置会异常尴尬
然后呢,可有看到我们这个例子的sql我并没有写where 为什么呢?因为无论执行的怎样我们的where都会为什么自动在最前面拼上where 子句,,如果呢,后面标签内都不符合那么where 不会拼接
做的很牛逼!!!
3. Choose
这里,,,choose,就是上面所说的可以实现if-else结构的标签,,没有久等吧,嘿嘿嘿
Choose标签呢,他又有两个子标签来配合他实现结构功能分别是:
When
If语句或者else if 语句
Otherwise
Else语句
<choose> <when test="">xx</when> <when test="">yy</when> <otherwise>zz</otherwise> </choose>
比如上面的语句
最后只会出现一个值, xx或者yy或者zz
第一个when就充当了if()
第二个when就充当了else if()
就算后面更多when也充当的是else if()语句 (test为true输出,但when只会执行一道,靠前者优先)
有人疑惑了,那最后的else呢?
Ok ,,,当然是之前没有出场的otherwise标签了,(所以条件不满足的时候走)
学过选择结构,不傻 的相信都 一点就懂,不多说
4. Set
Set元素主要用于更新操作,它的主要功能和where元素差不多,主要在包含的语句前输出宇哥set,若包含的语句是以逗号结束的,会自动把改逗号忽略掉,再配合id元素就可以动态地更新需要修改的字段;而不需修改的字段,则可以不再更新(因为有的时候在update操作中使用多个if或者别的选择标签,若一部分没有执行,则导致在语句末尾残留多余的逗号,解决此问题)
Tip:
<update id="up" parameterType="com.metro.entity.SubwayInfo"> UPDATE `subwayInfo` <set> <if test="subwayName!=null and "".equals(subwayName.trim())"> SUBWAYNAME=#{subwayName}, </if> <if test="startStation!=null and "".equals(startStation.trim())"> STARTSTATION=#{startStation}, </if> </set> WHERE id=#{id} </update>
5. Trim
更为灵活的元素,,,可以替代之前的
Trim元素也会自动识别其标签内是否有返回值,若有返回值,会在自己包含的内容前加上某些前缀(prefix),也可在其后加上某些后缀(suffix),也可把包含内容的首部某些内存覆盖(忽略)(prefixOverrides),或者把尾部的某些内容覆盖(suffixOverrides),无可厚非的强大,可以利用trim替代where或者set元素,实现相同效果
小Tip:
<trim prefix="where" prefixOverrides="and | or"> <if Test=””> and xx = x </if> 。。。 </trim>
不会报错,,前面的and会自动消除,,贼智能,,后缀处理同理
再解释一个各属性:
1> Prefix:
前缀,作用是通过自动识别是否有返回值后,在trim包含的内容上加上前缀,一般用于where
2> Suffix:
后缀,作用是在trim包含的内容上加上后缀
3> prefixOverrides:
对于trim包含内容的首部进行指定内容(如where子句中的and和or)的忽略。
4> suffixOverrides:
对于trim包含内容的首尾部进行指定内容的忽略。
6. Foreach
当MyBatis入参为数组类型的时候,就需要使用foeach来进行迭代取值了
Foreach呢,主要用于构建in条件中,他可以在sql语句中迭代一个集合,,主要属性有:
1)Item:
表示集合中每个元素进行迭代时的别名
2)Index:
指定一个名称,用于表示在迭代过程中,每次迭代到的位置
3)Close:
表示该语句以什么结束(这里以in条件语句举例,,所以用“)”结束,具体根据需求而定)
4)Open:
表示该语句以什么开始(这里以in条件语句举例,,所以用“(”开始,具体根据需求而定)
5)Separator
表示在每次进行迭代之间以什么符号作为分隔符(这里以in条件语句举例,,,所以用“,”分隔)
6)Collection:
最关键并最容易出错的属性,需要格外注意,改属性必须指定 不同情况下,该属性的值是不一样的。注意有三种情况:
a. 若入参为单参数且参数类型是一个List的时候,collection的属性值为list
b. 若从入参为单参数且参数类型是一个数组的时候,collection属性值为array
c. 若传入参数为多参数,就需要把它们封装为一个Map进行处理
最后来个关于collection三种情况的例子吧:
1.单参数List的类型:
<select id="dynamicForeachTest" parameterType="java.util.List" resultType="Blog"> select * from t_blog where id in <foreach collection="list" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> </select>
上述collection的值为list,对应的Mapper是这样的
public List dynamicForeachTest(List ids);
测试代码:
@Test public void dynamicForeachTest() { SqlSession session = Util.getSqlSessionFactory().openSession(); BlogMapper blogMapper = session.getMapper(BlogMapper.class); List ids = new ArrayList(); ids.add(1); ids.add(3); ids.add(6); List blogs = blogMapper.dynamicForeachTest(ids); for (Blog blog : blogs) System.out.println(blog); session.close(); }
2.单参数array数组的类型:
<select id="dynamicForeach2Test" parameterType="java.util.ArrayList" resultType="Blog"> select * from t_blog where id in <foreach collection="array" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> </select>
上述collection为array,对应的Mapper代码:
public List dynamicForeach2Test(int[] ids);
对应的测试代码:
@Test public void dynamicForeach2Test() { SqlSession session = Util.getSqlSessionFactory().openSession(); BlogMapper blogMapper = session.getMapper(BlogMapper.class); int[] ids = new int[] {1,3,6,9}; List blogs = blogMapper.dynamicForeach2Test(ids); for (Blog blog : blogs) System.out.println(blog); session.close(); }
3.自己把参数封装成Map的类型
<select id="dynamicForeach3Test" parameterType="java.util.HashMap" resultType="Blog"> select * from t_blog where title like "%"#{title}"%" and id in <foreach collection="ids" index="index" item="item" open="(" separator="," close=")"> #{item} </foreach> </select>
上述collection的值为ids,是传入的参数Map的key,对应的Mapper代码:
public List dynamicForeach3Test(Map params);
对应测试代码:
@Test public void dynamicForeach3Test() { SqlSession session = Util.getSqlSessionFactory().openSession(); BlogMapper blogMapper = session.getMapper(BlogMapper.class); final List ids = new ArrayList(); ids.add(1); ids.add(2); ids.add(3); ids.add(6); ids.add(7); ids.add(9); Map params = new HashMap(); params.put("ids", ids); params.put("title", "中国"); List blogs = blogMapper.dynamicForeach3Test(params); for (Blog blog : blogs) System.out.println(blog); session.close(); }
7. Include
最后提一句这个标签。。。怕忘了,,用于引用外部sql标签
未完,待续。。。