MyBatis持久化框架
什么是MyBatis?
- 原本是apache的一个开源项目iBatis
- MyBatis是一个持久化框架,支持自定义sql查询、存储过程以及高级映射
- MyBatis消除了所有的代码和参数的手工设置
- MyBatis是一个ORM框架
- MyBatis可以使用XML或注解方式进心配置、映射;
MyBatis与Hibernate的区别:
- MyBatis将实体类和sql语句之间建立映射关系
- Hibernate在实体类和数据库表之间建立映射关系
MyBatis核心配置
包含了对MyBatis系统的核心设置,包含获取数据库连接实例的数据源【DataSource】和决定事务作用域和控制方式的事务管理器【TransactionManager】。
settings配置全局参数,会改变MyBatis的运行时行为。
logImpl设置指定Mybatis所用日志的具体实现,设 STDOUT_LOGGING 表示在控制台显示sql语句
未指定则自动查找。
配置环境【environments
mybatis可以配置成适应多种环境,配置多个environment子元素
environments的default属性取值要与environment的id属性取值一致
环境【environment
事务管理器 transactionManager type属性表示事务管理器类型
- JDBC-直接使用JDBC事务提交和回滚
- MANAGED(托管)-不提交或回滚,让容器来管理事务的整个生命周期(Spring
数据源 dataSource type属性表示数据源类型
- UNPOOLED 每次被请求时简单打开、关闭连接
- POOLED JDBC连接对象的数据库连接池的实现,避免创建新的连接实例
- JNDI 使用Spring、应用服务器的容器
MyBatis映射文件
<mapper> namespace属性代表唯一标识符,是映射器接口的完全限定名
<select> 映射查询语句 id表示方法名 resultType表示结果集
<insert> 映射插入语句
<update> 映射更新语句
<delete> 映射删除语句
MyBatis中Mapper接口的要求
- Mapper接口的全限定名为映射文件的namespace的值
- Mapper接口的方法名称和映射文件中定义的每个sql的id相同
- Mapper接口的方法参数和映射文件中定义每个sql的parameterType类型相同
- Mapper接口的方法返回的单个对象类型和映射文件中定义的每个sql的resultType类型相同
映射文件与主配置文件关联
在mybatis.xml中
- <mappers>
- <mapper resource="../../../Mapper.xml"/>
- </mappers>
映射器【mappers】:用来配置多个映射文件的位置
使用相对于类路径的资源引用
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
使用映射器接口的完全限定类名
<mapper class="org.mybatis.builder.AuthorMapper"/>
将包内的映射器接口全部注册为映射器
<package name="org.mybatis.builder"/>
动态代理机制
MyBatis与数据库的交互方式:
- 使用传统的MyBatis提供的API
- 使用Mapper接口
- 通过sqlSession.getMapper(XXXMapper.class) 方法,MyBatis 会根据相应的接口声明的方法信息,通过动态代理机制生成一个Mapper 实例
- 使用Mapper 接口的某一个方法时,MyBatis 会调用MapperProxy类的invoke()方法
- 底层还是通过SqlSession的select、update、delete、inser
- 等方法来实现对数据库的操作
插入:
<insert id="insert" parameterType=" ">
insert into Table(表属性)
values(#{实体类属性},#{实体类属性})
</insert>
id属性:命名空间唯一标识符,为mapper中方法名字
parameterType:方法参数类型,可省略
#{}:MyBatisSQL使用预编译参数方式,当实际参数为JavaBean对象,大括号内是属性名
返回主键自增值:(只对insert、upadte有用
useGeneratedKeys="true"
keyProperty="id"
不支持自增,返回id值:在<insert>内容添加
<selectKey resultType="int" keyProperty="id" order="AFTER">【oracle为Before】
select LAST_INSERT_ID()
</selectKey>
selectKey只支持数据库生成添加的ID,不能找到自己添加的
查询:
- <select id="映射方法名" resultType="查询结果的实体类">
- select * from USER where id = #{id}
- </select>
实体类属性名和表字段不一致:
1.别名
2.使用resultMap
- <resultMap type="com.mybatis.entity.User" id="userMap">
- <id property="id" column="id"/>
- <result property="userName" column="user_name"/>
- <result property="password" column="password"/>
- </resultMap>
- <select id="selectById" resultMap="userMap">
- select * from USER where id = #{id}
- </select>
模糊查询
<select id="方法名" resultMap="userMap">
select * from user where user_name like "%"#{name}"%"【占位符,安全
</select>
更新:
<update id="方法名">
update table
set num = #{num},
pas = #{pas}
where id = #{id}
</update>
删除:
<delete id="方法名">
delete from table where id=#{id}
</delete>
两个参数:arg0、arg1;param1、param2,Mapper方法加注解@Param(""),
一对一映射
两个实体类+映射接口
处理一对一关联关系的方法:
- 使用自动映射处理一对一关系
- 通过别名自动将值匹配到对应字段
- 对于外键:属性 "对象类属性名.属性"
- 使用resultMap配置一对一映射
- <resultMap>
- 使用association元素配置一对一映射
- <association property="对象类">
- <id property="" column=""/>
- <result property="" column=""/>
- </association>
- 属性resultMap=" "
- association元素的嵌套查询
- 在ResultMap中
- <association
-
property="shoppingCart"//返回值
-
column="{user_id=id}"//参数{参数值=ResultMap中的元素}
-
select="com.mybatis.mapper. ShoppingCartMapper.findShoppingCartById"/>
- fetchType//数据加载方式 【lazy】延迟加载 【eager】积极加载,会覆盖lazyLoadingEnabled
- 补充:在<settings/>中 <setting name="lazyLoadingEnabled" value="true">//全局延迟加载
- <setting name="aggressiveLazyLoading" value="true">方法的调用都会加载该对象的所有属性。
- 延迟加载时,
equals、hashCode、toString、clone方法时,就会加载该对象的全部数据
- 使用自动映射处理一对一关系
对比
-
- 前面的三种方式都属于“关联的嵌套结果映射“,即通过一次SQL查询根据表或指定的属性映射到不同的对象中
- 最后一种方式属于“关联的嵌套查询”,利用简单的SQL语句,通过多次查询得到想要的结果,可实现延迟加载效果
一对多
方式:
-
- collection集合的嵌套结果映射:通过一次SQL查询找到所有结果
-
<resultMap type="com.mybatis.entity.User" id="userAndOrderListMap" extends="userMap">
-
<collection property="orderList" ofType="com.mybatis.entity.Order">//必须填
-
<id property="id" column="order_id"/>
-
<result property="price" column="price"/>
-
</collection>
-
-
</resultMap>
- resultMap元素中的extends属性可以实现结果映射的继承
-
- collection集合的嵌套查询:会执行额外的SQL查询
-
<resultMap type="com.mybatis.entity.User" id="userAndOrderListMap" extends="userMap">
-
<collection
-
property="orderList"
-
column="{uid=id}"
-
ofType="com.mybatis.entity.Order"//必写
-
select="com.mybatis.mapper.OrderMapper.findOrdersByUserId">
-
</collection>
-
-
</resultMap>
-
- collection集合的嵌套结果映射:通过一次SQL查询找到所有结果
鉴别器
单独的数据库查询返回很多种不同类型的结果集
<discriminator>
属性:
column:该属性用于设置需要进行鉴别比较值的列
javaType:该属性用于指定列的类型
<case>
属性:
value:该值用来匹配column指定字段的值
resultMap:当value值和column的值匹配时的结果映射,优先级高于resultType
resultType:当value值和column的值匹配时的结果类型
- <resultMap type="com.mybatis.entity.SalariedEmployee" id="SalaryMap" extends="EmployeeMap">
- <result property="salary" column="salary"/>
- </resultMap>
- <resultMap type="com.mybatis.entity.Employee" id="selectMap">
- <discriminator column="employee_type" javaType="String">
- <case value="HE" resultMap="HourlyMap"/>
- <case value="SE" resultMap="SalaryMap"/>
- </discriminator>
- <discriminator column="employee_type" javaType="String">
- </resultMap>
动态SQL
if 判断参数值是否插入条件中
- where 1=1
- <if test="phone != null and !phone.equals('')">
- and phone = #{phone}
- </if>
- <if test="email != null and !email.equals('')">
- and email = #{email}
- </if>
- <if test="phone != null and !phone.equals('')">
值为true,则放入sql语句、为false则不放
test的值符合ONGL对象图导航语言:表达式中能够出现方法调用,但是结果只能为true或者false

如果不想出现where 1=1,则使用<where>:如果该元素中有内容,就在生成SQL语句时加上where条件,如果该元素的内容以AND或者OR开头,就去除开头
- <where>
- <if test="phone != null and !phone.equals('')">
- and phone = #{phone}
- </if>
- <if test="email != null and !email.equals('')">
- and email = #{email}
- </if>
- <if test="phone != null and !phone.equals('')">
- </where>
if的更新:<set>:生成SQL语句时就加上set语句;当set元素的内容以逗号结尾时,去掉逗号
- <set>
- <if test="userName != null and !userName.equals('')">
- user_name = #{userName},
- </if>
- <if test="userName != null and !userName.equals('')">
- </set>
if的插入:
insert into user(user_name, password, phone
- <if test="email != null and !email.equals('')">
- </if>
- ) values(#{userName}, #{password}, #{phone}
- <if test="email != null and !email.equals('')">
- ,#{email}
- </if>)
choose (when, otherwise)
至少有一个when
至多有一个otherwise
- where 1 = 1
- <choose>
- <when test="id != null and id != ''">
- and id = #{id}
- </when>
- <when test="userName != null and userName != ''">
- and user_name = #{userName}
- </when>
- <otherwise>
- and 1 = 2
- </otherwise>
- <when test="id != null and id != ''">
- </choose>
- <choose>
trim (where, set)
元素属性:
prefix:给内容增加该属性指定的前缀
prefixOverrides:把内容中匹配的前缀字符串去掉
suffix:给内容增加该属性指定的后缀
suffixOverrides:把内容中匹配的后缀字符串去掉
实现where功能
<trim prefix="where" prefixOverrides="AND |OR ">……</trim>
实现trim功能
<trim prefix="set" suffixOverrides=",">……</trim>
foreach
- <foreach collection="list" open="(" close=")" separator="," item="id" index="i">
- #{id}
- </foreach>

collection属性:
List:list
数组:array
Map: _parameter
也可以使用@Param注解指定名字,collection取值为该注解指定的名字
foreach实现动态更新,当参数是Map类型时,
foreach元素的index属性值对应的不是索引值,而是Map中的key
- update user set
- <foreach collection="_parameter" item="value" index="key" separator=",">
- ${key} = #{value}
- </foreach>
- <foreach collection="_parameter" item="value" index="key" separator=",">
- where id = #{id}
bind
使用OGNL表达式创建一个变量并将其绑定到上下文中
- <bind name="userName" value="'%' + name + '%'"/>
- select * from user
- where user_name like #{userName}
在mybatis.xml数据库名配置后添加 &allowMultiQueries=true:允许多条sql执行
Mybatis缓存配置:使应用更快地获取数据,避免频繁的数据库交互
一级缓存(本地缓存
SqlSession会话级别的缓存,位于表示一次数据库会话的SqlSession对象之中。
用户不能配置,默认情况下自动支持的缓存
根据 返回值、参数、statementId
一级缓存的生命周期
MyBatis在开启一个数据库会话时,会创建一个新的SqlSession对象;
当会话结束时,SqlSession对象也一并释放掉。
SqlSession调用了close()方法,会释放掉一级缓存,一级缓存将不可用;
SqlSession调用了clearCache(),会清空缓存,但是该对象仍可使用;
SqlSession中执行了任何一个update操作、insert操作、delete操作,都会清空缓存,但是该对象可以继续使用;
缓存获取条件:
- 传入的 statementId
- 查询时要求的结果集中的结果范围
- 这次查询所产生的最终要传递给JDBC
- java.sql.Preparedstatement的Sql语句字符串
- 传递给java.sql.Statement要设置的参数值
一级缓存的特点
- 使用了HashMap来维护,并没有对HashMap的容量和大小进行限制。
- 一级缓存是一个粗粒度的缓存,没有更新缓存和缓存过期的概念
- 适当清空sqlSession的缓存
- select操作执行大范围的sqlsession对象,生存时间不应太长
二级缓存
Application级别的缓存,它的作用范围是整个应用程序。
默认开启,settings配置中有一个参数cacheEnable,当把该参数的值设置为false时,可关闭二级缓存
<setting name="cacheEnable" value="false"/>
二级缓存是和命名空间绑定的,即二级缓存需要配置在映射文件中
配置方式:
- 为每一个Mapper分配一个Cache缓存对象(使用<cache>元素配置)
- 多个Mapper共用一个Cache缓存对象(使用<cache-ref>元素配置);
二级缓存开启条件:
- 二级缓存的全局配置开启
- 使用二级缓存的实体类要实现Serializable接口。【序列化/反序列化,让实体以二进制的形式存在文件中
- 在<mapper>标签中添加<cache/>
二级缓存效果:
- 映射语句文件中的所有 select 语句将会被缓存
- 映射语句文件中的所有 insert,update和delete语句会刷新缓存
- 缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回
- 根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序来刷新
- 缓存会存储集合或对象(无论查询方法返回什么类型)的 1024 个引用
- 缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改
序列化:使用可读可写的缓存,实例化的是两个对象,所以不同。
MyBatis刷新和置换的策略
LRU(Least Recently Used):最近最少使用算法,移除最长时间不被使用的对象,这是默认值
FIFO(First in first out):先进先出算法,移除最先进入缓存中的对象
flushInterval:刷新间隔,会以指定的某一个时间间隔将缓存中的数据清空,不设置时仅仅在调用语句时刷新
size:可以缓存的对象数目,默认值为1024
readOnly:只读属性,只读的缓存会给调用者返回缓存对象的相同实例,因此这些对象不能被修改;可读写的缓存会返回缓存对象的拷贝,这种方式更安全,因此默认值是false
<cache readOnly="true"></cache>//只会做查询,不做修改
序列化的对象:返回对象相同的引用
- <cache
- eviction="FIFO"
- flushInterval="60000"
- size="512"
- readOnly="true"/>
命中率:Hit,命中次数/查询次数
执行顺序:二级缓存、一级缓存、数据库
使用二级缓存:
- 它自己内部基于HashMap实现了一系列的Cache缓存实现类,并提供了各种缓存刷新策略如LRU,FIFO等
- MyBatis还允许用户自定义Cache接口实现,然后将Cache实现类配置在<cache type="">type属性上即可
- MyBatis还支持第三方缓存框架(EhCache、Redis)的集成
集成EhCache缓存
添加jar包
resources目录下配置ehcache.xml配置文件
修改映射接口中的缓存配置
- <mapper namespace="com.mybatis.mapper.UserMapper">
- <cache type="org.mybatis.caches.ehcache.LoggingEhcache"/>
- <!-- 其他配置 -->
- </mapper>
解决脏数据读取:
<cache-ref namespace="net.onest.mapper.UserMapper"/>
二级缓存适用场景
- 以查询为主的应用中,只有尽可能少的增、删、改操作
- 绝大多数都是单表操作时,很少出现相互关联的情况
- 如关联的表比较少,可以通过参照缓存进行配置

浙公网安备 33010602011771号