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接口
  1. 通过sqlSession.getMapper(XXXMapper.class) 方法,MyBatis 会根据相应的接口声明的方法信息,通过动态代理机制生成一个Mapper 实例
  2. 使用Mapper 接口的某一个方法时,MyBatis 会调用MapperProxy类的invoke()方法
  3. 底层还是通过SqlSession的select、update、delete、inser
  4. 等方法来实现对数据库的操作

插入:

<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(""),

 一对一映射

两个实体类+映射接口

处理一对一关联关系的方法:

    1. 使用自动映射处理一对一关系
      • 通过别名自动将值匹配到对应字段
      • 对于外键:属性 "对象类属性名.属性"  
    2. 使用resultMap配置一对一映射
      • <resultMap>
    3. 使用association元素配置一对一映射
      • <association property="对象类">
      • <id property="" column=""/>
      • <result property="" column=""/>
      • </association>
      • 属性resultMap=" "
    4. 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>

鉴别器

单独的数据库查询返回很多种不同类型的结果集

<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>
  • </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>

值为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>
  • </where>

  if的更新:<set>:生成SQL语句时就加上set语句;当set元素的内容以逗号结尾时,去掉逗号

  

  • <set>
    • <if test="userName != null and !userName.equals('')">
      • user_name = #{userName},
    • </if>
  • </set>

  if的插入:

insert into user(user_name, password, phone

  • <if test="email != null and !email.equals('')">
    • ,email
  • </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>
    • </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>
  • where id = #{id}

 

bind

使用OGNL表达式创建一个变量并将其绑定到上下文中

  • <bind name="userName" value="'%' + name + '%'"/>
  • select * from user
  • where user_name like #{userName}

 

在mybatis.xml数据库名配置后添加 &amp;allowMultiQueries=true:允许多条sql执行

 

Mybatis缓存配置:使应用更快地获取数据,避免频繁的数据库交互

一级缓存(本地缓存

SqlSession会话级别的缓存,位于表示一次数据库会话的SqlSession对象之中。

用户不能配置,默认情况下自动支持的缓存

根据 返回值、参数、statementId

 

一级缓存的生命周期

MyBatis在开启一个数据库会话时,会创建一个新的SqlSession对象;

当会话结束时,SqlSession对象也一并释放掉。

 

SqlSession调用了close()方法,会释放掉一级缓存,一级缓存将不可用;

SqlSession调用了clearCache(),会清空缓存,但是该对象仍可使用;

SqlSession中执行了任何一个update操作、insert操作、delete操作,都会清空缓存,但是该对象可以继续使用;

缓存获取条件:

  1. 传入的 statementId
  2. 查询时要求的结果集中的结果范围
  3. 这次查询所产生的最终要传递给JDBC
  4. java.sql.Preparedstatement的Sql语句字符串
  5. 传递给java.sql.Statement要设置的参数值

一级缓存的特点

  • 使用了HashMap来维护,并没有对HashMap的容量和大小进行限制。
  • 一级缓存是一个粗粒度的缓存,没有更新缓存和缓存过期的概念
    • 适当清空sqlSession的缓存
    • select操作执行大范围的sqlsession对象,生存时间不应太长

 

二级缓存

Application级别的缓存,它的作用范围是整个应用程序。

默认开启,settings配置中有一个参数cacheEnable,当把该参数的值设置为false时,可关闭二级缓存

<setting name="cacheEnable" value="false"/>

二级缓存是和命名空间绑定的,即二级缓存需要配置在映射文件中

配置方式:

  1. 为每一个Mapper分配一个Cache缓存对象(使用<cache>元素配置)
  2. 多个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"/>

二级缓存适用场景

  • 以查询为主的应用中,只有尽可能少的增、删、改操作
  • 绝大多数都是单表操作时,很少出现相互关联的情况
  • 如关联的表比较少,可以通过参照缓存进行配置
posted @ 2020-06-05 21:59  不爱学习的小策  阅读(610)  评论(0)    收藏  举报