mybatis第二天——动态SQL与关联查询

  大纲摘要:

    1、输入映射和输出映射

      a) 输入参数映射

      b) 返回值映射

    2、动态sql

      a) If

      b) Where

      c) Foreach

      d) Sql片段

    3、关联查询

      a) 一对一关联

      b) 一对多关联

【更新】:延迟加载

一、输入映射和输出映射

  1.输入映射

  也就是day01提到的入参 parameterType

  传递简单类型:见day01,这里不再赘述

  传递POJO包装类型:

      开发中通过pojo传递查询条件 ,查询条件是综合的查询条件,

      不仅包括用户查询条件还包括其它的查询条件(比如将用户购买商品信息也作为查询条件),

      这时可以使用包装对象(VO?)传递输入参数。即POJO类中包含POJO。

  创建POJO包装类QueryVo:

package cn.pojo;

/**
 * view object 视图层对象,用来做一些查询等,拓展性较好
 * @author jiangbei01
 *
 */
public class QueryVo {

    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
    
}

   mapper文件入参设置SQL:

<!-- 测试入参的SQL -->
    <select id="findUserByVo" parameterType="cn.pojo.QueryVo" resultType="cn.pojo.User">
        SELECT * FROM user where username like '%${user.username}%' AND sex=#{user.sex}
    </select>

  #{}中的要和层级属性名称一致

  接口中增加查询方法

public interface UserMapper {

    User findById(Integer id);
    //返回值是list集合时,mybatis会自动调用selectList()方法
    List<User> findByName(String username);
    void insertUser(User user);
    List<User> findUserByVo(QueryVo vo);
}

  测试函数:

@Test
    public void findUserByVo() throws Exception{
        SqlSession session = factory.openSession();
        //通过getMapper()方法实例化实现类
        UserMapper mapper = session.getMapper(UserMapper.class);
        //创建vo对象
        QueryVo vo = new QueryVo();
        //创建vo的属性user
        User user = new User();
        user.setUsername("宋江");
        user.setSex("2");
        user.setBirthday(new Date());
        user.setAddress("北京");
        vo.setUser(user);
        List<User> list = mapper.findUserByVo(vo);
        System.out.println(list);
        
    }

   2.输出映射

   也就是 resultType 【更新】:resultMap

    如果查询的sql的列名有别名,那么这个别名就是和属性映射的列名。

  mybatis如何按照字段名与属性名对应呢:

    解决办法一: 通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致,这样就可以表的字段名和实体类的属性名一一对应上了,这种方式是通过在sql语句中定义别名来解决字段名和属性名的映射关系的。

    解决办法二: 通过来映射字段名和实体类属性名的一一对应关系(配置驼峰式的命名规范)。这种方式是使用MyBatis提供的解决方式来解决字段名和属性名的映射关系的

    个人理解:可以通过逆向工程自动生成对应,简单对应无需手动配置,复杂配置可以通过resultMap进行配置

  输出简单类型(例如返回整形):

  配置文件中增加相关的配置:

<!-- 测试输出整形 -->
    <select id="findUserCount" resultType="java.lang.Integer">
        SELECT count(*) from user
    </select>

  mapper接口增加方法

Integer findUserCount();

  测试函数:

@Test
public void findUserCount() throws Exception{
SqlSession session = factory.openSession();
//通过getMapper()方法实例化实现类
UserMapper mapper = session.getMapper(UserMapper.class);
int count = mapper.findUserCount();
System.out.println(count);

}

  //注意前面有一个before的初始化的方法

   输出POJO对象与POJO列表:

      参见day01

  resultMap

    使用resultMap进行结果映射时,不需要查询的列名和映射的属性名必须一致(也就是属性名对应别名)。但是需要声明一个resultMap,来对列名和属性名进行映射。

    (详细见下文第三章 方式二:手动映射处)

二、动态SQL

    动态SQL可以提高SQL的重用性

  1.where if

  一般使用场景为高级查询,多条件组合查询等场景(查询条件不确定)

    JDBC阶段的处理方案是 字符串的拼接(where 1=1)

  以下演示拼接SQL的方案:

    配置文件mapper.xml中添加配置:

<!-- 动态SQL -->
    <select id="findUserByUsernameAndSex" parameterType="cn.pojo.User" resultType="cn.pojo.User">
        SELECT * FROM user where 1=1 
        <if test="username != null and username != ''">
            and username like '%${username}%'
        </if>
        <if test="sex != null and sex != ''">
            and sex=#{sex}
        </if>
    </select>

  //if为判断,只能条件成立才拼接SQL,若不传参则查询所有,注意条件应当双重判断,明显 1=1不优雅又容易出别的问题

  接口中添加方法:

List<User> findUserByUsernameAndSex(User user);

  测试函数:

@Test
    public void testFindUserByUsernameAndSex() throws Exception{
        SqlSession session = factory.openSession();
        //通过getMapper()方法实例化实现类
        UserMapper mapper = session.getMapper(UserMapper.class);
        User user = new User();
        user.setUsername("王");
        user.setSex("2");
        List<User> list = mapper.findUserByUsernameAndSex(user);
        System.out.println(list);
        
    }

  接下来介绍where关键字的使用:

  修改配置文件,增加where关键字

<select id="findUserByUsernameAndSex" parameterType="cn.pojo.User" resultType="cn.pojo.User">
        SELECT * FROM user
        <!-- where标签作用:
            1.会自动向SQL语句中添加where关键字
            2.会去掉第一个条件的关键字
            综上 1=1 的非人性化可以去掉
         -->
        <where>
            <if test="username != null and username != ''">
            and username like '%${username}%'
        </if>
        <if test="sex != null and sex != ''">
            and sex=#{sex}
        </if>
        </where>
        
    </select>

  //这里真正执行之前Mybatis会进行SQL字符串的处理(去掉第一个条件的AND等的处理操作)

  这里引出一个小问题,若下面还要用到name和sex的查询条件,此语句又要写一遍,

  显然,我们需要对其进行封装,以便整个文件都可以随处调用

  也就是Sql的片段

  这里,我们使用<sql>标签进行封装:

<!-- 封装SQL条件以便复用 -->
    <sql id="UserWhere">
        <!-- where标签作用:
            1.会自动向SQL语句中添加where关键字
            2.会去掉第一个条件的关键字
            综上 1=1 的非人性化可以去掉
         -->
        <where>
            <if test="username != null and username != ''">
            and username like '%${username}%'
        </if>
        <if test="sex != null and sex != ''">
            and sex=#{sex}
        </if>
        </where>
    </sql>

    【更新】:一般而言,为了提高sql的可重用性,一般而言不要在里面加 <where> <select>等sql标签,这样大大降低了可重用性!

  于是我们把响应的sql定义的片段和sql引用的地方进行改动:

    <sql id="UserWhere">
        <!-- where标签作用:
            1.会自动向SQL语句中添加where关键字
            2.会去掉第一个条件的关键字
            综上 1=1 的非人性化可以去掉
         -->
            <if test="username != null and username != ''">
            and username like '%${username}%'
        </if>
        <if test="sex != null and sex != ''">
            and sex=#{sex}
        </if>
    </sql>
View Code
<select id="findUserByUsernameAndSex" parameterType="cn.pojo.User" resultType="cn.pojo.User">
        SELECT * FROM user
        <!-- 调用SQL条件 -->
        <where>
            <include refid="UserWhere"></include>
        </where>
    </select>
View Code

  封装好后进行调用(使用include):

<select id="findUserByUsernameAndSex" parameterType="cn.pojo.User" resultType="cn.pojo.User">
        SELECT * FROM user
        <!-- 调用SQL条件 -->
        <include refid="UserWhere"></include>
    </select>

   //见上文更新处改动

  2.foreach

  (SQL中应当避免 or 关键字的使用,效率严重下降,改用 in 等参数替代)

  演示查询id符合某个集合特征的(例如查询id为 1 16 28的)

    配置文件mapper.xml中加入相关的配置: 

<!-- 要传入的是一个id的集合,此时就体现了vo的好处了 -->
    <select id="findUserByIds" parameterType="cn.pojo.QueryVo" resultType="cn.pojo.User">
        <!-- 首先where应该通过动态拼接 -->
        <!-- SELECT * FROM user where id IN(1,16,28) -->
        SELECT * FROM user
        <where>
            <if test="ids != null">
                <!-- foreach循环传入的集合参数
                    collection:传入的集合变量名称(POJO中集合属性的名称)
                    item:每次循环的变量,与EL等类同
                    open:循环开始拼接的字符串
                    close:循环结束拼接的字符串
                    separator:循环中拼接的分隔符
                 -->
                <foreach collection="ids" item="id" open="id IN(" close=")" separator=",">
                    #{id}
                </foreach>
            </if>
        </where>
    </select>

   //where由<where>标签拼接

  //collenctio是传进来的集合 ,此处是vo的集合属性 ids

  //开始和结束字符串已经定死为 id IN(   和 )

  //item迭代遍历传入占位符 #{} 中进行拼接

  //分隔符 separator 为 , 

  //于是得到完整SQL(见下方控制台日志输出的SQL)

  【更新】:有时候为了open 和 close 都是 括号,可以将 id IN 可以提取出来,放在forreach前面,这时Open里就可以只写 ( 了,

      由于是where语句,里面可以写 AND id IN等(会自动处理这个AND)

    此时体现出vo的好处了,查询条件复杂时,例如这里需要传入一个集合,就可以借助vo

package cn.pojo;

import java.util.List;

/**
 * view object 视图层对象,用来做一些查询等,拓展性较好
 * @author jiangbei01
 *
 */
public class QueryVo {

    private User user;
    private List<Integer> ids;

    public List<Integer> getIds() {
        return ids;
    }

    public void setIds(List<Integer> ids) {
        this.ids = ids;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
    
}
View Code

    在接口中定义方法:

List<User> findUserByIds(QueryVo vo);

    测试函数:

@Test
    public void findIds() throws Exception{
        SqlSession session = factory.openSession();
        //通过getMapper()方法实例化实现类
        UserMapper mapper = session.getMapper(UserMapper.class);
        QueryVo vo = new QueryVo();
        List<Integer> ids = new ArrayList<Integer>();
        ids.add(1);
        ids.add(16);
        ids.add(28);
        vo.setIds(ids);
        List<User> list = mapper.findUserByIds(vo);
        System.out.println(list);
    }

 //注意此时传入的是 vo ,vo里是集合

   看底层SQL:

DEBUG [main] - ==>  Preparing: SELECT * FROM user WHERE id IN( ? , ? , ? ) 
DEBUG [main] - ==> Parameters: 1(Integer), 16(Integer), 28(Integer)

 三、关联查询

  1.商品订单数据模型

  注意点:

    数据库中表与表之间的关系(主键外键非空字段?)

    实际业务之中表与表的关系

  2.一对一查询

  方法一:自动映射对应关系 

  使用resultType,定义订单信息po类,此po类中包括了订单信息和用户信息:(此方式通过继承来实现!)

package cn.pojo;

import java.util.Date;

public class CustomerOrder extends Orders{

    /*不能直接放private User user;这样整个实体放进入,不能找到单个属性,没有这样的级联属性*/
    //这样就有订单和用户所有的信息了
    private int uid;
    private String username;// 用户姓名
    private String sex;// 性别
    private Date birthday;// 生日
    private String address;// 地址
    public int getUid() {
        return uid;
    }
    public void setUid(int uid) {
        this.uid = uid;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }

}
View Code

  映射的XML代码:

<!-- 一对一自动映射 -->
    <select id="findOrderAndUser" resultType="cn.pojo.CustomerOrder">
        SELECT o.*,u.id uid,u.address,u.birthday,u.sex,u.username 
        FROM orders o,user u 
        WHERE o.user_id=u.id
    </select>

  接口方法:

List<CustomerOrder> findOrderAndUser();

  测试函数:

@Test
    public void findOrderAndUser() throws Exception{
        SqlSession session = factory.openSession();
        //通过getMapper()方法实例化实现类
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<CustomerOrder> list = mapper.findOrderAndUser();
        //通常这种复杂的查询都打个断点看看内部查询结果结构
        System.out.println(list);
    }

  小结:

    定义专门的PO类来放所有属性,使得配置文件更加简单清晰

      (但这里采用了部分继承的方式,由此也容易得到代码不优雅,单继承的局限性等,属于取巧的手段)

  方法二、手动映射对应关系

     Orders类中加入User属性user 属性中用于存储关联查询的用户信息,(对象嵌套对象,而方法一则是平铺式的,全是一些普通字段!)

    因为订单关联查询用户是一对一关系,所以这里使用单个User对象存储关联查询的用户信息。

  PO类完整代码:

package cn.pojo;

import java.util.Date;

public class Orders {
    private Integer id;

    private Integer userId;

    private String number;

    private Date createtime;

    private String note;
    
    private User user;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number == null ? null : number.trim();
    }

    public Date getCreatetime() {
        return createtime;
    }

    public void setCreatetime(Date createtime) {
        this.createtime = createtime;
    }

    public String getNote() {
        return note;
    }

    public void setNote(String note) {
        this.note = note == null ? null : note.trim();
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    
    
}
View Code

  重要的是配置部分的更改:

<!-- 一对一手动映射 
        手动映射需要指定数据库字段和POJO属性的对应关系
    -->
    <!-- id为map的唯一标识
        type为要放的指定的对象中
     -->
    <resultMap type="cn.pojo.Orders" id="OrderAndUserResultMap">
        <!-- 对应关系,类似hibernate的配置映射 id对应主键 result对应其它属性 -->
        <id column="id" property="id"/>
        
        <!-- result:标签指定非主键字段的对应关系 -->
        <result column="user_id" property="userId"/>
        <result column="number" property="number"/>
        <result column="createtime" property="createtime"/>
        <result column="note" property="note"/>
        
        <!-- 这个标签指定单个对象的对应关系 
        property:指定将数据放入Orders中的user属性中
        javaType:user属性的类型
        -->
        <association property="user" javaType="cn.itheima.pojo.User">
           <!-- 强烈建议将id写上,虽然是可选!-->
       <id column="uid" property="id"/> <result column="username" property="username"/> <!-- <result column="birthday" property="birthday"/>--> <!-- <result column="sex" property="sex"/>--> <result column="address" property="address"/> </association> </resultMap> <select id="findOrderAndUser2" resultMap="OrderAndUserResultMap"> SELECT orders.*, user.username,user.address FROM orders,user WHERE orders.user_id = user.id </select>

 //这里是将查询出来的列 column 映射到指定的属性 propertity 中!(此处示例有不准确之处!)

更新示例:

<?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.itheima.mybatis.mapper.OrdersMapper">

    <!-- 一对一映射之resultType -->
    <select id="findOrdersAndUser" resultType="com.itheima.mybatis.po.OrdersExt">
        SELECT
        orders.`id`,
        orders.`user_id`,
        orders.`number`,
        user.`username`,
        user.`sex`
        FROM
        orders,
        USER
        WHERE orders.`user_id` = user.`id`
    </select>

    <!-- OrdersAndUserRstMap -->
    <resultMap type="com.itheima.mybatis.po.OrdersExt" id="OrdersAndUserRstMap">
        <!-- 订单信息 -->
        <id column="id" property="id" />
        <result column="user_id" property="userId" />
        <result column="number" property="number" />

        <!-- 用户信息(一对一) -->
        <!-- association:一对一关联映射 -->
        <!-- property:关联信息查询的结果将要映射的扩展类中的对象属性名称 -->
        <!-- id标签:建议在关联查询时必须写上,不写不会报错,但是会影响性能 -->
        <association property="user" javaType="com.itheima.mybatis.po.User">
            <id column="user_id" property="id" />
            <result column="username" property="username" />
            <result column="sex" property="sex" />
        </association>
    </resultMap>

    <!-- 一对一映射之resultMap -->
    <select id="findOrdersAndUserRstMap" resultMap="OrdersAndUserRstMap">
        SELECT
        orders.`id`,
        orders.`user_id`,
        orders.`number`,
        user.`username`,
        user.`sex`
        FROM
        orders,
        USER
        WHERE orders.`user_id` = user.`id`
    </select>

    <!-- OrdersAndDetailRstMap -->
    <!-- extends:可以继承一个已有的resultMap,指定resultMap的唯一标示即可 -->
    <!-- 注意:继承时,只能继承type类型是一样的resultMap -->
    <resultMap type="com.itheima.mybatis.po.OrdersExt" id="OrdersAndDetailRstMap"
        extends="OrdersAndUserRstMap">
        <!-- 订单明细信息(一对多) -->
        <!-- collection:映射一对多关系 -->
        <collection property="detailList" ofType="com.itheima.mybatis.po.Orderdetail">
            <id column="detailId" property="id" />
            <result column="items_id" property="itemsId" />
            <result column="items_num" property="itemsNum" />
        </collection>

    </resultMap>

    <!-- 一对多映射 -->
    <select id="findOrdersAndDetailRstMap" resultMap="OrdersAndDetailRstMap">
        SELECT
        orders.`id`,
        orders.`user_id`,
        orders.`number`,
        user.`username`,
        user.`sex`,
        orderdetail.`id` detailId,
        orderdetail.`items_id`,
        orderdetail.`items_num`
        FROM
        orders,
        USER,
        orderdetail
        WHERE
        orders.`user_id` = user.`id`
        AND orders.`id` = orderdetail.`orders_id`
    </select>

    <!-- UserAndItemsRstMap -->
    <resultMap type="com.itheima.mybatis.po.User" id="UserAndItemsRstMap">
        <!-- 用户信息 -->
        <id column="user_id" property="id" />
        <result column="username" property="username" />
        <result column="sex" property="sex" />
        <!-- 订单信息(一对多) -->
        <collection property="orders" ofType="com.itheima.mybatis.po.Orders">
            <id column="id" property="id" />
            <result column="user_id" property="userId" />
            <result column="number" property="number" />
            <!-- 订单明细信息(一对多) -->
            <collection property="detailList" ofType="com.itheima.mybatis.po.Orderdetail">
                <id column="detailId" property="id" />
                <result column="items_id" property="itemsId" />
                <result column="items_num" property="itemsNum" />
                <!-- 商品信息(一对一) -->
                <association property="items" javaType="items">
                    <id column="items_id" property="id" />
                    <result column="name" property="name" />
                    <result column="price" property="price" />
                </association>
            </collection>
        </collection>
    </resultMap>

    <!-- 多对多 -->
    <select id="findUserAndItemsRstMap" resultMap="UserAndItemsRstMap">
        SELECT
        orders.`id`,
        orders.`user_id`,
        orders.`number`,
        user.`username`,
        user.`sex`,
        orderdetail.`id` detailId,
        orderdetail.`items_id`,
        orderdetail.`items_num`,
        items.`name`,
        items.`price`
        FROM
        orders,
        USER,
        orderdetail,
        items
        WHERE orders.`user_id` = user.`id`
        AND orders.`id` =
        orderdetail.`orders_id`
        AND orderdetail.`items_id` = items.`id`
    </select>
    <!-- lazyLoadingRstMap -->
    <resultMap type="ordersExt" id="lazyLoadingRstMap">
        <!-- 订单信息 -->
        <id column="id" property="id" />
        <result column="user_id" property="userId" />
        <result column="number" property="number" />
        <!-- 用户信息(一对一) -->
        <!-- select:指定关联查询的查询statement(即查询用户的statement的id),然后将查询结果,封装到property属性指定的变量中 -->
        <!-- column:通过column指定的列所查询出的结果,作为select指的statement的入参 -->
        <!-- 注意:如果select指定的statement,入参需要多个值,需要在column中{col1=prop1,col2=prop2} -->
        <association property="user"
            select="com.itheima.mybatis.mapper.UserMapper.findUserById" column="user_id"></association>
    </resultMap>

    <!-- 延迟加载 -->
    <select id="findOrderAndUserLazyLoading" resultMap="lazyLoadingRstMap">
        SELECT * FROM
        orders
    </select>
</mapper>
View Code

   //当然,这里的SQL的多表查询使用的是方言,更推荐的标准的SQL:JOIN ON 

SELECT o.id, o.user_id, o.number, o.createtime, o.note, u.username, u.address
FROM orders o
JOIN `user` u ON u.id = o.user_id

  这里resultMap指定orderUserResultMap

  association:表示进行关联查询单条记录

  property:表示关联查询的结果存储在cn.itcast.mybatis.po.Ordersuser属性中

  javaType:表示关联查询的结果类型

  <id property="id" column="user_id"/>:查询结果的user_id列对应关联对象的id属性,这里是<id />表示user_id是关联查询对象的唯一标识。

  <result property="username" column="username"/>:查询结果的username列对应关联对象的username属性。

  接口方法(不妨先回忆如何编写接口方法)

List<Orders> findOrderAndUser2();

  //注意返回值的写法

  测试函数与上方法一类同,不再赘述

  小结:

    此方法为Mybatis提供的标准方法,只是配置稍显麻烦

【更新】:resultType是平铺式的,而resultMap是对象嵌套对象形式的!两者场景不同!

   3.一对多查询

  案例:查询所有用户信息及用户关联的订单信息。

  用户信息和订单信息为一对多关系。

  使用resultMap实现如下:

  定义po

  在User类中加入List<Orders> orders属性

   完整类如下:

package cn.pojo;

import java.util.Date;
import java.util.List;

public class User {
    private int id;
    private String username;// 用户姓名
    private String sex;// 性别
    private Date birthday;// 生日
    private String address;// 地址
    
    private List<Orders> orderList;

    public List<Orders> getOrderList() {
        return orderList;
    }
    public void setOrderList(List<Orders> orderList) {
        this.orderList = orderList;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", username=" + username + ", sex=" + sex
                + ", birthday=" + birthday + ", address=" + address + "]";
    }

    
    

}
View Code

  mapper.xml配置文件:

<!-- 一对多映射 -->
    <!-- resultMap类同上述配置 -->
    <resultMap type="cn.pojo.User" id="UserAndOrderResultMap">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="birthday" property="birthday"/>
        <result column="sex" property="sex"/>
        <result column="address" property="address"/>
        <!-- 指定对应的集合关系的集合 
            property:放入User对象的集合名 orderList
            offType:指定orderList的泛型类型
        -->
        <collection property="orderList" ofType="cn.pojo.Orders">
            <id column="oid" property="id"/>
            <result column="user_id" property="userId"/>
            <result column="number" property="number"/>
            <result column="createtime" property="createtime"/>
        </collection>
    </resultMap>
    <select id="findUserAndOrder" resultMap="UserAndOrderResultMap">
        select a.*, b.id oid ,user_id, number, createtime 
        from user a, orders b 
        where a.id = b.user_id
    </select>

//注意检查SQL(应先做测试)

【更新】:resultMap可以继承

<!-- extends:可以继承一个已有的resultMap,指定resultMap的唯一标示即可 -->
    <!-- 注意:继承时,只能继承type类型是一样的resultMap -->
    <resultMap type="com.itheima.mybatis.po.OrdersExt" id="OrdersAndDetailRstMap"
        extends="OrdersAndUserRstMap">

  接口方法:

List<User> findUserAndOrder();

  测试函数:

@Test
    public void testfindUserAndOrder() throws Exception{
        SqlSession session = factory.openSession();
        //通过getMapper()方法实例化实现类
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<User> list = mapper.findUserAndOrder();
        //通常这种复杂的查询都打个断点看看内部查询结果结构
        System.out.println(list);
    }

  //出现错误应该重点先查看错误信息,这是重要的解决来源之一!之后才是考虑复制错误流查阅相关引擎等

【更新】:学完可以加深前面的介绍的理解了:mybatis可以让开发人员只关注SQL本身!其它的用法用熟了基本也没问题。

  【更新】:延迟加载 :也叫按需加载

    在mybatis中,只有 resultMap 标签的 association 标签和 collection 标签两个标签具有懒加载的功能

<!-- lazyLoadingRstMap -->
    <resultMap type="ordersExt" id="lazyLoadingRstMap">
        <!-- 订单信息 -->
        <id column="id" property="id" />
        <result column="user_id" property="userId" />
        <result column="number" property="number" />
        <!-- 用户信息(一对一) -->
        <!-- select:指定关联查询的查询statement(即查询用户的statement的id),然后将查询结果,封装到property属性指定的变量中 -->
        <!-- column:通过column指定的列所查询出的结果,作为select指的statement的入参 -->
        <!-- 注意:如果select指定的statement,入参需要多个值,需要在column中{col1=prop1,col2=prop2} -->
        <association property="user"
            select="com.itheima.mybatis.mapper.UserMapper.findUserById" column="user_id"></association>
    </resultMap>

    <!-- 延迟加载 -->
    <select id="findOrderAndUserLazyLoading" resultMap="lazyLoadingRstMap">
        SELECT * FROM
        orders
    </select>

//懒加载中懒加载中的参数是通过上面的查询出的列作为入参再进行查询的

  //N+1现象:一条查询SQL,查出多条记录,每条记录又对应一条SQL,数据量大时是非常不利的

  核心配置文件中开启懒加载:

<settings>
        <!-- 开启延迟加载 ,默认值为true-->
        <setting name="lazyLoadingEnabled" value="true"/>
        
        <!-- 设置积极的懒加载,默认是true -->
        <setting name="aggressiveLazyLoading" value="false"/>
        
        <!-- 二级缓存的总开关 -->
        <setting name="cacheEnabled" value="true"/>
    </settings>

   【更新】:mybatis的缓存

    Mybatis的缓存,包括一级缓存和二级缓存

    一级缓存指的就是sqlsession,在sqlsession中有一个数据区域,是map结构,这个区域就是一级缓存区域。一级缓存中的key是由sql语句、条件、statement等信息组成一个唯一值。一级缓存中的value,就是查询出的结果对象。(如果增删改不提交,可能出现脏读!)

    二级缓存指的就是同一个namespace下的mapper,二级缓存中,也有一个map结构,这个区域就是一级缓存区域。一级缓存中的key是由sql语句、条件、statement等信息组成一个唯一值。一级缓存中的value,就是查询出的结果对象。

    一级缓存是默认使用的。

    二级缓存需要手动开启。(这点类同hibernate),开启也是在核心配置文件开启(待更新redis)

      ——禁用缓存、刷新缓存。。。

    这里需要注意的是,mybatis的缓存是被抛弃的(和hibernate一样),所以这里不做展开(不支持分布式

  什么叫分布式呢?

    为了提高性能,对系统采取分布式部署(集群部署的方式)

原文地址:https://www.cnblogs.com/jiangbei/p/6888974.html