Mybatis

一概述

1.什么是Mybatis?

Mybatis是Apache的一个开源项目,原名为ibatis,移植到google code后改名为Mybatis,目前迁移到了Github。Mybatis是一个支持定制化SQL、存储过程以及高级映射的优秀持久层框架,避免了几乎所有的JDBC代码、手动设置参数以及获取结果集的过程。

2.Mybatis与Hibernate对比

两者的主要区别在于封装程度:

  • Hibernate封装程度较高,表现为可以自动建立映射关系,即查询结果自动封装为实体类对象,而Mybatis必须手动为结果指明映射的实体类。
  • Hibernate封装了SQL语句,而Mybatis中的SQL语句需要程序员编写。
  • Hibernate能够创建表,Mybatis不能创建与修改表,只能修改表中的数据。
  • Hibernate不需要程序员编写SQL语句,移植性比Mybatis高。

正因为Hibernate封装程度较高,获得同样的结果所需代码比Mybatis复杂,执行速度慢,而Web应用中速度是至关重要的,所以Hibernate正逐渐被Mybatis取代。

3.Mybatis体系结构

⑴API接口

Mybatis的顶层,Mybatis提供给开发人员的用于操作数据库的入口,主要是一些对象中的方法。

⑵数据处理层

顶层的API接口被调用以后,触发数据处理层,该层具体负责SQL查找、SQL解析、SQL执行、结果映射。

⑶基础支撑层

为整个框架的运行提供了基础性质的支撑,主要有配置加载、连接管理、事务管理、缓存管理。

4.Mybatis基本执行过程

启动时加载配置文件,为映射文件中的每一个SQL语句创建一个MappedStatement对象,通过API接口调用SQL语句,系统根据id找到对应的MappedStatement对象,解析SQL语句,连接数据库执行,最后将查询结果映射成属性或者对象。

5.配置文件

<configuration>
    <!--加载包含数据库连接四要素的属性文件 -->
    <properties  resource="dbConnection.properties"/>
    <!--为映射文件中的类起一个别名,这样在映射文件中可以省去书写全限定性类名,直接使用别名 -->
    <typeAliases>
             <typeAlias type="com.mybatis.beans.demo01.Student" alias="Student"/>
             <!--<package  name="com.mybatis.beans.demo01" /> -->
    </typeAliases>
    <!--配置环境,可以有多个环境,比如测试环境,发布环境,default选择使用的环境。环境指的是数据库管理方面的信息,比如数据源、事务-->
    <environments default="development">
       <environment id="development">
           <!--表示采用JDBC默认的事务管理器,项目开发时用DataSourceTransactionManger-->
           <transactionManager type="JDBC" />
           <!--采用Mybatis自带的数据库连接池,项目开始时通常使用C3P0或者DBCP -->
           <dataSource type="POOLED">
                      <!--从加载的属性文件中读取数据库连接四要素 -->
                      <property name="driver" value="${jdbc.driver}" />
                      <property name="url" value="${jdbc.url}" />
                      <property name="username" value="${jdbc.username}" />
                      <property name="password" value="${jdbc.password}" />
           </dataSource>
       </environment>
    </environments>
    <!--加载映射文件 -->
    <mappers>
           <mapper resource="com/mybatis/beans/demo01/Student.xml" />
    </mappers>
</configuration>

⑴数据库连接四要素

数据库连接四要素通常放在属性文件中,便于更改。为每一个环境都创建一个属性文件,key相同,当需要加载某个环境时,只需要将总环境的默认值设为该环境的id。从属性文件中读取数据的基本语法格式:${key}。

⑵别名

<typeAliases>用于为类指定一个别名:

  • 为全限定性类名指定一个别名,在映射文件中使用该别名替代全限定性类名,使代码简洁:
  • <typeAlias type="com.mybatis.beans.demo01.Student" alias="Student"/>
  • 指定包名,在没有注解的情况下,系统采用非限定性类名作为别名,首字母可以小写:
  • <package name="com.mybatis.beans.demo01" />

⑶其他标签

  • <environments>:配置文件中可以有多个环境,分别用于不同的用途,default属性指定当前使用的环境。
  • <environment>:主要包含事务管理器、数据源等。
  • <mappers>:将映射文件加载到配置文件中。

6.映射文件

<mapper namespace="student">
      <insert id="insertStudent">
               insert  into tb_student(name,score)values(#{name},#{score})
                      ----------在数据插入完成后获取插入数据的id------------
              <selectKey resultType="int"keyProperty="id">
                              select@@identity
              </selectKey>
      </insert>
      <delete id="deleteStudent">delete from tb_student  whereid=#{?}</delete>
                      ---------------模糊查询-------------------------------
      <select id=""resultType="xxxx">select name from xxx where name like '%'#{}'%'</select>
<mapper>
  • namespace:命名空间,即名称存在的范围,用于区分不同映射文件中同名的SQL。
  • id:在同一个映射文件中不可重复,用于在程序中调用该SQL。
  • parameterType:当参数类型是实体类时,可以直接使用配置文件中的别名。
  • select:有返回值,返回值可以是属性,也可以是实体类对象,因此必须指定单行数据返回值类型resultType,以便系统按照该类型返回查询结果。
  • Mybatis为常见的java类型设定了别名,别名大小写不敏感,即大小写作用相同。基本数据类型的别名在前面加下划线,如_int,其他常用类型的别名就是首字符小写后的非限定性类名,如String的别名是string,由于大小写不敏感,也可以写成String。

⑴获取参数

  • #{属性名}:获取对象属性值。
  • #{xxx}:用作占位符,可以是任意合法字符,当参数不是实体类对象时取得参数的值。

⑵模糊查询

‘%’#{xxx}

为了避免系统将占位符视作普通字符处理,占位符放在单引号或者双引号外面,与单引号或者双引号之间不使用任何连接。

7.结果封装

Mybatis从数据库查询得到的是字段值,采用反射机制将字段封装为实体对象,封装时根据字段名调用set方法初始化对象,因此表中的字段名必须与实体类中对应的属性名相同,如果字段名与属性名不同,那么该属性无法被初始化。

属性名与字段名不一致时的解决方案:

  • 使用别名:在SQL语句中使用别名,通过别名将查询结果的字段名修改为属性名,缺点是每一次查询时都需要创建别名。
  • 使用resultMap:采用Hibernate的处理方式,手动在字段与属性之间建立映射:
  • <resultMap type="studentResultMap"id="studentMap">
           <id column="id" property="sid" />
           <result column="name" property="sname" />
    </resultMap>

8.SqlSessionFactory

  • 相当于Hibernate中的SessionFactory对象,主要负责创建SqlSession对象。创建初始化过程耗费大量系统资源,并且线程安全,因此将对象设定为应用级的,即在整个应用范围采用单例模式。
  • 创建:
  • InputStream input=Resources.getResourceAsStream("配置文件的类路径");//基于路径创建输入流
    SqlSessionFactory factory=new  SqlSessionFactoryBuilder().build(input);

输入流在工厂对象创建完毕后自动关闭,不需要手动关闭。

9.SqlSession

  • Mybatis持久化操作的核心,线程不安全,应该设置为多例的,即一个线程一个。
  • 创建:
  • SqlSession sqlSession=factory.openSession();//使用openSession的无参形式时,自动关闭自动提交
  • 主要方法:
  • sqlSession.close();//关闭sqlSession对象,如果未回滚,回滚
  • dirty:一个框架底层的boolean值,主要用于判断当前缓存中内容与数据库中内容是否相同,相同为false,不同为true,数据提交或者回滚时需要判断该值。

10.日志输出

使用log4j或者log4j2输出Mybatis框架内部运行状况。

二 Mapper动态代理

1.原理

如果Dao接口的方法名与映射文件中SQL语句的id相同,那么根据方法名就可以找到对应的SQL语句,从而省去将方法与SQL语句绑定的过程。底层利用JDK动态代理创建了一个代理对象,该代理对象在Dao方法与映射文件SQL语句间建立了映射关系,所以称作MapperProxy。

2.条件

方法名与id相同,命名空间为Dao接口的全限定性类名。

3.获取操作对象

由于采用Mapper动态代理,省去了Dao实现层,只剩下Dao接口,而Dao接口无法实例化,那么就根据该接口创建一个代理对象,作为操作对象:

IDao dao=sqlSession.getMapper(IDao.class);

三多条件查询

1.什么是多条件查询?

根据多个条件进行查询,叫做多条件查询,即查询时传入多个参数,称作多参数查询更准确。

2.产生的原因

同时向SQL语句传入多个参数,就出现了一个对应问题,传入的参数与SQL语句中的哪个占位符对应,因此产生了多参数查询问题。Mybatis中进行持久化操作的方法只允许存在一个传入SQL语句的参数。

3.解决方法

  • 手动封装:为每一个参数设定一个key值,将多个key/value保存到Map对象中,将Map对象作为参数传入SQL语句,在SQL语句中通过 #{key}获取参数value的值。
  • 自动封装:在方法的形参上设定key,由底层Map集合:
  • Student selectStudentByIndex(@Param("name")String name,@Param("score") double score);
  • 高版本多参数查询不支持索引。

四 #在映射文件中的用法

  • #{attr}:当参数是实体类对象时,获取属性值。
  • #{?}:当参数是基本数据类型或者字符串时,用作占位符,获取参数的值,{}内部可以是任意合法字符,一般用属性名。
  • #{key}:当参数是Map集合时,通过key值获取value的值。

五动态SQL

1.什么是动态SQL?

根据传入的参数选择性执行SQL语句,这一过程叫做动态SQL.

2.动态SQL中用到的字符

<if>\<where>\<choose  when  otherwise>\<foreach>。

3.<where if>

从多个过滤条件中选择若干个,基本格式:

<where>
       <if test="scoreKey>0">score>#{scoreKey}</if>
       <if test="nameKey!=null &amp;nameKey!=''">and name like"%"#{nameKey}"%"</if>
</where>
  • test用来指明判断条件,其中使用key值;为了完成组合,除第一个<if>标签外,其他标签的内容都必须以连接符开头,如and,or。
  • 映射文件是一个XML文档,而在XML文档中不允许使用> >= < <= &,必须使用对应的实体替换。

4.<where choose when otherwise>

从多个条件中选择第一个为真的执行,基本格式:

<where>
     <choose>
           <when test="nameKey!=null and nameKey!=''"> name like"%"#{nameKey}"%"</when>
           <when test="scoreKey > 0">score>#{scoreKey}</when>
           <otherwise>1=2</otherwise>
     </choose>
</where>

5.<where foreach>

用于遍历列表,基本格式:

    <select id="selectStudentsByArray" resultType="student">
        select id,name,score from tb_student
        <where>
            <choose>
                <when test="array.length>0">
                    id in
                    <foreach collection="array" item="myid" open="(" close=")" separator=",">
                        #{myid}
                    </foreach>
                </when>
                <otherwise>1=2</otherwise>
            </choose>
        </where>
    </select>
  • 当参数类型是数组时,collection值为array;参数类型是List时,collection值为list。
  • item表示遍历的子对象,集合或者数组中一个元素。
  • open/close/separator:用于拼凑JDBC中的(a,b,c)的列表形式。

六实体关联查询

1.什么是实体关联查询?

两个实体间存在关联关系,即引用关系,通过一个实体查询关联的实体,这就是实体关联查询。

2.关联关系查询时必须手动地为字段建立与属性的映射关系。

3.一对多关联查询

映射文件的编写:

<resultMap id="countryMapAlone"type="Country">
          <id property="cid" column="cid" />
          <result property="cname" column="cname" />
          <collection property="ministers" ofType="Minister"select="ministerSelect"column="cid" />
</resultMap>
<select id="selectCountryByIdAlone" resultMap="countryMapAlone">
          selectc id,cname from tb_country where cid=#{cid}
</select>
<select id="ministerSelect" resultType="Minister">
          select mid,mname,countryId from tb_minister where countryId=#{cid}
</select>

在<collection>标签中:

  • ofType:指明集合中的每一个元素的类型。
  • select属性用来指明从加载方的查询语句。
  • column属性指明将主加载方查询结果的哪个字段传入从加载方的查询语句中。

4.多对一关联查询

映射文件编写:

<select id="selectMinisterById"resultMap="ministerMap">
           select mid,mname,countryId from tb_minister where mid=#{id}
</select>
<resultMap id="ministerMap"type="com.mybatis.entityRelation.m2o.Minister">
           <id property="mid" column="mid" />
           <result property="mname" column="mname" />
           <result property="countryId" column="countryId" />
           <association property="country"javaType="com.mybatis.entityRelation.m2o.Country"
                 select="selectCountry"column="countryId" />
</resultMap>
<select id="selectCountry" resultType="com.mybatis.entityRelation.m2o.Country">
                 selectc id,cname from tb_country where cid=#{countryId}
</select>

5.自关联

两个实体类除了一个关联属性外其他属性完全相同,将两个实体当做一个实体处理,这样关联关系就转化为了自关联。把自关联看做一对多双向关联,以下示例为查找自身及其子对象:

<select id="selectNewsLabelsById"resultMap="">
             select id,name,pid from tb_newslabel where id=#{id}
</select>
<resultMap id="" type="newsLabel">
            <id property="id" column="id" />
<result property="name" column="name" />
           <result property="pid" column="pid" />
           <collection property="childNewsLabels" ofType="newsLabel"select="selectChild"column="id" />
</resultMap>
<select id="selectChild" resultType="newsLabel">
           select id,name,pid from tb_newslabel where pid=#{id}
</select>

6.多对多关联查询

⑴将多对多关联看做两个一对多关联,一方是原有两方中的任一方,多方是中间表,中间表有两个外键,分别引用其他两张表的主键。

⑵多对多关联查询多采用表连接查询,同时查询三张表,因为这种操作方式简单。

<select id="selectStudentByIdAlone" resultMap="aloneMap">
            select  sid,sname from tb_studentc where sid=#{sid}
</select>
<resultMap id="aloneMap"type="com.mybatis.entityRelation.m2m.Student">
            <id property="sid" column="sid" />
            <result property="sname" column="sname" />
            <collection property="courses" ofType="com.mybatis.entityRelation.m2m.Course"
                  select="selectCourse"column="sid" />
</resultMap>
<select d="selectCourse"resultType="com.mybatis.entityRelation.m2m.Course">
    select cid,cname from tb_course where cid in (select course_id from tb_mid where student_id=#{sid})//采用临时表
</select> 

七延时加载

1.什么是延时加载?

加载主加载方时,不立即加载从加载方,而是根据设定延迟加载从加载方。

2.延时加载针对的关联查询时的从加载方,设定从加载方的加载时机。

3.Mybatis中加载从加载方的几种方式:

  • 直接加载:主加载方加载完毕后,立即加载从加载方,未开启延时加载时采用该加载方式。
  • 侵入式延迟:将从加载方当做主加载方的详情,访问主加载方的详情时,加载从加载方。
  • 深度延迟:只有在访问从加载方对象时才加载从加载方。

4.延时加载的设置

⑴默认情况下,延迟加载是关闭的,主加载方加载完毕后立即加载从加载方。在配置文件中开启延时加载:

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

⑵开启延迟加载以后,默认采用深度延迟。

⑶直接输出主加载方对象时,无论采取哪种延迟策略,都会立即加载从加载方。

⑷如需要采用侵入式延迟:

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

八缓存

1.概述

  • 缓存内容的来源:SQL查询从数据库中加载的数据。
  • 缓存内容的类型:封装的实体类对象与分散的属性值。
  • 无论一级缓存,还是二级缓存,只在同一namespace中可见,对其他namespace的查询不可见。

2.存储结构

从数据库加载的数据并不直接存放到一级缓存与二级缓存中,而是存放到一个内存公共区域,一级缓存与二级缓存分别去引用该内存,即一级缓存与二级缓存持有的是同一对象的引用变量。

数据在一级缓存与二级缓存中以Map集合形式存储,key为SQL id与SQL语句的组合,value是对象的引用变量。

3.一级缓存:

  • 一级缓存:SqlSession级的,缓存中的内容只能在同一个SqlSession内部共享,生命周期与SqlSession相同。
  • 一级缓存是默认的缓存,无法关闭,也无法通过设置改变属性,只能采用默认的方式。映射文件中<select>标签的属性useCache是用来关闭与开启二级缓存的,对一级缓存不起作用。
  • sqlSession关闭意味着Session对象销毁,一级缓存释放持有的引用地址,而不是销毁对象。

4.二级缓存

Mybatis提供了两种二级缓存方案:内置二级缓存与第三方二级缓存Ehcache。二级缓存默认情况下是开启的。

⑴开启内置二级缓存

  • 序列化实体类:应用内置二级缓存的实体类必须序列化。
  • <cache/>:在指定命名空间使用二级缓存。

⑵开启Ehcache

  • 导入架包。
  • 在命名空间指定使用Ehcache二级缓存:
  • <cache type="org.mybatis.caches.ehcache.EhcacheCache" />:
  • 导入Ehcache配置文件ehcache.xml,放在类路径下。

⑶被动加载对象

二级缓存不会主动加载对象,要想将对象加载到二级缓存中,必须在命名空间下开启二级缓存:<cache/>,该命名空间下所有查询操作的结果都会被保存到二级缓存中。

⑷关闭二级缓存

  • 应用范围关闭:
  • <setting name="cacheEnabled" value="false" />
  • 命名空间范围关闭:注销掉命名空间下的<cache/>。
  • SQL范围关闭:
  • <select useCache="false"/>

⑸二级缓存使用原则

  • 避免多个namespace操作同一张表:二级缓存是基于namespace的,不同namespace对二级缓存的操作是隔离的,相当于每一方都把数据库中的数据读取到内存中,各自独立操纵,这样当一方的操作同步到数据库以后,另一方还在操作数据库中改变了的数据,造成幻读。
  • 不要在关联关系表上执行增删改操作:不同的表对应不同的namespace,同一张表可能同时被多个namespace操作,导致幻读。
  • 查询多于增删改时使用二级缓存:增删改清空缓存,消耗系统资源。

⑹清空缓存

执行增删改操作会清空一级缓存引用的对象:

此时一级缓存持有对象的引用变量,执行增删改操作会清空该引用变量指向的对象,由于二级缓存与一级缓存引用同一对象,二级缓存同样被清空。

当一级缓存不再持有对象的引用时,对象只被二级缓存引用,此时执行增删改操作是否清空二级缓存可以在映射文件中设定,<insert><delete><update>标签的共有属性flushCache就是用来设定执行该操作时是否清空二级缓存的。

  • true:会由二级缓存的引用变量出发,清空该变量引用的对象。
  • false:不会清空对象。

九注解

1.注解是在java类文件中添加注解以替代映射文件的注册方式。

2.为了充分发挥框架的功能,Mybatis推荐使用映射文件,不建议使用注解。

3.常用注解:

  • @Insert()/@Delete()/Update()。
  • @Select(value=""):使用注解查询时可以省略结果类型。
  • 获取刚插入数据的主键值,必须与插入注解联合使用:
  • @SelectKey(statement = "select@@identity",resultType=Integer.class,keyProperty = "id", before =false)
  • 在配置文件中注册Dao接口:
  • <mapper class=""/>//通过Dao类注册。
    <package name="Dao类所在的包"/>//通过包名注册,可以同时注解该包下的多个Dao接口。
posted @ 2017-05-27 22:39  tonghun  阅读(764)  评论(0编辑  收藏  举报