Mybatis

前言

1、MyBatis

  • MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。

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

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

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

2、配置

  • 配置访问数据库

    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>
  • 编写 SQL 语句

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

    <mapper namespace="com.how2java.pojo">
    <select id="listCategory" resultType="Category">
    select * from category_
    </select>
    </mapper>
  • 基本原理

    • 应用程序找 Mybatis 要数据。

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

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

2.1 Spring Boot 集成

  • 导入相关依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <dependencyManagement>

    <dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.2</version>
    </dependency>

    <dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.2.9</version>
    </dependency>

    </dependencies>
  • 配置访问数据库

    1
    2
    3
    4
    5
    6
    spring:
    datasource:
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&charset=utf8&serverTimezone=GMT%2b8&useSSL=false&allowMultiQueries=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

3、SQL 注入

  • SQL 注入是一种代码注入技术,用于攻击数据驱动的应用,恶意的 SQL 语句被插入到执行的实体字段中(例如,为了转储数据库内容给攻击者)。

  • SQL 注入,大家都不陌生,是一种常见的攻击方式。攻击者在界面的表单信息或 URL 上输入一些奇怪的 SQL 片段(例如 “or '1'='1'” 这样的语句),有可能入侵参数检验不足的应用程序。所以,在我们的应用中需要做一些工作,来防备这样的攻击方式。在一些安全性要求很高的应用中(比如银行软件),经常使用将 SQL 语句全部替换为存储过程这样的方式,来防止 SQL 注入。这当然是一种很安全的方式,但我们平时开发中,可能不需要这种死板的方式。

3.1 #$ 的区别

  • 首先看一下下面两个 sql 语句的区别

    1
    2
    3
    4
    5
    6
    <select id="selectByNameAndPassword" parameterType="java.util.Map" resultMap="BaseResultMap">
    SELECT id, username, password, role
    FROM user
    WHERE username = #{username, jdbcType=VARCHAR}
    AND password = #{password, jdbcType=VARCHAR} <!-- 使用 # -->
    </select>
    1
    2
    3
    4
    5
    6
    <select id="selectByNameAndPassword" parameterType="java.util.Map" resultMap="BaseResultMap">
    SELECT id, username, password, role
    FROM user
    WHERE username = ${username, jdbcType=VARCHAR}
    AND password = ${password, jdbcType=VARCHAR} <!-- 使用 $ -->
    </select>
  • Mybatis 中的 #$ 的区别

    • # 将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:where username=#{username},如果传入的值是 111,那么解析成 sql 时的值为 where username="111", 如果传入的值是 id,则解析成的 sql 为 where username="id". 

    • $ 将传入的数据直接显示生成在 sql 中。如:where username=${username},如果传入的值是 111,那么解析成 sql 时的值为 where username=111,如果传入的值是 ;drop table user;,则解析成的 sql 为:select id, username, password, role from user where username=;drop table user;

    • # 方式能够很大程度防止 sql 注入,$ 方式无法防止 sql 注入。

    • $ 方式一般用于传入数据库对象,例如传入表名。

    • 一般能用 # 的就别用 $,若不得不使用 “${xxx}” 这样的参数,要手工地做好过滤工作,来防止 sql 注入攻击。

    • 在 MyBatis 中,“${xxx}” 这样格式的参数会直接参与 SQL 编译,从而不能避免注入攻击。但涉及到动态表名和列名时,只能使用 “${xxx}” 这样的参数格式。所以,这样的参数需要我们在代码中手工进行处理来防止注入。

3.2 Mybatis 防止 SQL 注入

  • MyBatis 框架作为一款半自动化的持久层框架,其 SQL 语句都要我们自己手动编写,这个时候当然需要防止 SQL 注入。其实,MyBatis 的 SQL 是一个具有 “输入 + 输出” 的功能,类似于函数的结构,参考上面的两个例子。其中,parameterType 表示了输入的参数类型,resultType 表示了输出的参数类型。回应上文,如果我们想防止 SQL 注入,理所当然地要在输入参数上下功夫。

  • 上面代码中使用 # 的即输入参数在 SQL 中拼接的部分,传入参数后,打印出执行的 SQL 语句,会看到 SQL 是这样的:

    1
    select id, username, password, role from user where username=? and password=?
  • 不管输入什么参数,打印出的 SQL 都是这样的。这是因为 MyBatis 启用了预编译功能,在 SQL 执行前,会先将上面的 SQL 发送给数据库进行编译;执行时,直接使用编译好的 SQL,替换占位符 “?” 就可以了。因为 SQL 注入只能对编译过程起作用,所以这样的方式就很好地避免了 SQL 注入的问题。

  • 【底层实现原理】MyBatis 是如何做到 SQL 预编译的呢?其实在框架底层,是 JDBC 中的 PreparedStatement 类在起作用,PreparedStatement 是我们很熟悉的 Statement 的子类,它的对象包含了编译好的 SQL 语句。这种 “准备好” 的方式不仅能提高安全性,而且在多次执行同一个 SQL 时,能够提高效率。原因是 SQL 已编译好,再次执行时无需再编译。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 安全的,预编译了的
    private String getNameByUserId(String userId) {
    Connection conn = getConn(); // 获得连接
    String sql = "select id, username, password, role from user where id=?"; // 执行 sql 前会预编译号该条语句
    PreparedStatement pstmt = conn.prepareStatement(sql);
    pstmt.setString(1, id);
    ResultSet rs=pstmt.executeUpdate();
    ......
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 不安全的,没进行预编译
    private String getNameByUserId(String userId) {
    Connection conn = getConn(); // 获得连接
    String sql = "select id, username, password, role from user where id=" + id;

    // 当 id 参数为 "3;drop table user;" 时,执行的 sql 语句如下:
    // select id, username, password, role from user where id=3; drop table user;

    PreparedStatement pstmt = conn.prepareStatement(sql);
    ResultSet rs=pstmt.executeUpdate();
    ......
    }
  • 【结论】

符号 说明
#{} 相当于 JDBC 中的 PreparedStatement
${} 是输出变量的值
  • 简单说,#{} 是经过预编译的,是安全的;${} 是未经过预编译的,仅仅是取变量的值,是非安全的,存在 SQL 注入。

  • 如果我们 order by 语句后用了 ${},那么不做任何处理的时候是存在 SQL 注入危险的。你说怎么防止,那我只能悲惨的告诉你,你得手动处理过滤一下输入的内容。如判断一下输入的参数的长度是否正常(注入语句一般很长),更精确的过滤则可以查询一下输入的参数是否在预期的参数集合中。

4、CRUD 操作

4.1 基本操作

  • 增加 数据

    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");

4.2 条件查询

  • 模糊查询

    1
    2
    3
    4
    5
    6
    7
    8
    <mapper namespace="com.how2java.pojo">
    <select id="listCategoryByName" parameterType="string" resultType="Category">
    select * from category_ where name like concat('%', #{name}, '%')
    </select>
    </mapper>

    <!-- 如果是 oracle,写法是 -->
    select * from category_ where name like '%'||#{name}||'%'
    1
    List<Category> cs = session.selectList("listCategoryByName", "cat");
  • 多条件查询

    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);

4.3 多关系查询

  • 一对多

    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
    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
    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
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <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>

    <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>
    <select id="getProduct" 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
    where p.id = #{id}
    </select>
    </mapper>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.how2java.pojo">

    <insert id="addOrderItem" parameterType="OrderItem">
    insert into order_item_
    values(null,#{order.id},#{product.id},#{number})
    </insert>
    <insert id="deleteOrderItem" parameterType="OrderItem">
    delete from order_item_
    where oid = #{order.id} and pid = #{product.id}
    </insert>

    </mapper>
    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
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.how2java.pojo">
    <resultMap type="Order" id="orderBean">
    <id column="oid" property="id" />
    <result column="code" property="code" />

    <collection property="orderItems" ofType="OrderItem">
    <id column="oiid" property="id" />
    <result column="number" property="number" />
    <association property="product" javaType="Product">
    <id column="pid" property="id"/>
    <result column="pname" property="name"/>
    <result column="price" property="price"/>
    </association>
    </collection>
    </resultMap>

    <select id="listOrder" resultMap="orderBean">
    select o.*,p.*,oi.*, o.id 'oid', p.id 'pid', oi.id 'oiid', p.name 'pname'
    from order_ o
    left join order_item_ oi on o.id =oi.oid
    left join product_ p on p.id = oi.pid
    </select>

    <select id="getOrder" resultMap="orderBean">
    select o.*,p.*,oi.*, o.id 'oid', p.id 'pid', oi.id 'oiid', p.name 'pname'
    from order_ o
    left join order_item_ oi on o.id =oi.oid
    left join product_ p on p.id = oi.pid
    where o.id = #{id}
    </select>
    </mapper>
    1
    List<Order> os = session.selectList("listOrder");

4.4 动态 SQL 语句

4.4.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>

4.4.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>

4.4.3 set 标签

  • 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>

4.4.4 trim 标签

  • 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>

4.4.5 choose 标签

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

    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>

4.4.6 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);

4.4.7 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('%', #{name}, '%') -->
    <!-- </select> -->

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

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

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

    </mapper>

5、使用注解方式

  • 对比不使用注解方式,其实就是把 SQL 语句从 XML 文件挪到了注解上来。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <?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 class="com.how2java.mapper.CategoryMapper"/> <!-- 增加对 CategoryMapper 映射 -->
    </mappers>
    </configuration>

5.1 CRUD

  • CRUD

    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
    import java.util.List;

    import org.apache.ibatis.annotations.Delete;
    import org.apache.ibatis.annotations.Insert;
    import org.apache.ibatis.annotations.Select;
    import org.apache.ibatis.annotations.Update;

    import com.how2java.pojo.Category;

    public interface CategoryMapper {

    @Insert("insert into category_ ( name ) values (#{name}) ")
    public int add(Category category);

    @Delete("delete from category_ where id= #{id} ")
    public void delete(int id);

    @Select("select * from category_ where id= #{id} ")
    public Category get(int id);

    @Update("update category_ set name=#{name} where id=#{id} ")
    public int update(Category category);

    @Select("select * from category_ ")
    public List<Category> list();
    }

5.2 多关系查询

  • 一对多

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public interface CategoryMapper {

    @Select(" select * from category_ ")
    @Results({
    @Result(property = "id", column = "id"),
    @Result(property = "products", javaType = List.class, column = "id", many = @Many(select = "com.how2java.mapper.ProductMapper.listByCategory"))
    })
    public List<Category> list();
    }
    1
    2
    3
    4
    5
    public interface ProductMapper {

    @Select(" select * from product_ where cid = #{cid}")
    public List<Product> listByCategory(int cid);
    }
  • 多对一

    1
    2
    3
    4
    5
    public interface CategoryMapper {

    @Select(" select * from category_ where id = #{id}")
    public Category get(int id);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    public interface ProductMapper {

    @Select(" select * from product_ ")
    @Results({
    @Result(property="category",column="cid",one=@One(select="com.how2java.mapper.CategoryMapper.get"))
    })
    public List<Product> list();
    }
  • 多对多

    1
    2
    3
    4
    5
    public interface ProductMapper {

    @Select("select * from product_ where id = #{id}")
    public Product get(int id);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    public interface OrderItemMapper {

    @Select(" select * from order_item_ where oid = #{oid}")
    @Results({
    @Result(property="product",column="pid",one=@One(select="com.how2java.mapper.ProductMapper.get"))
    })
    public List<OrderItem> listByOrder(int oid);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public interface OrderMapper {

    @Select("select * from order_")
    @Results({
    @Result(property = "id", column = "id"),
    @Result(property = "orderItems", javaType = List.class, column = "id",
    many = @Many(select = "com.how2java.mapper.OrderItemMapper.listByOrder"))
    })
    public List<Order> list();
    }

5.3 动态 SQL 语句

  • 动态 SQL 语句

    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
    import org.apache.ibatis.jdbc.SQL;

    public class CategoryDynaSqlProvider {

    public String list() {
    return new SQL() // 使用 SQL 类的方式构建
    .SELECT("*")
    .FROM("category_")
    .toString();
    }

    public String get() {
    return new SQL()
    .SELECT("*")
    .FROM("category_")
    .WHERE("id=#{id}")
    .toString();
    }

    public String add() {
    return new SQL()
    .INSERT_INTO("category_")
    .VALUES("name", "#{name}")
    .toString();
    }

    public String update() {
    return new SQL()
    .UPDATE("category_")
    .SET("name=#{name}")
    .WHERE("id=#{id}")
    .toString();
    }

    public String delete() {
    return new SQL()
    .DELETE_FROM("category_")
    .WHERE("id=#{id}")
    .toString();
    }
    }
    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
    import java.util.List;

    import org.apache.ibatis.annotations.DeleteProvider;
    import org.apache.ibatis.annotations.InsertProvider;
    import org.apache.ibatis.annotations.SelectProvider;
    import org.apache.ibatis.annotations.UpdateProvider;

    import com.how2java.CategoryDynaSqlProvider;
    import com.how2java.pojo.Category;

    public interface CategoryMapper {

    @SelectProvider(type=CategoryDynaSqlProvider.class,method="list")
    public List<Category> list();

    @SelectProvider(type=CategoryDynaSqlProvider.class,method="get")
    public Category get(int id);

    @InsertProvider(type=CategoryDynaSqlProvider.class,method="add")
    public int add(Category category);

    @UpdateProvider(type=CategoryDynaSqlProvider.class,method="update")
    public int update(Category category);

    @DeleteProvider(type=CategoryDynaSqlProvider.class,method="delete")
    public void delete(int id);
    }

5.4 SQL 类

  • SQL 类是用于进行动态 SQL 生成的,如下代码是一个相对复杂的 SQL 类的使用举例。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    private String selectPersonSql() {

    return new SQL() {{
    SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME");
    SELECT("P.LAST_NAME, P.CREATED_ON, P.UPDATED_ON");
    FROM("PERSON P");
    FROM("ACCOUNT A");
    INNER_JOIN("DEPARTMENT D on D.ID = P.DEPARTMENT_ID");
    INNER_JOIN("COMPANY C on D.COMPANY_ID = C.ID");
    WHERE("P.ID = A.ID");
    WHERE("P.FIRST_NAME like ?");
    OR();
    WHERE("P.LAST_NAME like ?");
    GROUP_BY("P.ID");
    HAVING("P.LAST_NAME like ?");
    OR();
    HAVING("P.FIRST_NAME like ?");
    ORDER_BY("P.ID");
    ORDER_BY("P.FULL_NAME");
    }}.toString();
    }

6、相关概念

6.1 日志

  • 有时候需要打印日志,知道 mybatis 执行了什么样的 SQL 语句,以便进行调试。

  • 这时,就需要开启日志,而 mybatis 自身是没有带日志的,使用的都是第三方日志,如 log4j。

6.2 事务管理

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

    • JDBC 方式

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

      • 交由容器管理,如 weblogic 等。
  • 在 MySql 中,只有当表的类型是 INNODB 的时候,才支持事务,所以需要把表的类型设置为 INNODB,否则无法观察到事务。

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

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

6.3 延迟加载

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

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

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

6.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);

6.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());

6.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 { }

6.6 C3P0 连接池

  • Mybatis 整合 C3P0 数据库连接池,不是 SSM 的那种整合方式。

  • 配置 mybatis-config.xml

    • 注释掉 type="POOLED" 的数据源。
    • 换成 type="org.mybatis.c3p0.C3P0DataSourceFactory" 的数据源。
  • 新建 C3P0DataSourceFactory.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
    import com.mchange.v2.c3p0.ComboPooledDataSource;

    public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {

    public C3P0DataSourceFactory() {
    this.dataSource =new ComboPooledDataSource();
    }
    }

6.7 逆向工程

  • 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>

7、Mybatis 常见面试题

  • Mybatis 常见面试题

  • 1)JDBC 编程有哪些不足之处,Mybatis 是如何解决这些问题的

    • 数据库连接的创建、释放频繁造成系统资源浪费从而影响了性能,如果使用数据库连接池就可以解决这个问题。当然 JDBC 同样能够使用数据源。
      解决:在 SQLMapConfig.xml 中配置数据连接池,使用数据库连接池管理数据库连接。

    • SQL 语句在写代码中不容易维护,事件需求中 SQL 变化的可能性很大,SQL 变动需要改变 Java 代码。解决:将 SQL 语句配置在 mapper.xml 文件中与 Java 代码分离。

    • 向 SQL 语句传递参数麻烦,因为 SQL 语句的 where 条件不一定,可能多,也可能少,占位符需要和参数一一对应。解决:Mybatis 自动将 Java 对象映射到 SQL 语句。

    • 对结果集解析麻烦,SQL 变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成 pojo 对象解析比较方便。解决:Mybatis 自动将 SQL 执行结果映射到 Java 对象。

  • 2)Mybatis 编程步骤

    • Step 1:创建 SQLSessionFactory
    • Step 2:通过 SQLSessionFactory 创建 SQLSession
    • Step 3:通过 SQLSession 执行数据库操作。
    • Step 4:调用 session.commit() 提交事物。
    • Step 5:调用 session.close() 关闭会话。
  • 3)MyBatis 与 hibernate 有哪些不同

    • Mybatis 是支持定制化 SQL、存储过程以及高级映射的一种持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。Mybatis 它不完全是一个 ORM (对象关系映射) 框架;它需要程序员自己编写部分 SQL 语句。Mybatis 可以通过 xml 或者注解的方式灵活的配置要运行的 SQL 语句,并将 Java 对象和 SQL 语句映射生成最终的执行的 SQL,最后将 SQL 执行的结果在映射生成 Java 对象。Mybatis 程序员可以直接的编写原生态的 SQL 语句,可以控制 SQL 执行性能,灵活度高,适合软件需求变换频繁的企业。缺点:Mybatis 无法做到数据库无关性,如果需要实现支持多种数据库的软件,则需要自定义多套 SQL 映射文件,工作量大。

    • Hibernate 是支持定制化 SQL、存储过程以及高级映射的一种持久层框架。Hibernate 对象-关系映射能力强,数据库的无关性好,Hirberate 可以自动生成 SQL 语句,对于关系模型要求高的软件,如果用 HIrbernate 开发可以节省很多时间。

  • 4)使用 Mybatis 的 mapper 接口调用时候有哪些要求

    • Mapper 接口方法名和 Mapper.xml 中定义的每个 SQL 的 id 相同;
    • Mapper 接口方法的输入参数类型和 Mapper.xml 中定义的每个 sql 的 parameterType 的类型相同
    • Mapper 接口方法的输出参数类型和 Mapper.xml 中定义的每个 sql 的 resultType 的类型相同
    • Mapper.xml 文件中的 namespace,就是接口的类路径。
  • 5)SQLMapConfig.xml 中配置有哪些内容

    • properties(属性)
    • settings(配置)
    • typeAliases(类型别名)
    • typeHandlers(类型处理器)
    • objectFactory(对象工厂)
    • plugins(插件)
    • environments(环境集合属性对象)
    • environment(环境子属性对象)
    • transactionManager(事务管理)
    • dataSource(数据源)
    • mappers(映射器)
  • 6)Mybatis 的一级缓存和二级缓存

    • 一级缓存:Mybatis 的一级缓存是指 SQLSession,一级缓存的作用域是 SQlSession, Mabits 默认开启一级缓存。在同一个 SQLSession 中,执行相同的 SQL 查询时;第一次会去查询数据库,并写在缓存中,第二次会直接从缓存中取。当执行 SQL 时候两次查询中间发生了增删改的操作,则 SQLSession 的缓存会被清空。每次查询会先去缓存中找,如果找不到,再去数据库查询,然后把结果写到缓存中。Mybatis 的内部缓存使用一个 HashMap,key 为 hashcode+statementId+sql 语句。Value 为查询出来的结果集映射成的 Java 对象。SQLSession 执行 insert、update、delete 等操作 commit 后会清空该 SQLSession 缓存。

    • 二级缓存:是 mapper 级别的,Mybatis 默认是没有开启二级缓存的。Mybatis 二级缓存是 SessionFactory,如果两次查询基于同一个 SessionFactory,那么就从二级缓存中取数据,而不用到数据库里去取了。

  • 7)Mapper 编写有几种方式

    • 方式 1:接口实现类集成 SQLSessionDaoSupport 此方法需要编写 mapper 接口,mapper 接口的实现类,mapper.xml 文件。
    • 方式 2:使用 org.mybatis.spring.mapper.MapperFactoryBean 此方法需要在 SqlMapConfig.xml 中配置 mapper.xml 的位置,还需定义 mapper 接口。
    • 方式 3:使用 mapper 扫描器,需要编写 mapper.xml 文件,需要 mapper 接口,配置 mapper 扫描器 @MapperScan("com.qianchia.dao"),使用扫描器从 Spring 容器中获取 mapper 的实现对象。
  • 8)Mybatis 的映射文件

    • Mybatis 真正强大的在于它的映射文件,它和 JDBC 代码进行比较,可以省掉 95% 的代码,Mybatis 就是针对 SQL 进行构建。

    • SQL 映射文件中几个顶级的元素:

      • cache – 给定命名空间的缓存配置。
      • cache-ref – 其他命名空间缓存配置的引用。
      • resultMap – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
      • sql – 可被其他语句引用的可重用语句块。
      • insert – 映射插入语句。
      • update – 映射更新语句。
      • delete – 映射删除语句。
      • select – 映射查询语句。
  • 9)Mybatis 动态 SQL

    • 传统的 JDBC 的方法,在组合 SQL 语句的时候需要去拼接,稍微不注意就会少少了一个空格,标点符号,都会导致系统错误。Mybatis 的动态 SQL 就是为了解决这种问题而产生的;Mybatis 的动态 SQL 语句值基于 OGNL 表达式的,方便在 SQL 语句中实现某些逻辑;可以使用标签组合成灵活的 SQL 语句,提供开发的效率。

    • 2) Mybatis 的动态 SQL 标签主要由以下几类:

      • If 语句(简单的条件判断)。
      • Choose(when/otherwise),相当于 Java 语言中的 switch,与 jstl 中 choose 类似。
      • Trim(对包含的内容加上 prefix,或者 suffix)。
      • Where(主要是用来简化 SQL 语句中 where 条件判断,能智能的处理 and/or 不用担心多余的语法导致的错误)。
      • Set(主要用于更新时候)。
      • Foreach(一般使用在 Mybatis in 语句查询时特别有用)。
  • 10)Mybatis 分页查询

    • Mybatis 本身有分页查询,但是并不是真正的分页查询,它是把数据查出来放在内存里面,你想要什么就给你什么。

    • 我们使用 Mybatis 实现分页查询的时候,是要实现真分页查询,就是要用 SQL 语句来实现分页查询。

    • MySQL 和 Oracle 两种数据库的实现方法是不一样的。

      • Mysql:select * from table limit N, M; 其中:N 表示从第几页开始,M 表示每页显示的条数。比如:数据库中有 30 条数据,要求每页显示 10 条,显示第 2 页的所有数据。SQL 语句就可以写成:Limit 10 , 20;
      • Oracle 实现分页查询:采用伪列 ROWNUM。
  • 11)Mybais 常用注解

    • @Select:查询 sql, 和 xml select sql 语法完全一样。
    • @Insert:插入 sql, 和 xml insert sql 语法完全一样。
    • @Update:更新 sql, 和 xml update sql 语法完全一样。
    • @Delete:删除 sql, 和 xml delete sql 语法完全一样。
    • @Param:入参。
    • @Results:结果集合。
    • @Result:结果。
  • 12)Mybatis 的表关联的映射

    • 一对一关联

      • property: 对象属性名称
      • javaType: 对象属性的类型
      • column: 所对应的外键字段的名称
      • select: 使用另一个查询封装的结果
    • 一对多关联

    • 多对多关联
  • 13)Mybatis 与 Spring 的整合

    • Spring 是一个轻量级控制反转(IOC)和面向切面(AOP)的容器框架;AOP 和 IOC 是 Spring 框架重要的两个模块;控制反转就是改变对象的创建方式,将对象的创建和维护由开发人员创建改为由容器帮我们完成创建和维护。

    • 2)Mybatis 是支持 SQL 查询,存储过程和高级映射的优秀持久成框架。Mybatis 几乎是消除了使用 JDBC 存在的重复创建和关闭连接,以及结果集查询的问题。它使用简单的 xml 或者注解用于配置和映射,将 Java 的 POJOs 映射成数据库中的记录。

    • 3)整合,涉及的常用包:

  • 14)Mybatis 与 Spring MVC 的整合

    • Spring 整合 Mybatis 底层原理实现步骤

      • 1.首先 Spring 主要做对 Bean 的管理,包含 bean 的初始化等信息 spring 内部有一个 BeanDefinition 有一系列对 Bean 的描述,包含单例/多例 beanName BeanClass 等信息,也可以理解成 标签所描述的信息。
      • 2.spring 集成了 Mybatis 那么也要管理 mybatis 相关联的 bean,我们经常使用的通常是定义 xxxMapper 的接口,想要接口被 Spring 管理必须需要对应类实例化,所以就需要的动态代理相关内容,我们所知的已知接口可以使用 JDK 动态代理为 mapper 相关生成代理类,放入 spring 容器种等待调用 然后就可以使用 @Autowired xxxMapper 获取具体的实例。
      • 3.mybatis 相关代理类生成的时机,已知 spring 的 bean 的流程 扫描指定包路径下的 Class 文件,根据 class 信息生成对应的 BeanDefinition 这里根据时机修改 beanDefinition 信息也就是为 mapper 生成代理类 根据 BD 生成 bean 实例,把实例放入 spring 容器中。
      • 4.spring 处理的流程是 class –> 组建 beanDefinition -> 为 mapper 生成对应的 BeanDefinition –> BeanFactory 进行处理 –> beanFactory 后续处理 –> 实例化生成 Bean 实例放入 spring 容器中 至于如何生成 bean 就可以由 FactoryBean 去自定义生成我们需要的 mapper 的 bean 对象。
    • 正式环境使用中

      • 1.使用 @MapperScan 注解放入需要扫描的 mapper 的接口包路径。
      • 2.内部使用 @Import 导入 MapperScannerRegistrar.class 来实现生成代理 Mapper 类的步骤。
      • 3.实现 registerBeanDefinition 方法扫描 @MapperScan 注解拿到扫描路径。
      • 4.调用 spring 类拿到 BeanDefinition 需要的所有的 Mapper 接口等待生成代理类。
      • 5.创建的 definition 设置构造方法传递的是被代理类的 Class 及我们自己写的 Mapper 接口,为 definition 设置 beanClass 为 MapperFacotryBean.class(org.mybatis.spring.mapper.MapperFactoryBean) 添加到 spring 容器中。
      • 6.在实例化 bean 实例的时候调用 MapperFactoryBean 的 getObject 方法里面使用 jdk 动态代理生成对应的代理实例,供项目使用 至此 spring中整合 myatis 并合理使用。
文章目录
  1. 1. 前言
  2. 2. 1、MyBatis
  3. 3. 2、配置
    1. 3.1. 2.1 Spring Boot 集成
  4. 4. 3、SQL 注入
    1. 4.1. 3.1 # 和 $ 的区别
    2. 4.2. 3.2 Mybatis 防止 SQL 注入
  5. 5. 4、CRUD 操作
    1. 5.1. 4.1 基本操作
    2. 5.2. 4.2 条件查询
    3. 5.3. 4.3 多关系查询
    4. 5.4. 4.4 动态 SQL 语句
      1. 5.4.1. 4.4.1 if 标签
      2. 5.4.2. 4.4.2 where 标签
      3. 5.4.3. 4.4.3 set 标签
      4. 5.4.4. 4.4.4 trim 标签
      5. 5.4.5. 4.4.5 choose 标签
      6. 5.4.6. 4.4.6 foreach 标签
      7. 5.4.7. 4.4.7 bind 标签
  6. 6. 5、使用注解方式
    1. 6.1. 5.1 CRUD
    2. 6.2. 5.2 多关系查询
    3. 6.3. 5.3 动态 SQL 语句
    4. 6.4. 5.4 SQL 类
  7. 7. 6、相关概念
    1. 7.1. 6.1 日志
    2. 7.2. 6.2 事务管理
    3. 7.3. 6.3 延迟加载
    4. 7.4. 6.4 分页
      1. 7.4.1. 6.4.1 PageHelper
    5. 7.5. 6.5 缓存
    6. 7.6. 6.6 C3P0 连接池
    7. 7.7. 6.7 逆向工程
  8. 8. 7、Mybatis 常见面试题
隐藏目录