Java Mybatis

前言

  • MyBatis 是一款优秀的持久层框架,前身是(ibatis),它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。

  • MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

  • Mybatis 官网

  • Mybatis 中文网

  • MyBatis 教程

  • MyBatis 快速入门

1、基础

  • MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。

  • MyBatis 可以对配置和原生 Map 使用简单的 XML 或注解,将接口和 Java 的 POJOs (Plain Old Java Objects,普通的 Java 对象) 映射成数据库中的记录。

  • 平时我们都用 JDBC 访问数据库,除了需要自己写 SQL 之外,还必须操作 Connection, Statement, ResultSet 这些其实只是手段的辅助类。不仅如此,访问不同的表,还会写很多雷同的代码,显得繁琐和枯燥。

  • 那么用了 Mybatis 之后,只需要自己提供 SQL 语句,其他的工作,诸如建立连接,Statement,JDBC 相关异常处理等等都交给 Mybatis 去做了,那些重复性的工作 Mybatis 也给做掉了,我们只需要关注在增删改查等操作层面上,而把技术细节都封装在了我们看不见的地方。

1.1 配置

  • Mybatis 配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <?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">

    <configuration>
    <typeAliases>
    <package name="com.how2java.pojo"/>
    </typeAliases>
    <environments default="development">
    <environment id="development">
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">
    <property name="driver" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/how2java?characterEncoding=UTF-8"/>
    <property name="username" value="root"/>
    <property name="password" value="admin"/>
    </dataSource>
    </environment>
    </environments>
    <mappers>
    <mapper resource="com/how2java/pojo/Category.xml"/>
    </mappers>
    </configuration>
  • 基本原理:

    • 应用程序找 Mybatis 要数据。

    • mybatis 从数据库中找来数据。

      • 通过 mybatis-config.xml 定位哪个数据库。
      • 通过 Category.xml 执行对应的 select 语句。
      • 基于 Category.xml 把返回的数据库记录封装在 Category 对象中。
      • 把多个 Category 对象装在一个 Category 集合中。
    • 返回一个 Category 集合。

1.2 CRUD

  • CRUD

  • 增加

    1
    2
    3
    4
    5
    <mapper namespace="com.how2java.pojo">
    <insert id="addCategory" parameterType="Category">
    insert into category_ ( name ) values (#{name})
    </insert>
    </mapper>
    1
    2
    3
    4
    Category c = new Category();
    c.setName("新增加的 Category");

    session.insert("addCategory", c);
  • 删除

    1
    2
    3
    4
    5
    <mapper namespace="com.how2java.pojo">
    <delete id="deleteCategory" parameterType="Category">
    delete from category_ where id = #{id}
    </delete>
    </mapper>
    1
    2
    3
    4
    Category c = new Category();
    c.setId(6);

    session.delete("deleteCategory", c);
  • 修改

    1
    2
    3
    4
    5
    <mapper namespace="com.how2java.pojo">
    <update id="updateCategory" parameterType="Category">
    update category_ set name = #{name} where id = #{id}
    </update>
    </mapper>
    1
    2
    3
    4
    Category c = session.selectOne("getCategory", 3);
    c.setName("修改了的 Category 名称");

    session.update("updateCategory", c);
  • 获取

    1
    2
    3
    4
    5
    <mapper namespace="com.how2java.pojo">
    <select id="getCategory" parameterType="_int" resultType="Category">
    select * from category_ where id = #{id}
    </select>
    </mapper>
    1
    Category c = session.selectOne("getCategory", 3);           // 获取 id=3 的记录
  • 查询所有

    1
    2
    3
    4
    5
    <mapper namespace="com.how2java.pojo">
    <select id="listCategory" resultType="Category">
    select * from category_
    </select>
    </mapper>
    1
    List<Category> cs = session.selectList("listCategory");

1.3 更多查询

  • 更多查询

  • 模糊查询

    1
    2
    3
    4
    5
    <mapper namespace="com.how2java.pojo">
    <select id="listCategoryByName" parameterType="string" resultType="Category">
    select * from category_ where name like concat('%', #{0}, '%')
    </select>
    </mapper>
    1
    List<Category> cs = session.selectList("listCategoryByName", "cat");
    • 如果是 oracle,写法是

      1
      select * from category_ where name like '%'||#{0}||'%'
  • 多条件查询

    1
    2
    3
    4
    5
    <mapper namespace="com.how2java.pojo">
    <select id="listCategoryByIdAndName" parameterType="map" resultType="Category">
    select * from category_ where id > #{id} and name like concat('%', #{name}, '%')
    </select>
    </mapper>
    1
    2
    3
    4
    5
    Map<String, Object> params = new HashMap<>();
    params.put("id", 3);
    params.put("name", "cat");

    List<Category> cs = session.selectList("listCategoryByIdAndName", params);

1.4 一对多

  • 一对多

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <mapper namespace="com.how2java.pojo">
    <resultMap type="Category" id="categoryBean">
    <id column="cid" property="id" />
    <result column="cname" property="name" />

    <!-- 一对多的关系 -->
    <!-- property: 指的是集合属性的值, ofType:指的是集合中元素的类型 -->
    <collection property="products" ofType="Product">
    <id column="pid" property="id" />
    <result column="pname" property="name" />
    <result column="price" property="price" />
    </collection>
    </resultMap>

    <!-- 关联查询分类和产品表 -->
    <select id="listCategory" resultMap="categoryBean">
    select c.*, p.*, c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname' from category_ c left join product_ p on c.id = p.cid
    </select>
    </mapper>
    1
    List<Category> cs = session.selectList("listCategory");

1.5 多对一

  • 多对一

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <mapper namespace="com.how2java.pojo">
    <resultMap type="Product" id="productBean">
    <id column="pid" property="id" />
    <result column="pname" property="name" />
    <result column="price" property="price" />

    <!-- 多对一的关系 -->
    <!-- property: 指的是属性名称, javaType:指的是属性的类型 -->
    <association property="category" javaType="Category">
    <id column="cid" property="id"/>
    <result column="cname" property="name"/>
    </association>
    </resultMap>

    <!-- 根据 id 查询 Product, 关联将 Orders 查询出来 -->
    <select id="listProduct" resultMap="productBean">
    select c.*, p.*, c.id 'cid', p.id 'pid', c.name 'cname', p.name 'pname' from category_ c left join product_ p on c.id = p.cid
    </select>
    </mapper>
    1
    List<Product> ps = session.selectList("listProduct");

1.6 多对多

2、动态 SQL

2.1 if 标签

  • if 标签:条件判断。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <mapper namespace="com.how2java.pojo">
    <select id="listProduct" resultType="Product">
    select * from product_

    <!-- if 标签 -->
    <if test="name != null">
    where name like concat('%', #{name}, '%')
    </if>

    </select>
    </mapper>

2.2 where 标签

  • where 标签:多条件判断。如果任何条件都不成立,那么就在 sql 语句里就不会出现 where 关键字。如果有任何条件成立,会自动去掉多出来的 and 或者 or。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <mapper namespace="com.how2java.pojo">
    <select id="listProduct" resultType="Product">
    select * from product_

    <!-- where 标签 -->
    <where>
    <if test="name != null">
    and name like concat('%', #{name}, '%')
    </if>
    <if test="price != null and price != 0">
    and price > #{price}
    </if>
    </where>

    </select>
    </mapper>
  • set 标签:在 update 语句里也会碰到多个字段相关的问题。在这种情况下,就可以使用 set 标签。其效果与 where 标签类似,有数据的时候才进行设置。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <update id="updateProduct" parameterType="Product">
    update product_

    <!-- set 标签 -->
    <set>
    <if test="name != null">
    name = #{name} ,
    </if>
    <if test="price != null">
    price = #{price}
    </if>
    </set>

    where id = #{id}
    </update>
  • trim 标签:用来定制想要的功能。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <mapper namespace="com.how2java.pojo">
    <select id="listProduct" resultType="Product">
    select * from product_

    <!-- 替换 where 标签 -->
    <trim prefix="WHERE" prefixOverrides="AND | OR ">
    <if test="name!=null">
    and name like concat('%', #{name}, '%')
    </if>
    <if test="price!=null and price!=0">
    and price > #{price}
    </if>
    </trim>

    </select>
    </mapper>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <mapper namespace="com.how2java.pojo">
    <update id="updateProduct" parameterType="Product">
    update product_

    <!-- 替换 set 标签 -->
    <trim prefix="SET" suffixOverrides=",">
    <if test="name != null">
    name = #{name} ,
    </if>
    <if test="price != null">
    price = #{price}
    </if>
    </trim>

    where id = #{id}
    </update>
    </mapper>

2.3 choose 标签

  • choose 标签:Mybatis 里面没有 else 标签,但是可以使用 when otherwise 标签来达到这样的效果。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <mapper namespace="com.how2java.pojo">
    <select id="listProduct" resultType="Product">
    SELECT * FROM product_
    <where>

    <!-- choose 标签 -->
    <choose>
    <when test="name != null">
    and name like concat('%', #{name}, '%')
    </when>
    <when test="price != null and price != 0">
    and price > #{price}
    </when>
    <otherwise>
    and id > 1
    </otherwise>
    </choose>

    </where>
    </select>
    </mapper>

2.4 foreach 标签

  • foreach 标签:通常用于 in 这样的语法里。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <mapper namespace="com.how2java.pojo">
    <select id="listProduct" resultType="Product">
    SELECT * FROM product_
    WHERE ID in

    <!-- foreach 标签 -->
    <foreach item="item" index="index" collection="list" open="(" separator="," close=")">
    #{item}
    </foreach>

    </select>
    </mapper>
    1
    2
    3
    4
    5
    6
    List<Integer> ids = new ArrayList();
    ids.add(1);
    ids.add(3);
    ids.add(5);

    List<Product> ps = session.selectList("listProduct", ids);

2.5 bind 标签

  • bind 标签:就像是再做一次字符串拼接,方便后续使用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <mapper namespace="com.how2java.pojo">
    <!-- 本来的模糊查询方式 -->
    <!-- <select id="listProduct" resultType="Product"> -->
    <!-- select * from product_ where name like concat('%', #{0}, '%') -->
    <!-- </select> -->

    <select id="listProduct" resultType="Product">

    <!-- bind 标签 -->
    <bind name="likename" value="'%' + name + '%'" />

    select * from product_ where name like #{likename}
    </select>

    </mapper>

3、注解

4、相关概念

1.1 日志

  • 有时候需要打印日志,知道 mybatis 执行了什么样的 SQL 语句,以便进行调试。这时,就需要开启日志,而 mybatis 自身是没有带日志的,使用的都是第三方日志,如 log4j。

1.2 事务管理

  • 在 mybatis-config.xml 配置文件中设置事务管理方式。

    • JDBC 方式

      1
      2
      3
      4
      5
      <environments default="development">
      <environment id="development">
      <transactionManager type="JDBC"/>
      </environment>
      </environments>
    • MANAGED 方式

  • 在 MySql 中,只有当表的类型是 INNODB 的时候,才支持事务,所以需要把表的类型[设置为 INNODB][link34],否则无法观察到事务。

    1
    2
    3
    4
    5
    # 修改表的类型为 INNODB
    $ alter table product_ ENGINE = innodb;

    # 查看表的类型的 SQL
    $ show table status from test;

1.3 延迟加载

  • 在 mybatis-config.xml 配置文件中设置延迟加载配置。

    1
    2
    3
    4
    5
    6
    7
    <settings> 
    <!-- 打开延迟加载的开关 -->
    <setting name="lazyLoadingEnabled" value="true" />

    <!-- 将积极加载改为消息加载即按需加载 -->
    <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

1.4 分页

  • 分页

    1
    2
    3
    4
    5
    6
    <select id="listCategory" resultType="Category">
    select * from category_
    <if test="start!=null and count!=null">
    limit #{start}, #{count}
    </if>
    </select>
    1
    2
    3
    4
    Map<String,Object> params = new HashMap<>();
    params.put("start", 0);
    params.put("count", 5);
    List<Category> cs = session.selectList("listCategory", params);
  • 注解方式

    1
    2
    @Select("select * from category_ limit #{start}, #{count}")
    public List<Category> listByPage(@Param("start") int start, @Param("count")int count);
    1
    2
    CategoryMapper mapper = session.getMapper(CategoryMapper.class);
    List<Category> cs = mapper.listByPage(0, 5);

1.4.1 PageHelper

  • PageHelper 是一款犀利的 Mybatis 分页插件,使用了这个插件之后,分页开发起来更加简单容易。

  • PageHelper 官网

  • 在 mybatis-config.xml 中配置 PageHelper 插件。

    1
    2
    3
    <plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>
  • 查询,只需要在执行查询所有的调用之前,执行一条语句即可。

    1
    2
    3
    <select id="listCategory" resultType="Category">
    select * from category_
    </select>
    1
    2
    3
    4
    5
    PageHelper.offsetPage(0, 5);

    List<Category> cs = session.selectList("listCategory");

    session.commit();
  • 获取总数

    1
    2
    PageInfo pageInfo = new PageInfo<>(cs);
    System.out.println("总数:" + pageInfo.getTotal());

1.5 缓存

  • Mybatis 的一级缓存在 session 上,只要通过 session 查过的数据,都会放在 session上,下一次再查询相同 id 的数据,都直接冲缓存中取出来,而不用到数据库里去取了。

  • Mybatis 二级缓存是 SessionFactory,如果两次查询基于同一个 SessionFactory,那么就从二级缓存中取数据,而不用到数据库里去取了。

    • 在 mybatis-config.xml 中新增一个段配置,以支持二级缓存。

      1
      2
      3
      4
      <settings> 
      <!-- 开启二级缓存 -->
      <setting name="cacheEnabled" value="true"/>
      </settings>
    • 在 Category.xml 中新增 <cache /> 以启动对 Category 对象的二级缓存。

      1
      2
      3
      <mapper namespace="com.how2java.pojo">
      <cache />
      </mapper>
    • 让 Category 实现序列化接口。

      1
      public class Category implements Serializable { }

1.6 C3P0 连接池

1.7 查询总数

  • 查询总数

    1
    2
    @Select("select count(*) from category_") 
    public int count();

1.8 逆向工程

  • Mybatis Generator 是一个用于 Mybatis 逆向工程的工具。

  • 前面学习的方式都是先有 pojo, mapper, xml, 然后再创建表。

  • 用逆向工程的方式,首先保证数据库里有表,然后通过 Mybatis Generator 生成 pojo, mapper 和 xml 文件。可以节约大家的时间,提高开发效率,降低出错几率。

  • 配置文件 generatorConfig.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

    <generatorConfiguration>

    <!--数据库驱动-->
    <!--
    如果 IDE (eclipse 或者 idea) 项目里导入了 jar 包,那么就不需要配置了 jar 包的绝对路径了
    <classPathEntry location="e:/project/mybatis/lib/mysql-connector-java-5.0.8-bin.jar"/>
    -->

    <context id="DB2Tables" targetRuntime="MyBatis3">
    <commentGenerator>
    <property name="suppressDate" value="true"/>
    <property name="suppressAllComments" value="false"/>
    </commentGenerator>

    <!--数据库链接地址账号密码-->
    <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost/how2java" userId="root" password="admin">
    </jdbcConnection>
    <javaTypeResolver>
    <property name="forceBigDecimals" value="false"/>
    </javaTypeResolver>

    <!--生成 Model 类存放位置-->
    <javaModelGenerator targetPackage="com.how2java.pojo" targetProject="src">
    <property name="enableSubPackages" value="true"/>
    <property name="trimStrings" value="true"/>
    </javaModelGenerator>

    <!--生成映射文件存放位置-->
    <sqlMapGenerator targetPackage="com.how2java.pojo" targetProject="src">
    <property name="enableSubPackages" value="true"/>
    </sqlMapGenerator>

    <!--生成 Dao 类存放位置-->
    <javaClientGenerator type="XMLMAPPER" targetPackage="com.how2java.mapper" targetProject="src">
    <property name="enableSubPackages" value="true"/>
    </javaClientGenerator>

    <!--生成对应表及类名-->
    <table tableName="category_" domainObjectName="Category" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true" selectByExampleQueryId="false">
    <property name="my.isgen.usekeys" value="true"/>
    <generatedKey column="id" sqlStatement="JDBC"/>
    </table>
    <!-- <table tableName="product_" domainObjectName="Product" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"></table> -->
    </context>
    </generatorConfiguration>
文章目录
  1. 1. 前言
  2. 2. 1、基础
    1. 2.1. 1.1 配置
    2. 2.2. 1.2 CRUD
    3. 2.3. 1.3 更多查询
    4. 2.4. 1.4 一对多
    5. 2.5. 1.5 多对一
    6. 2.6. 1.6 多对多
  3. 3. 2、动态 SQL
    1. 3.1. 2.1 if 标签
    2. 3.2. 2.2 where 标签
    3. 3.3. 2.3 choose 标签
    4. 3.4. 2.4 foreach 标签
    5. 3.5. 2.5 bind 标签
  4. 4. 3、注解
  5. 5. 4、相关概念
    1. 5.1. 1.1 日志
    2. 5.2. 1.2 事务管理
    3. 5.3. 1.3 延迟加载
    4. 5.4. 1.4 分页
      1. 5.4.1. 1.4.1 PageHelper
    5. 5.5. 1.5 缓存
    6. 5.6. 1.6 C3P0 连接池
    7. 5.7. 1.7 查询总数
    8. 5.8. 1.8 逆向工程
隐藏目录