MyBatis


MyBatis

三层架构包含三层:界面层 业务逻辑层 数据访问层

1
1

界面层:接收用户的数据 显示请求的处理结果 (Jsp servlet html)

业务逻辑层:处理业务逻辑

数据访问层:完成对数据库的增删改查操作 三层对应的包:

界面层:controller(servlet)

业务逻辑层:Service包(xxxsercice类)

数据访问层:dao包(xxxDao类) 三层中类的交互:

用户使用界面层–>业务逻辑层–>Dao层–>数据库

三层对应的处理框架:

界面层:–servlet–springMvc(框架)

业务逻辑层:–service类–spring(框架)

数据访问层:–dao类–mybatis(框架)

框架是一个舞台 是一个模板

模板:1.规定了一些条款 内容,2.加入自己的东西

MyBatis是sql映射框架

sqlmapper:sql映射 可以把数据库表中的一行数据 映射为一个Java对象,一行数据可以看作是一个Java对象 操作这个对象相当于操作表中的数据

Data Access ObjectslDaps:数据访问 对数据库的增删改查操作

MyBatis提供功能:

提供了创建Comment Statement Result的能力

提供了执行sql语句的能力 不用我们执行sql

提供了循环sql 把sql的结果转化为对象 list集合的能力

提供了关闭资源的能力 不用我们关闭 Connection Statement Result

实现步骤

1.创建person表

2.加入maven的mybatis的坐标 mysql驱动的坐标

3.创建实体类person 保存表中的一行数据的

4.创建持久层的dao接口定义操作数据库的方法

5.创建一个mybatis使用的配置文件叫做sql映射文件 用来写sql语句的 一般一个表一个sql映射文件 这个文件是xml文件

6.创建mybatis的主配置文件 一个项目就一个主配置文件 主配置文件提供了数据库的连接信息和sql映射文件的位置信息

7.创建使用mybais类 通过mybatis访问数据库

定义实体类 定义dao层接口 并在同级dao下建立与dao接口相同名字的sql映射文件 一般一张表一个 里面写官方的固定格式

接口名与Mybatis的映射文件名一定要一模一样。

sql mapper文件 sql映射文件配置

<?xml version="1.0" encoding="UTF-8" ?>      
    <!--    sql映射文件  (sql mapper)-- > 
    <!DOCTYPE mapper PUBLIC         -------指定约束文件----------
    "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!--mapper为根元素,namespace指定了命名空间 namespace="接口的全限定名称"--><mapper namespace="com.bdqn.dao.PersonDao">  
    <!--查询操作--><!--id="dao接口里的方法名" resultType="实体类全限定名称(全类名)"-->    <select id="getList" resultType="com.bdqn.entity.Person"> 返回的数据的类型    
    SELECT * FROM person   
    </select
    ><!--    插入操作-->   
    <insert id="insert"> 
    insert  into person(id,useName,sex,pass)values (#{id},#{useName},#{sex},#{pass})
    </insert>
  </mapper>

注释:1.sql映射文件是用来写sql语句的 mybatis会执行这些sql,约束文件:限制检查当前文件中出现的属性及标签必须符合mybatis的要求

2.nameSpace:叫做命名空间 唯一值 可以是自定义的字符串但是不可重复,要求使用dao接口的全限定名称(全类名)

3.在当前文件中 可以使用特定的标签 表示数据特定的操作,表示查询 select语句, 表示更新操作 update语句, 表示插入操作 insert语句, 表示删除操作 delete语句

在resources(资源)中创建mybaits.xml主配置文件 用来提供数据库的连接信息和sql映射文件位置信息

mybatis主配置文件信息

<?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">
<!--myBatis的主配置文件,主要定义了数据库的配置信息 sql映射文件的位置-->
<!--配置信息-->
<configuration>
    <!--
       properties配置,用于加载外部的配置文件
     指定properties文件的位置 从类路径根开始找文件-->
     <properties resource=" jdbc.properties"/>

<!--    settings:控制mybatis全局行为-->
    <settings>
<!--        设置mybatis输出日志-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
<!--    定义自定义的别名  ①type:自定义类型的全限定名称 alias:别名(短小 容易记忆的)-->
    <typeAliases>
<!--        <typeAlias type="com.bdqn.entity.Person" alias="ps"></typeAlias>-->
<!--        ②name是包名 这个包中所有的类 类名就是别名(类名不区分大小写)-->
        <package name="com.bdqn.entity"/>
    </typeAliases>

<!--       主配置文件中配置插件--> 
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>   插件放在环境上面<environments>

    <!--environments主要用与数据源的配置 数据库的连接信息
        可以配置多个数据源,
        通过default=“数据源id”指定-->
    <environments default="development"><!--使用的是id为development的数据源-->
        <!--
        environment:用于配置一个具体的独立的数据源
        id属性是一个唯一值 用于给当前数据源定义一个名称,方便我们在上一级environments中指定
        -->
        <environment id="development">
            <!--
            transactionManager:用于配置事务管理,事务管理默认使用的jdbc管理
            -->
            <transactionManager type="JDBC"/>
            <!--
            dataSource:具体数据源的连接信息
            type:用于指定是否使用连接池
            这里type="POOLED"代表使用连接池
            -->
            <dataSource type="POOLED">
                <!--value是从properties中取到的-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/shun?useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
        <!--第二个数据源 也可以通过数据库的属性配置文件-->
        <environment id="test">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.user}"/>
                <property name="password" value="${jdbc.pass}"/>
            </dataSource>
        </environment>
    </environments>
    <!--
    mappers:只要用于配置我们外部的映射配置文件
    在主配置中需要引入加载映射配置文件
    -->
    <mappers>
        <!-- (sql mapper) sql映射文件的位置
        mapper:主要配置引入某一个具体的映射文件,
        resource:进行路径方式引入
        -->
<!--        一个mapper标签指定一个文件的位置
                  从类路径开始的路径信息  代码编译后生成 target/classes(类路径) 不需要写target/classes
-->
<!--        <mapper resource="com/bdqn/dao/PersonDao.xml"></mapper>-->
    
<!--        当天需要因为入的映射文件多的时候  使用<package/>
           要求①mapper文件名称需要和接口名称一样 区分大小写一样
               ②mapper文件和dao接口需要在同一级目录
-->
        <package name="com.bdqn.dao"/>
    </mappers>
</configuration>

environment(环境)配置:数据库的连接信息

default:必须和某个environment的id值一样 告诉mybatis使用哪个数据库的信息 也就是访问哪个数据库

执行刚才示例操作的最后一步 创建使用nybatis类 通过mybatis访问数据库

使用步骤:

访问mybatis读取student 数据

1.定义mybatis猪配置文件的名称 从类路径的根开始(target/classes)

String config=“mybatis.xml”;

2.读取这个config表示的文件

InoutStream in=Resources.getResourceASStream(config);

3.创建sqlSessionFactoryBuilder对象

sqlSessionfactoryBuilder builder=new sqlSessionfactoryBuilder();

4.创建sqlSessionFactory对象

sqlSessionFactiory factory=builder.build(in);

5.获取sqlsession对象 从sqlSessionFacetory中获取(重要)

SqlSession sqlsession=factory.openSession() 里面参数为true时候 默认提交事务 不然则非默认提交事务

6.指定要执行的sql语句的标识 sql映射文件中的namespace+“.”+标签id值即和方法名一样

Strign sqlId=“com.bdqn.dao.personDao . getList”

7.执行sql语句 通过sql id找到语句

List list=sqlSession.selectList(sqlId);

8.输出结果

mybatis基本步骤:

1.加入maven依赖

2.创建dao接口 定义了操作数据库方法

3.创建mapper文件也叫做sql映射文件 写sql语句的和接口中的方法对应的sql语句

4.创建mybatis的主配置文件 1.连接数据库 2.指定mapper文件的位置路径

5.使用mybatis对象 sqlsession通过他的不同方法执行sql语句

mybatis示例
                     
//访问mybatis读取person数据  
//步骤:定义mybatis主配置文件的名称 从类路径的根开始(target/classes)     
String config="myBatis.xml";//        
//读取这个config表示的文件    
  InputStream  in=Resources.getResourceAsStream(config);
//创建sqlSessionFactoryBuilder对象     
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
//创建sqlSessionFactory对象       
SqlSessionFactory factory= builder.build(in);           
//   获取sqlSession对象  从sqlSessionFactory中获取 (重要)      
SqlSession sqlSession=factory.openSession();
// 指定要执行的sql语句的标识 sql映射文件中的nameSpace+"."+"标签id值" (重要)       
 String sqlId="com.bdqn.dao.PersonDao.getList";//         
//   执行sql语句通过sqlId找到语句       
 List<Person>  pp=sqlSession.selectList(sqlId);//          
//   输出结果  
    for (Person p:pp){   
  System.out.println("查询的Person"+p);    
}     
sqlSession.close();

添加操作:

1.在dao接口添加执行的接口

2.在sql映射文件中添加

inser into person(id,name,sex,pass)values(#{id},#{name},#{sex},#{pass});

使用#{}相当于占位符 其实就是JDBC的PreparedStatement中的??? 可以防止sql注入

#{}里的值必须和实体类的一致 例#{age}这个过程是通过反射来做的

**${}一般会用于在模糊查询的情况**
 </insert>

**一张表 一般一个sql映射文件 弄好之后在主配置文件中添加sql映射文件的路径信息**

创建使用mybatis类 有固定模板 只需把执行的sql标识更改要执行的方法即可 然后创建一个数据对象  sqlsession调用insert(sql标识,obj)返回整数 一定要开启提交事务 sqlSession.commit();

mybatis不会默认提交事务 所以在增删改操作后要手动提交事务sqlSession.commit();或者在获取sqlSession对象的时候factory调用的openSession()参数为true时 自动提交事务

事务

开启日志可得到执行的过程信息 把日志打印到控制台

<!--    settings:控制mybatis全局行为-->         -----打印日志--------
<settings>
<!--设置mybatis输出日志-->       
<setting name="logImpl" value="STDOUT_LOGGING"/>  
</settings>

sqlSession

执行的操作方法不同返回的类型也不同

增删改 返回的是整数型,查询的话 返回的可以是单个对象或者集合

查询select(sql标识)

添加insert(sql标识,传的数据对象)

主要类的介绍:

1.配置文件

Resource:mybatis中的一个类 负责读取主配置文件

String config=“myBatis.xml”; InputStream in= Resources.getResourceAsStream(config);

  1. sqlSessionFactoryBuilder:创建
    SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
    sqlSessionfactory factory= builder.build(in);

3.sqlSessionFactory:重量级对象 创建对象耗时较长 使用资源较多 在整个项目中有一个就够用了

sqlSessionFactory接口:接口实现类defaultsqlSessionFactory()

作用:获取sqlsession对象

sqlSession sqlsession=factort.openSession();

sqlsession是一个接口 定义了操作数据库的方法 例如

selectone()

selectList()

insert()

update()

delete()

commit()

rollback

使用sqlsession对象不是线程安全的 需要在方法内部中使用 在执行sql语句之前 使用opensession()

获取session() 在执行完成后关闭它sqlsession.close() 可以保证它使用是线程安全的

把重复的操作封装MybatisUtils工具类

由于sqlSessionFactory() 是重量级对象 占用资源大 所以一个项目只需创建一个就够用了 所以把创建它放在静态代码块中 只能被执行一次 不用每次调用都新new一个 浪费资源 在虚拟机加载类的时候被执行 且只执行一次

静态代码块:

static {

}

它是随着类的加载机制而执行且只执行一次 并优先于主函数 在代码块设计中可用来创建单例的对象 不用每次调用一个相同的对象的时候都要重新new一次 提高性能

                        mybatis封装的工具类
//由于sqlSessionFactory是重量级对象 创建对象时候耗时较长 占用资源较大 整个项目只有一个就够用了    //不用每次调用时候都要new一个新的 所以把它放在静态代码块中在虚拟机加载类的时候就会被执行 而且只执行一次  
  private static SqlSessionFactory factory=null;  
  static {       
  //访问mybatis读取person数据      
  //步骤:①定义mybatis主配置文件的名称 从类路径的根开始(target/classes)        String config="myBatis.xml";   
   //2.读取这个config表示的文件      
  try {          
  InputStream in= Resources.getResourceAsStream(config);            SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
  // 创建sqlSessionFactory对象           
  factory= builder.build(in); 
  //重量级对象 创建对象耗时较长 使用资源较多 整个项目有一个就够用了   
  } catch (IOException e) {      
  e.printStackTrace();        
  }     
   //创建sqlSessionFactoryBuilder对象   
  }    
  /实现功能描述:获取sqlSession的方法   
  *@param   
  *@return org.apache.ibatis.session.SqlSession  
  */  
  public static SqlSession getSqlSession(){ 
  SqlSession sqlSession=null;      
  if(factory!=null){       
  //获取sqlSession对象,从sqlSessionFactory中获取              sqlSession=factory.openSession();//非自动提交事务 参数为true时候 自动提交事务        }       
  return sqlSession; 
  }

mybatis是封装的JDBC操作

使用#{}后 mybatis执行sql是使用JDBC中的prepareStatement对象 由mybatis执行下面代码mybatis创建Connection prepareStatement Result 对象
String sql=“select * from person where id=?;
prepareStatement pst=conn.prepareStatement(sql);
pst.setInt(15);

mybatis中当需要传多个参数的时候 不能直接传 需要使用命名参数或者对象参数传递

命名参数:

多个参数时候 使用@param命名参数 在形参定义的前面加入 @Param(“自定义的参数名称”)

例如: List list(@Param("myId) Integet id,@Param(“myName”) String name);

List<Person> personList
(@Param("myId") Integer id,@Param("myPass") String pass);

mapper文件中 多个参数时候 使用@Param命名

例如:select * from person where id=#{“myId”} or name=#{“myName”);

<select id="personList" resultType="com.bdqn.entity.Person">   
    select  * from person where id=#{myId} or pass=#{myPass};
</select>

操作完成之后 sqlsession.close();

两个占位符的比较

**#**占位符 告诉mybatis使用实际的参数值代替并使用 PreparStatement对象执行sql语句#{}代替sql语句的? 避免sql注入 这样做更安全 更迅速 也是首选的做法

** ∗ ∗ 字符串替换告诉 m y b a t i s 使用 **字符串替换 告诉mybatis使用 ∗∗字符串替换告诉mybatis使用中包含的 '字符串’替换所在的位置 使用statement把sql语句和${}的内容连接起来的 执行效率低 $有sql注入的风险 缺乏安全性主要用在替换表名字 列名 不同列排序等的操作

$可以替换表名或者列名 能确定数据是安全的情况下 可以使用 select*from person order by id{id} 和#区别===》

**#**①#使用了占位符在sql语句中占位的使用prepareStatement执行sql效率高

②使用#避免sql注入 更安全

**$**1. $不使用占位符 是字符串连接的方式 使用statement对象 执行sql效率低

2.$有sql注入的风险 缺乏安全性

3.$用于替换表名或者列名

执行的时候: $==>select * from person order by id asc

#==>select * from person order by ?asc

ASC升序 DESC降序

排序的时候使用 #{} 则无效果

用 $来替换表名或者列名reultType:的使用resultType的结果类型 可以是全限定名称或者别名

mybatis的输出结果 mybatis执行了sql语句 得到Java对象

resultType结果类型 指sql语句执行完毕后 数据转化为Java的对象

定义别名:若查询总条数或处理接收返回结果的类型 即int类型的

使用全限定名称 java.lang.Interger或者别名int

<select id="countList" reultType="java.lang.Interget"> 全限定名称
 或者   
<select id="countList" resultType="int"> 别名

定义自定类的别名的几种方式 (主要用全限定名称 --安全)

 1.<typeAliases>   

  type:自定义类型的全限定名称

 alias:别名 

<typeAlias type="com.bdqn.entity.Person" alias="ps">

</typeAliases>
     
 2.<package>   
   <package> name是包名 这个包中所有类 类名就是别名(类名不区分大小写)
     <package name="com.bdqn.entity"/>
缺点: 若不同包下的相同类名 则会报错-->重复      这个<package>也放在<typeAliases>

查询返回map 1.列名是map的key 2. 列值是map的value返回map的时候 数据只能是一行的记录 多于一行 会报错resultType:叫做结果映射类型指定列名和java对象属性的对应关系 resultType已帮我们映射好

resultMap:

1.当自定义列值赋给哪个属性 2.当列名和属性名不一致的时候 一定使用resultMap

设置结果映射类型

while(res.next){

person.setId(res.getInt(“id”)) mybatis进行自动进行操作的时候 属性和列名必须一致 不然赋值不上因为mybatis是通过映射自动完成赋值的

}

映射到JavaBean的简单类型属性

            自定义列值赋给哪个属性
<resultMap id="personMap" type="com.bdqn.entity.Person"> id是自定义名称
   <!--        主键列用  <id column="id" property="id"/>-->    
   <id column="id" property="id"/> ====
   <!--        非主键列用  <result column="useName" property="name"/>-->
   <!--        column 是字段名 property 是对应的属性名-->   
   <result column="useName" property="useName"/>    ===
   <result column="sex" property="sex"/>      ===
   <result column="pass" property="pass"/>    ===
   </resultMap>
   <!--   ② 设置结果映射类型-->    
   <select id="personListSame" resultMap="personMap">这里填的自定义的名称
   SELECT id,useName,sex,pass FROM person 
   </select>

association 一对一

javaType:完整Java类名或别名。 property:映射数据库列的实体对象的属性。 id。设置该项可以有效地提升MyBatis的性能。 result。property:映射数据库列的实体类对象的属性。 column:数据库列名或别名

  <resultMap id="billProvideResult" type="com.bdqn.entity.Bill" >   
  <id property="id" column="id"/>    
  <result property="billCode" column="billCode"/>      
  <result property="productName" column="productName"/>    
  <result property="totalPrice" column="totalPrice"/>   
  <result property="isPayment" column="isPayment"/>      
  <!--property是在实体类中的属性名称 javaType是属性的数据类型-->
      <!--        通过association进行provider对象赋值 -->       
  <association property="provider" javaType="com.bdqn.entity.Provider">            <id column="id" property="id"/>         
  <result column="proCode" property="proCode"/>          
  <result column="proName" property="proName"/>      
  <result column="proContact" property="proContact"/>     
  <result column="proPhone" property="proPhone"/>    
  </association>  
  </resultMap>

association仅处理一对一的关联关系。

association复用

association提供了另一个属性:resultMap。通过这个属性可以扩展一个resultMap来进行联合映射

<resultMap id="Repeat" type="com.bdqn.entity.Provider">通过id 进行Provider结果映射复用
<id column="id" property="id"/>  
<result column="proCode" property="proCode"/>
<result column="proName" property="proName"/>   
<result column="proContact" property="proContact"/>  
<result column="proPhone" property="proPhone"/>
</resultMap>

collection一对多

<resultMap id="billProvideResult" type="com.bdqn.entity.Bill" >   
  <id property="id" column="id"/>    
  <result property="billCode" column="billCode"/>      
  <result property="productName" column="productName"/>    
  <result property="totalPrice" column="totalPrice"/>   
  <result property="isPayment" column="isPayment"/>   
    //嵌套一个复杂数据类型(集合)属性进行赋值
    <collection property="java类中集合的名字" ofType="java类中集合中的实体全类名">
       <id property="" cloumn=""/>
       <result property="" cloumn=""/>
     </collection>
 </resultMap>

resultType和resultMap不可以一起用

当不使用mybatis执行查询操作的时候 实体类可以不与表中列名一致 因为是手动进行赋值的

使用mybatis执行查询操作的时候实体类属性则必须和列名一致 因为mybatis是通过映射自动完成赋值操作的

mybatis会根据查询的列名 将列名转化为小写 去进行设置(列名setter方法 ),如果匹配不到则为空值

1.通过设置别名和Java实体类的属性名一致

<select id="personListSame" resultType="com.bdqn.entity.Person">实体类的全限定名称    
    SELECT id,useName as name,sex,pass FROM person
    </select>

2.设置结果映射类型

   <resultMap id="personMap" type="com.bdqn.entity.Person">
<!--        主键列用  <id column="id" property="id"/>-->
        <id column="id" property="id"/>   ===
<!--        非主键列用  <result column="useName" property="name"/>-->
<!--        column 是字段名 property 是对应的实体类属性名-->
        <result column="useName" property="name"/>      ===
        <result column="sex" property="sex"/>     ===
        <result column="pass" property="pass"/>  ===
    </resultMap>
    <select id="personListSame" resultMap="personMap"> 和命名的一样
        SELECT id,useName,sex,pass FROM person
    </select>

Like的三种方式

方法一:直接传参法(手动拼接)

public void select1(String name) {
      String name = "%" + name+ "%";
      UserDao.getUserByName(name);
 }

Dao层

// Dao层指定参数别名
List<User> getUserByName(@Param("name") String name);

mapper.xml层:

   <select id="getUserByName"  resultMap="result">
       SELECT  * FROM t_user  WHERE  name LIKE #{name}
    </select>

注意:此方法可以完成模糊查询任务,但是不推荐,因为与Java代码耦合了,不利于维护和修改

方法二: mysql的CONCAT()函数

List<User> getUserByName(@Param("keyword") String keyword);

mapper.xml层:

name LIKE CONCAT(‘%’,#{name},‘%’)

<select id="getUserByName"  resultMap="result">
   SELECT  * FROM t_user  WHERE  name LIKE CONCAT('%',#{name},'%')
</select>

name like “%”#{name}“%”

<select id="getUserByName"  resultMap="result">
   SELECT  * FROM t_user  WHERE  name like "%"#{name}"%"
</select>

name like “%”${name}“%”( 不建议可能会产生sql注入)

<select id="getUserByName"  resultMap="result">
   SELECT  * FROM t_user  WHERE  name like "%"${name}"%"
</select>

注意:执行效果与上面的直接传参一样,但比直接传参法更好,因为与代码解耦了,但是此方法只针对mysql数据库所用,如果换成Oracle数据库则需要修改sql代码,因此此方法不通用,但可以使用

方法三:Mybatis的bind绑定

mybatis针对这种情况作出了调整,提供了一个bind元素,此元素可以跨越数据库限制,也就是说不管是mysql还是Oracle数据库都可以通用模糊查询。

Dao接口:

List<User> getUserByName(@Param("keyword") String keyword);

mapper.xml层:

<select id="getUserByName"  resultMap="result">
<!--name就是传入的参数,bind相当于是一个参数,这个参数的值就是value拼接出来的值-->
    <bind name="pattern" value="'%' + name + '%'" />    
    SELECT * FROM t_user WHERE name LIKE #{pattern}
    OR id LIKE #{pattern}    
</select>

MyBatis 动态SQL

MyBatis 采用功能强大的基于 OGNL 的表达式来简化操作

if

choose (when, otherwise)

trim(where, set)

foreach

If,Where

Where用于解决SQL语句中where关键字以及条件中第一个and或者or的问题

<select id="getEmpsByConditionIf" resultType="com.atg.mybatis.beans.Employee">
select id , last_name ,email  , gender  
		from tbl_employee 
		<where>
			<if test="id!=null">
				and id = #{id}
			</if>
			<if test="lastName!=null &amp;&amp; lastName!=&quot;&quot;">
				and last_name = #{lastName}
			</if>
			<if test="email!=null and email.trim()!=''">
				and email = #{email}
			</if>
			<if test="&quot;m&quot;.equals(gender) or &quot;f&quot;.equals(gender)">
				and gender = #{gender}
			</if>
		</where>
</select>

trim

可以在条件判断完的SQL语句前后 添加或者去掉指定的字符

prefix: 添加前缀

prefixOverrides: 去掉前缀

suffix: 添加后缀

suffixOverrides: 去掉后缀

<select id="getEmpsByConditionTrim" resultType="com.atg.mybatis.beans.Employee">
		select id , last_name ,email  , gender  
		from tbl_employee 
		<trim prefix="where"  suffixOverrides="and">
			<if test="id!=null">
				 id = #{id} and
			</if>
			<if test="lastName!=null &amp;&amp; lastName!=&quot;&quot;">
				 last_name = #{lastName} and
			</if>
			<if test="email!=null and email.trim()!=''">
				 email = #{email} and
			</if>
			<if test="&quot;m&quot;.equals(gender) or &quot;f&quot;.equals(gender)">
				gender = #{gender}
			</if>
		</trim>
</select>

set

<update id="updateEmpByConditionSet">
		update  tbl_employee  
		<set>
			<if test="lastName!=null &amp;&amp; lastName!=&quot;&quot;">
				 last_name = #{lastName},
			</if>
			<if test="email!=null and email.trim()!=''">
				 email = #{email} ,
			</if>
			<if test="&quot;m&quot;.equals(gender) or &quot;f&quot;.equals(gender)">
				gender = #{gender} 
			</if>
		</set>
		 where id =#{id}
	</update>

choose(when、otherwise)

  1. choose 主要是用于分支判断,类似于java中的switch case,只会满足所有分支中的一个
select id="getEmpsByConditionChoose" resultType="com.atg.mybatis.beans.Employee">
		select id ,last_name, email,gender from tbl_employee
		<where>
			<choose>
				<when test="id!=null">
					id = #{id}
				</when>
				<when test="lastName!=null">
					last_name = #{lastName}
				</when>
				<when test="email!=null">
					email = #{email}
				</when>
				<otherwise>
					 gender = 'm'
				</otherwise>
			</choose>
		</where>
</select>

foreach

  1. foreach 主要用于循环迭代

collection: 要迭代的集合

item: 当前从集合中迭代出的元素

open: 开始字符

close:结束字符

separator: 元素与元素之间的分隔符

index:

​ 迭代的是List集合: index表示的当前元素的下标

​ 迭代的Map集合: index表示的当前元素的key

<select id="getEmpsByConditionForeach" resultType="com.atg.mybatis.beans.Employee">
		 select id , last_name, email ,gender from tbl_employee where  id in 
		 <foreach collection="ids" item="curr_id" open="(" close=")" separator="," >
		 		#{curr_id}
		 </foreach>
</select>

sql

sql 标签是用于抽取可重用的sql片段,将相同的,使用频繁的SQL片段抽取出来,单独定义,方便多次引用.

抽取SQL:

<sql id="selectSQL">
		select id , last_name, email ,gender from tbl_employee
</sql>

引用SQL:

<include refid="selectSQL"></include>

MyBatis 缓存机制

1.MyBatis系统中默认定义了两级缓存

一级缓存

二级缓存

2.默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启。

3.二级缓存需要手动开启和配置,他是基于namespace级别的缓存。

4.为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

一级缓存的使用

1.一级缓存(local cache), 即本地缓存, 作用域默认为sqlSession。当 Session flush 或 close 后, 该 Session 中的所有 Cache 将被清空。

2.本地缓存不能被关闭, 但可以调用 clearCache() 来清空本地缓存, 或者改变缓存的作用域.

3.可以配置本地缓存的作用域. 在 mybatis.xml 中配置

2
2

4.一级缓存的工作机制

同一次会话期间只要查询过的数据都会保存在当前SqlSession的一个Map中

key: hashCode+查询的SqlId+编写的sql查询语句+参数

一级缓存失效的几种情况

1.不同的SqlSession对应不同的一级缓存

  1. 同一个SqlSession但是查询条件不同

  2. 同一个SqlSession两次查询期间执行了任何一次增删改操作

    4.同一个SqlSession两次查询期间手动清空了缓存

二级缓存的使用

1.二级缓存(second level cache),全局作用域缓存

2.二级缓存默认不开启,需要手动配置

3.MyBatis提供二级缓存的接口以及实现,缓存实现要求POJO实现Serializable接口

4.二级缓存在 SqlSession 关闭或提交之后才会生效

5.二级缓存使用的步骤:

  1. 全局配置文件中开启二级缓存

    2.需要使用二级缓存的映射文件处使用cache配置缓存

  2. 注意:POJO需要实现Serializable接口

6.二级缓存相关的属性

  1. eviction=“FIFO”:缓存回收策略:

    LRU – 最近最少使用的:移除最长时间不被使用的对象。

    FIFO – 先进先出:按对象进入缓存的顺序来移除它们。

    SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。

    WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

    默认的是 LRU。

    2.flushInterval:刷新间隔,单位毫秒

    默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新

  2. size:引用数目,正整数

    代表缓存最多可以存储多少个对象,太大容易导致内存溢出

  3. readOnly:只读,true/false

    true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。

    false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。

缓存的相关属性设置

​ 1.全局setting的cacheEnable:

​ 配置二级缓存的开关,一级缓存一直是打开的。

​ 2.select标签的useCache属性:

​ 配置这个select是否使用二级缓存。一级缓存一直是使用的

​ 3.sql标签的flushCache属性:

​ 增删改默认flushCache=true。sql执行以后,会同时清空一级和二级缓存。

​ 查询默认 flushCache=false。

​ 4.sqlSession.clearCache():只是用来清除一级缓存。

PageHelper 分页插件

  1. 在MyBatis全局配置文件中配置分页插件

    <plugins>
    	<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>
    

2.Page对象的使用

1.在查询之前通过PageHelper.startPage(页码,条数)设置分页信息,该方法返回Page对象

@Test
	public void testPageHelper()  throws Exception{
		SqlSessionFactory ssf = getSqlSessionFactory();
		SqlSession session = ssf.openSession();
		try {
			EmployeeMapper mapper = 
                      session.getMapper(EmployeeMapper.class);
			//设置分页信息
			Page<Object> page = PageHelper.startPage(9, 1);
			List<Employee> emps = mapper.getAllEmps();
			for (Employee employee : emps) {
				System.out.println(employee);
			}
			System.out.println("=============获取分页相关的信息=================");
			System.out.println("当前页: " + page.getPageNum());
			System.out.println("总页码: " + page.getPages());
			System.out.println("总条数: " + page.getTotal());
			System.out.println("每页显示的条数: " + page.getPageSize());
		} finally {
			session.close();
		}
	}

2.在查询完数据后,使用PageInfo对象封装查询结果,可以获取更详细的分页信息以及

可以完成分页逻辑

@Test
	public void testPageHelper1()  throws Exception{
		SqlSessionFactory ssf = getSqlSessionFactory();
		SqlSession session = ssf.openSession();
		try {
			EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
			//设置分页信息
			Page<Object> page = PageHelper.startPage(9, 1);
			List<Employee> emps = mapper.getAllEmps();
			// 
			PageInfo<Employee> info  = new PageInfo<>(emps,5);
			for (Employee employee : emps) {
				System.out.println(employee);
			}
			System.out.println("=============获取详细分页相关的信息=================");
			System.out.println("当前页: " + info.getPageNum());
			System.out.println("总页码: " + info.getPages());
			System.out.println("总条数: " + info.getTotal());
			System.out.println("每页显示的条数: " + info.getPageSize());
			System.out.println("是否是第一页: " + info.isIsFirstPage());
			System.out.println("是否是最后一页: " + info.isIsLastPage());
			System.out.println("是否有上一页: " + info.isHasPreviousPage());
			System.out.println("是否有下一页: " + info.isHasNextPage());
			
			System.out.println("============分页逻辑===============");
			int [] nums = info.getNavigatepageNums();
			for (int i : nums) {
				System.out.print(i +" " );
			}
		} finally {
			session.close();
		}
	}