映射文件是一个封装SQL指令的文件,在程序执行中会来执行里面的SQL指令,进行数据的操作,基本上避免了所有的JDBC代码。

顶级元素有:

  • cache – 配置给定命名空间的缓存。
  • cache-ref – 从其他命名空间引用缓存配置。
  • resultMap – 最复杂,也是最有力量的元素,用来描述如何从数据库结果集中来封装实体对象,当数据表中的字段与实体类中的属性名不一致时才使用。 
  • parameterMap – 已经被废弃了!老式风格的参数映射。内联参数是首选,这个元 素可能在将来被移除。这里不会记录。
  • sql – 可以重用的 SQL ,也可以被其他语句引用。
  • insert – 映射插入语句
  • update – 映射更新语句
  • delete – 映射删除语句
  • select – 映射查询语句

select : SQL指令是查询时使用此元素,自动会把查询的数据行封装成指定的实体对象。id属性是唯一的,与数据持久化接口的方法名一致,在调用时MyBatis自动查找到。

parameterType:属性表示执行SQL指令时传入的数据类型,可以是基本数据类型,也可以是对象。

resultType:指定查询时的数据行封装成某个类型的对象。#{id}是一个占位符,相关于JDBCSQL语句中的 “?”,在执行时会把传入的参数代入到占位符中。占位符里面的id名称随意,与持久化接口方法中的形参名称无关。

<select id="selectPerson" parameterType="int" resultType="hashmap">

  SELECT * FROM PERSON WHERE ID = #{id}</select>

resultMap:数据行中的字段名称与要封装的实体对象的属性名称不一致时,则用此元素来做数据行与实体对象的映射。

<!-- 当实体类中的属性名与数据行中的字段名称不一致时,用映射来处理 -->

<resultMap type="Admin" id="adminMap">

   <!-- id只是表明这一列是主键,作用于result是一样的。column:是数据行中的列名,property:实体类的属性名 -->

  <id column="no" property="id"/>

  <result column="loginId" property="loginId"/>

  <result column="loginPwd" property="loginPwd"/>

  <result column="isAdmin" property="isAdmin"/>

</resultMap>

<!-- 持久化接口的方法名与SQL指令映射的ID值一致  Admin是实体的别名,建议,一个元素中用了resultType属性则不要再用resultMap属性。 -->

<select id="getAdmins" resultMap="adminMap">

 select * from admin

</select>

 statementType="CALLABLE" 属性值表示执行的是存储过程,默认是STATEMENT,执行普通的SQL指令。

insert, update and delete

这几个顶级元素分别用于执行插入,修改,删除的SQL时使用,都默认返回数据库的受影响行数,返回值是一个Int型。

元素中的相关属性如下:

<insert

  id="insertAuthor"

  parameterType="domain.blog.Author"

  flushCache="true"

  statementType="PREPARED"

  keyProperty=""

  keyColumn=""

  useGeneratedKeys=""

  timeout="20">

<update

  id="insertAuthor"

  parameterType="domain.blog.Author"

  flushCache="true"

  statementType="PREPARED"

  timeout="20">

<delete

  id="insertAuthor"

  parameterType="domain.blog.Author"

  flushCache="true"

  statementType="PREPARED"

  timeout="20">

insert中,有两个属性配合使用(keyProperty="id" useGeneratedKeys="true"),可以获取到这个插入语句执行时所产生的自动增长值。什么情况下需要这样使用?比如说下一个订单,先向订单表插入一条数据:形成订单,再根据订单编号向订单详细表插入订单所订购的商品列表。

 

注意,在MyBatis中,增删改必须要对SqlSession进行事务的提交,

 

sql元素:用于代码重用时。

 

字符串替换:MyBatis中,占位符用#{}${},但是这两种写法有本质的区别。${}在动态解析的时候,会将我们传入的参数当做String字符串填充到我们的语句中,#{} 在动态解析的时候, 会解析成一个参数标记符,使用#可以有效避免SQL注入攻击。

高级映射:主外关联,当在数据表中有外键列的处理方法,实体对象中存储的外键对象。

如果是连接查询则处理如下:

<mapper namespace="com.icss.dao.StudentDao">

   <resultMap type="Student" id="studentMap">

      <id column="studentNo" property="studentNo"/>

      <result column="name" property="name"/>

      <result column="sex" property="sex"/>

      <result column="address" property="address"/>

      <result column="age" property="age"/>

      <result column="phone" property="phone"/>

      <result column="email" property="email"/>

      <result column="birthday" property="birthday"/>

      <!-- 年级是一个对象,而不是年级编号,如何处理 -->

      <association property="grade" javaType="Grade">

        <result column="gradeId" property="gradeId"/>

        <result column="gradeName" property="gradeName"/>

      </association>

   </resultMap>   

   <select id="getStudents" resultMap="studentMap">

     select * from student s inner join grade g on g.gradeId=s.gradeId

   </select>

</mapper>

如果是查询外部查询:推荐用法

<mapper namespace="com.icss.dao.StudentDao">

   <resultMap type="Student" id="studentMap">

      <id column="studentNo" property="studentNo"/>

      <result column="name" property="name"/>

      <result column="sex" property="sex"/>

      <result column="address" property="address"/>

      <result column="age" property="age"/>

      <result column="phone" property="phone"/>

      <result column="email" property="email"/>

      <result column="birthday" property="birthday"/>

      <!-- 年级是一个对象,而不是年级编号,如何处理

        property:查询出的对象保存在此属性中,column:是外键列,要根据此列中的数据做查询条件,select:属性是要执行查询的方法

      -->

      <association property="grade" column="gradeId" select="com.icss.dao.GradeDao.getGradeById">        

      </association>

   </resultMap>   

   <!-- 字段与属性名不一致,不能用resultType属性 -->

   <select id="getStudents" resultMap="studentMap">

     select * from student

   </select>

</mapper> 

需求:获取到一个年级对象,同时要展示此年级下的所有学生信息。这个需求是比较常见的,比如说,登录后,可以看到此用户所有的订单信息,打开一个订单,则可以看到此订单的所有订购商品,

private int gradeId;

private String gradeName;

//映射:一对多的映射

private List<Student> students;

  

映射文件的处理:

<resultMap type="Grade" id="gradeMap">

     <id column="gradeId" property="gradeId"/>

     <result column="gradeName" property="gradeName"/>

     <!-- 根据数据行中的年级编号去获取到此年级下的所有学生对象

        property:返回的集合存储的属性名,

        column:查询的条件值,也就是主键值

        select:要调用的查询方法。

      -->

     <collection property="students" column="gradeId"

        select="com.icss.dao.StudentDao.getStudentByGID">        

     </collection>

  </resultMap>

 

MyBatis的日志处理

Mybatis内置的日志工厂提供日志功能,具体的日志实现有以下几种方式:

  • SLF4J
  • Apache Commons Logging
  • Log4j 2
  • Log4j
  • JDK logging

具体选择哪个日志实现由MyBatis的内置日志工厂确定。它会使用最先找到的(按上文列举的顺序查找)。 如果一个都未找到,日志功能就会被禁用。

常用是的Log4j。配置方法步骤:

  1. 添加log4jjar
  2. 在项目的根目录下(src目录)添加log4j的配置文件:log4j.properties 文件名要注意
  3. 编写log4j.properties内容,显现日志信息

 

缓存

MyBatis有缓存机制分两种:一级缓存,默认是打开的,二级缓存,需要简单配置。

缓存的作用是:临时存储数据在客户端,减轻服务端的压力。

一级缓存默认是打开的,以键值对的形式存储了查询的结果。键是SQL指令,值是查询的结果集。但是一级缓存是SqlSession会话级别的,会话关闭,则缓存自动清除。

二级缓存: 是高于会话级别的缓存,会话关闭,缓存不会被清除,其他会话中是可以使用的。

要开启二级缓存,你需要在你的 SQL 映射文件中添加一行:

<cache/>

字面上看就是这样。这个简单语句的效果如下:

映射语句文件中的所有 select 语句将会被缓存。

映射语句文件中的所有 insert,update delete 语句会刷新缓存。

缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。

根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序 来刷新。

缓存会存储列表集合或对象(无论查询方法返回什么)1024 个引用。

缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而 且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。 

注意:二级缓存对于要缓存的对象及对象中所引用的对象必须进行序列化操作。

存储过程的执行:

MyBatis,是可以执行过程的,当需要执行过程时,必须在映射文件的元素中设置属性:

statementType="CALLABLE",默认值是:STATEMENT

无参数的过程案例:

<!-- 执行存储过程,CALLABLE执行过程 -->

<select id="getStudents" resultMap="studentMap" statementType="CALLABLE">

  {call sp_studentAll()}

</select> 

 

带输入参数的过程执行案例:

<!-- parameterType="map"方法调用时传入的参数封装在map集合中,#{sex}根据集合中的键取值 -->

<select id="findBySex" parameterType="map" resultMap="studentMap" statementType="CALLABLE">

     call p_stuBySex(#{sex})

</select>

方法调用时处理:

@Override

public List<Student> findBySex(String sex) {

//封装访求调用时所需要的参数

Map<String, String> map = new HashMap<>();

map.put("sex", sex);

//执行MyBatis查询操作

SqlSession sqlSession = SqlSessionFactoryUtil.getsqlSession();

List<Student> list = null;

try {

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

list=dao.findBySex(map); //调用方法,传入参数

} catch (Exception e) {

System.out.println(e.getMessage());

}finally {

 sqlSession.close();  //关闭会话

}

return list;

}

带输出参数的案例:

过程:

 

映射文件处理:

  <!-- c: 表示参数名称   mode=OUT 表示是输出参数  -->

   <select id="getCount" parameterType="map" statementType="CALLABLE">

      {call p_stuCount(#{c,mode=OUT,jdbcType=INTEGER})}

   </select>

业务层代码:

Map<String, Integer> map = new HashMap<>();

map.put("c", 0);

dao.getCount(map);  //调用方法,执行了存储过程,传入了map集合

//执行中返回值存储在map集合中

System.out.println("学生总人数为:"+map.get("c"));

MySQL 数据库中还有输入输出型参数,不建议用。容易出错。