简介

  • MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
  • mybatis与hibernate不同
    不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句。mybatis可以通过XML或注解方式灵活配置要运行的sql语句,并将java对象和sql语句映射生成最终执行的sql,最后将sql执行的结果再映射生成java对象。
  • Mybatis学习门槛低,程序员直接编写原生态sql,可严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大。

jdbc编程问题

  1. 频繁创建、关闭数据连接,太消耗资源
  2. Sql语句存在硬编码,不利于维护
  3. Sql参数设置硬编码,不利于维护
  4. 结果集获取与遍历复杂,存在硬编码,不利于维护,期望能够查询后返回一个java对象

MyBatis

  1. 使用数据库连接池。
  2. 将Sql语句配置在XXXXmapper.xml文件中与java代码分离。
  3. Mybatis自动将java对象映射至sql语句,通过statement中的parameterType定义输入参数的类型。
  4. Mybatis自动将sql执行结果映射至java对象,通过statement中的resultType定义输出结果的类型。

JDBC代码

  public static void main(String[] args) {
     Connection connection = null;
     PreparedStatement preparedStatement = null;
     ResultSet resultSet = null;
 
     try {
       // 加载数据库驱动
       Class.forName("com.mysql.jdbc.Driver");
 
       // 通过驱动管理类获取数据库链接
       connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "root");
       // 定义sql语句 ?表示占位符
       String sql = "select * from user where username = ?";
       // 获取预处理statement
       preparedStatement = connection.prepareStatement(sql);
       // 设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
       preparedStatement.setString(1, "王五");
       // 向数据库发出sql执行查询,查询出结果集
       resultSet = preparedStatement.executeQuery();
       // 遍历查询结果集
       while (resultSet.next()) {
          System.out.println(resultSet.getString("id") + "  " + resultSet.getString("username"));
       }
     } catch (Exception e) {
       e.printStackTrace();
     } finally {
       // 释放资源
       if (resultSet != null) {
          try {
            resultSet.close();
          } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
       }
       if (preparedStatement != null) {
          try {
            preparedStatement.close();
          } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
       }
       if (connection != null) {
          try {
            connection.close();
          } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
       }
     }
  }

{ }和${ }

  • #{}:占位符,可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换。#{}可以有效防止sql注入。 #{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值,#{}括号中可以是value或其它名称。
  • \${}:拼接sql串,将parameterType 传入的内容拼接在sql中且不进行jdbc类型转换, \${}可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,\${}括号中只能是value。

SqlMapConfig.xml配置文件

  1. SqlMapConfig.xml中配置的内容和顺序如下:

    properties(属性)
    settings(全局配置参数)
    typeAliases(类型别名)
    typeHandlers(类型处理器)
    objectFactory(对象工厂)
    plugins(插件)
    environments(环境集合属性对象)
      environment(环境子属性对象)
         transactionManager(事务管理)
         dataSource(数据源)
    mappers(映射器)
  2. 自定义别名:

    <typeAliases>
         <!-- 单个别名定义 -->
         <typeAlias alias="user" type="cn.mybatis.pojo.User" />
         <!-- 批量别名定义,扫描整个包下的类,别名为类名(大小写不敏感) -->
         <package name="cn.mybatis.pojo" />
         <package name="其它包" />
    </typeAliases>
  3. mappers(映射器)

    使用相对于类路径的资源

    <mapper resource="sqlmap/User.xml" />

    使用mapper接口类路径。注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。

    <mapper class="cn.mybatis.mapper.UserMapper"/> 

    注册指定包下的所有mapper接口。注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。

    <package name="cn.mybatis.mapper"/>

输出映射

  <!-- resultMap最终还是要将结果映射到pojo上,type就是指定映射到哪一个pojo -->
  <!-- id:设置ResultMap的id -->
  <resultMap type="order" id="orderResultMap">
    <!-- 定义主键 ,非常重要。如果是多个字段,则定义多个id -->
    <!-- property:主键在pojo中的属性名 -->
    <!-- column:主键在数据库中的列名 -->
    <id property="id" column="id" />
 
    <!-- 定义普通属性 -->
    <result property="userId" column="user_id" />
    <result property="number" column="number" />
    <result property="createtime" column="createtime" />
    <result property="note" column="note" />
    <association property="author" javaType="Author">
      <result property="username" column="author_username"/>
    </association>
    <collection property="posts" ofType="Post">
      <result property="subject" column="post_subject"/> 
    </collection>
    <discriminator javaType="int" column="vehicle_type">
      <case value="1" resultType="carResult">
        <result property="doorCount" column="door_count" />
      </case>
      <case value="2" resultType="truckResult">
        <result property="boxSize" column="box_size" />
        <result property="extendedCab" column="extended_cab" />
      </case>
    </discriminator>
  </resultMap>
 
  <!-- 查询所有的订单数据 -->
  <select id="queryOrderAll" resultMap="orderResultMap">
    SELECT id, user_id,
    number,
    createtime, note FROM `order`
  </select>

动态sql

if语句

if条件判断语句可以直接执行对象的方法

<!-- 根据条件查询用户 -->
<select id="queryUserByWhere" parameterType="user" resultType="user">
  SELECT id, username, birthday, sex, address FROM `user`
  WHERE 1=1
  <if test="sex != null and sex != ''">
    AND sex = #{sex}
  </if>
  <if test="username != null and username != ''">
    AND username LIKE
    '%${username}%'
  </if>
</select>

数字类型

gt 对应 >
gte 对应 >=
lt 对应 <(会报错 相关联的 "test" 属性值不能包含 '<' 字符)
lte 对应 <=(会报错 相关联的 "test" 属性值不能包含 '<' 字符)

<if test="id != null">
<if test='id != null and id > 28'>
<if test='id != null and id gt 28'>

字符串

eq 对应 ==
neq 对应 !=

<if test="name != null and name.trim() != ''">
<if test="name != null and name.indexOf('ji') == 0">  <!-- 是否以什么开头 -->
<if test="name != null and name.indexOf('ji') >= 0">  <!-- 是否包含某字符 -->
<if test="name != null and name.lastIndexOf('ji') > 0">  <!-- 是否以什么结尾 -->

<if test="name != null and 'hello' == name">
<if test="username != null and 'hello' eq name">
<if test="name != null and 'hello'.toString() == name.toString()"> <!-- name非字符串时 -->
    
<if test="'xiaohong' eq name or 'xiao' eq name ">

list

<if test="list != null and list.size()>0">

数组

<if test="ids!=null and ids.length>0">

时间

<if test="releaseDateStart != null and releaseDateEnd != null ">

sql条件判断

UPDATE tb_volunteer
SET honor_level = (
CASE
<![CDATA[WHEN score < 5001 THEN 0]]>
<![CDATA[WHEN score > 5000 AND score < 10001 THEN 1]]>
<![CDATA[WHEN score > 10000 AND score < 15001 THEN 2]]>
END );

where语句

<!-- 根据条件查询用户 -->
<select id="queryUserByWhere" parameterType="user" resultType="user">
  SELECT id, username, birthday, sex, address FROM `user`
<!-- where标签可以自动添加where,同时处理sql语句中第一个and关键字 -->
  <where>
    <if test="sex != null">
       AND sex = #{sex}
    </if>
    <if test="username != null and username != ''">
       AND username LIKE
       '%${username}%'
    </if>
  </where>
</select>
  1. sql片段

    <!-- 根据条件查询用户 -->
    <select id="queryUserByWhere" parameterType="user" resultType="user">
      <!-- SELECT id, username, birthday, sex, address FROM `user` -->
      <!-- 使用include标签加载sql片段;refid是sql片段id -->
      SELECT <include refid="userFields" /> FROM `user`
      <!-- where标签可以自动添加where关键字,同时处理sql语句中第一个and关键字 -->
      <where>
        <if test="sex != null">
           AND sex = #{sex}
        </if>
        <if test="username != null and username != ''">
           AND username LIKE
           '%${username}%'
        </if>
      </where>
    </select>
     
    <!-- 声明sql片段 -->
    <sql id="userFields">
      id, username, birthday, sex, address
    </sql>
  2. foreach标签

    <!-- 根据ids查询用户 -->
    <select id="queryUserByIds" parameterType="queryVo" resultType="user">
      SELECT * FROM `user`
      <where>
        <!-- foreach标签,进行遍历 -->
        <!-- collection:遍历的集合,这里是QueryVo的ids属性 -->
        <!-- item:遍历的项目,可以随便写,,但是和后面的#{}里面要一致 -->
        <!-- open:在前面添加的sql片段 -->
        <!-- close:在结尾处添加的sql片段 -->
        <!-- separator:指定遍历的元素之间使用的分隔符 -->
        <foreach collection="ids" item="item" open="id IN (" close=")"
           separator=",">
           #{item}
        </foreach>
      </where>
    </select>

like语句

name LIKE CONCAT('%',#{keyword},'%')

foreach语句

<!--list -->
<foreach collection="list" index="index" item="item" open="(" separator="," close=")">
    
<!--array数组-->
<foreach collection="array" index="index" item="item" open="(" separator="," close=")">
    
<!--封装到map中,dicts为map中的key-->
<foreach collection ="dicts" item="dict" index= "index" separator =",">    

插入语句

插入另一张表的数据

insert into 表一(字段一,字段二) select 字段一,字段二 from 表二;

批量插入

<insert id="batchInserDictData" parameterType="map">
    INSERT INTO tb_volunteer_dict_data ( dict_id, dict_label, dict_value )
    VALUES
    <foreach collection ="list" item="dict" index= "index" separator =",">
        (#{dictTypeId}, #{dict.dictLabel},#{dict.dictValue} )
    </foreach>
</insert>

批量插入(返回id)

<insert id="saveDictType" parameterType="list" useGeneratedKeys="true" keyProperty="dictTypeId">
    INSERT INTO tb_type ( dict_name, creator, create_date)
    VALUES
    <foreach collection ="list" item="dict" index= "index" separator ="," >
        <if test="dict.dictName != null and dict.dictName.trim() != ''">
            (#{dict.dictName}, #{dict.creator}, #{dict.createDate})
        </if>
    </foreach>;
</insert>

单个插入(返回id)

<insert id="save" parameterType="map" useGeneratedKeys="true" keyProperty="dto.templateId">
    INSERT INTO tb_template ( template_name, template_status, org_id)
    VALUES (#{dto.templateName}, #{dto.templateStatus}, #{orgId});
</insert>

mysql死锁错误

代码调试的时候出现错误

SQLSTATE[HY000]: General error: 1205 Lock wait timeout exceeded; try restart

可以用下面三张表来查原因:

innodb_trx ## 当前运行的所有事务
innodb_locks ## 当前出现的锁
innodb_lock_waits ## 锁等待的对应关系

解决步骤

查找到为提交事务的数据,kill此线程即可

select * from information_schema.innodb_trx;
-- trx_mysql_thread_id  即为该进程

kill 1544;

备注

参考:https://blog.csdn.net/flysnow...
参考:https://blog.csdn.net/zc47423...


messchx
58 声望5 粉丝