MyBatis框架进阶(一),动态Sql,多表关联查询与延迟加载策略


回顾:

MyBatis框架快速入门(一),搭建MyBatis开发环境
MyBatis框架快速入门(二),使用映射器接口代理对象的方式实现单表的增删改查与配置文件参数的深入学习


动态sql

应用场景:如根据不同条件搜索信息,SQL语句通常是动态拼接。

动态 SQL 是 MyBatis 的强大特性之一。如不使用Mybatis的注解开发,SQL语句写在映射配置文件(XML文件)中, MyBatis提供了一些XML标签,可使我们更为便捷的实现动态SQL的拼接。

if标签where标签的使用 ——>根据用户名或id (实体类)搜索用户信息

   //if标签与where标签的使用   根据用户的名称或id搜索用户信息  根据实体类的不同取值,使用不同的SQL语句来进行查询
   List<User> search(User user);
 	<!--if标签与where标签的使用-->
    <!--  select * from t_user where 1=1 and username like ? and id > ? -->
    <select id="search" resultType="user" parameterType="user">

        <!--一般写法
        select * from t_user where 1=1
           <if test="username!=null and username!='' ">
               and username like #{username}
           </if>

           <if test="id!=null and id!='' ">
               and id > #{id}
           </if>
         -->

        <!--使用<where></where>标签代替where 1=1-->
        select * from t_user
        <where>
      <!--  在if标签的test属性中直接写OGNL表达式,从parameterType中取值进行判断,不需要加#{}或者${}.  -->
            <if test="username!=null and username!='' ">
                <!--使用<where>标签时,Mybatis会自动处理第1个and-->
                username like #{username}
            </if>

            <if test="id!=null and id!='' ">
                and id > #{id}
            </if>
        </where>
    </select>

测试:

    @Test
   public void searchTest(){
        User user = new User();
        //user.setId(4);
        user.setUsername("%上官%");
        List<User> userList = mapper.search(user);
        for (User u:userList) {
            System.out.println(u);
        }
    }

运行输出: id 为空时, username 不为空所以只使用用户名作为条件查询

DEBUG m.mycode.dao.UserMapper.search  - ==>  Preparing: select * from t_user WHERE username like ? 
DEBUG m.mycode.dao.UserMapper.search  - ==> Parameters: %上官%(String)
DEBUG m.mycode.dao.UserMapper.search  - <==      Total: 4

where标签foreach标签的使用 ——> 根据id数组查询用户

   //where标签与foreach标签的使用  根据id数组查询用户
    List<User> searchByArray(Integer[] ids);
<!--foreach标签的使用 :循环遍历一个集合,把集合的内容拼接到SQL语句中
               collection属性:被循环遍历的对象  注意:注意不加#{} 
               open属性:SQL语句的开始部分
               item属性:代表被循环遍历中每个元素生成的变量名
               separator属性:分隔符
               close属性:SQL语句的结束部分
    标签体:使用#{OGNL}表达式,获取到被循环遍历对象中的每个元素-->

 <!--根据id数组查询用户-->
    <select id="searchByArray" parameterType="arraylist" resultType="user">
        select * from t_user
        <where>
            <foreach collection="array" open=" and id in(" item="id" separator="," close=")">
                #{id}
            </foreach>
        </where>
    </select>

测试:

    //where标签与foreach标签的使用  根据id数组查询用户
    @Test
    public void searchByArrayTest(){
        Integer[] integers = {3,4,16};
        List<User> userList = mapper.searchByArray(integers);
        for (User user : userList) {
            System.out.println(user);
        }
    }

运行输出:

DEBUG e.dao.UserMapper.searchByArray  - ==>  Preparing: select * from t_user WHERE id in( ? , ? , ? ) 
DEBUG e.dao.UserMapper.searchByArray  - ==> Parameters: 3(Integer), 4(Integer), 16(Integer)
DEBUG e.dao.UserMapper.searchByArray  - <==  Total: 3
User{id=3, username='王五', password='1', email='null', birthday=null}
User{id=4, username='上官婉儿', password='1', email='null', birthday=Fri Mar 13 00:00:00 CST 2020}
User{id=16, username='关羽', password='1', email='null', birthday=null}

where标签foreach标签的使用 ——> 根据id集合查询用户

    //where标签与foreach标签的使用  根据id集合查询用户
    List<User> searchByList(List<Integer> ids);
<!--foreach标签的使用 :循环遍历一个集合,把集合的内容拼接到SQL语句中
               collection属性:被循环遍历的对象  注意:注意不加#{} 
               open属性:SQL语句的开始部分
               item属性:代表被循环遍历中每个元素生成的变量名
               separator属性:分隔符
               close属性:SQL语句的结束部分
    标签体:使用#{OGNL}表达式,获取到被循环遍历对象中的每个元素-->

    <!--根据id集合查询用户-->
    <select id="searchByList" parameterType="arraylist" resultType="user">
        select * from t_user
        <where>
            <foreach collection="list" open=" and id in(" item="id" separator="," close=")">
                #{id}
            </foreach>
        </where>
    </select>

测试:

    //where标签与foreach标签的使用  根据id集合查询用户
    @Test
    public void searchByList(){
        List<Integer> list=new ArrayList<Integer>();
        list.add(3);
        list.add(5);
        List<User> userList = mapper.searchByList(list);
        for (User user : userList) {
            System.out.println(user);
        }
    }

运行输出:

DEBUG de.dao.UserMapper.searchByList  - ==>  Preparing: select * from t_user WHERE id in( ? , ? ) 
DEBUG de.dao.UserMapper.searchByList  - ==> Parameters: 3(Integer), 5(Integer)
DEBUG de.dao.UserMapper.searchByList  - <==      Total: 2
User{id=3, username='王五', password='1', email='null', birthday=null}
User{id=5, username='上官锅儿', password='1', email='null', birthday=null}

if标签where标签foreach标签的综合使用 ——> 根据包装类查询用户信息

UserWrapper 包装类

public class UserWrapper {
    private Integer[] ids;
    private User user;

    public User getUser() {
        return user;
    }

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

    public Integer[] getIds() {
        return ids;
    }

    public void setIds(Integer[] ids) {
        this.ids = ids;
    }

    @Override
    public String toString() {
        return "UserWrapper{" +
                "ids=" + Arrays.toString(ids) +
                ", user=" + user +
                '}';
    }
}
   //if标签,where标签与foreach标签的使用  根据多个id或用户名查询用户信息,包装类userWrapper引用这个id数组与User,即使用包装类信息查询用户信息
   List<User> searchByUserWrapper(UserWrapper userWrapper);

    <select id="searchByUserWrapper" resultType="user" parameterType="userwrapper">
        select * from t_user
        <where>
            <if test="user.username!=null and user.username!='' ">
                username like #{user.username}
            </if>

            <if test="ids!=null and ids.length>0 ">
                <foreach collection="ids" open="and id in(" item="id" separator="," close=")">
                    #{id}
                </foreach>
            </if>
        </where>
    </select>

测试:

    @Test
    public void searchByUserWrapper(){
        User user = new User();
        user.setUsername("%上官%");
        
        UserWrapper userWrapper = new UserWrapper();
        userWrapper.setIds(new Integer[]{3,4,5});
        userWrapper.setUser(user);

        List<User> userList = mapper.searchByUserWrapper(userWrapper);
        for (User u : userList) {
            System.out.println(u);
        }
    }

运行输出:

DEBUG UserMapper.searchByUserWrapper  - ==>  Preparing: select * from t_user WHERE username like ? and id in( ? , ? , ? ) 
DEBUG UserMapper.searchByUserWrapper  - ==> Parameters: %上官%(String), 3(Integer), 4(Integer), 5(Integer)
DEBUG UserMapper.searchByUserWrapper  - <== Total: 2
User{id=4, username='上官婉儿', password='1', email='null', birthday=Fri Mar 13 00:00:00 CST 2020}
User{id=5, username='上官锅儿', password='1', email='null', birthday=null}

⑤【扩展】:有两个参数根据的情况 ——>根据这两个参数搜索用户信息@Param注解的使用。

    //有两个参数根据,根据这两个参数搜索用户信息
    //方式一,使用注解的方式(一般使用这种方式)
    List<User> searchByNameAndId(@Param("username") String username, @Param("id") Integer id);
    //方式二
    //List<User> searchByNameAndId(String username, Integer id);
     <!--有两个参数根据的情况,根据这两个参数搜索用户信息-->
    <!--方式一,使用注解的方式(一般使用这种方式)-->
    <select id="searchByNameAndId" resultType="user">
          select * from t_user where username like #{username} and id > #{id}
      </select>
    <!--方式二
    <select id="searchByNameAndId" resultType="user">
        select * from t_user where username like #{arg0} and id > #{arg1}
    </select>
    -->

测试:

    @Test
    public  void searchByNameAndIdTest(){
        List<User> userList = mapper.searchByNameAndId("%上官%", 4);
        for (User user : userList) {
            System.out.println(user);
        }
    }

运行输出:

DEBUG o.UserMapper.searchByNameAndId  - ==>  Preparing: select * from t_user where username like ? and id > ? 
DEBUG o.UserMapper.searchByNameAndId  - ==> Parameters: %上官%(String), 4(Integer)
DEBUG o.UserMapper.searchByNameAndId  - <==  Total: 3
User{id=5, username='上官锅儿', password='1', email='null', birthday=null}
User{id=6, username='上官瓢儿', password='1', email='null', birthday=null}
User{id=21, username='上官盖儿', password='1', email='null', birthday=null}

⑥【扩展】:根据Map查询用户

    //根据Map查询用户
    List<User> searchByMap(Map<String, String> map);
    <!--这里通过#{username}作为key值得到value值,进行查询  这里的parameterType可不写,Mybatis会判断-->
    <select id="searchByMap" resultType="user">
        select * from t_user where username like #{username}
    </select>

测试:

    @Test
    public void testSearchByMap(){
        Map<String, String> map = new HashMap<String, String>();
        map.put("username", "%上官%");

        List<User> userList = mapper.searchByMap(map);
        for (User user : userList) {
            System.out.println(user);
        }
    }

运行输出:

DEBUG ode.dao.UserMapper.searchByMap  - ==>  Preparing: select * from t_user where username like ? 
DEBUG ode.dao.UserMapper.searchByMap  - ==> Parameters: %上官%(String)
DEBUG ode.dao.UserMapper.searchByMap  - <==  Total: 4
User{id=4, username='上官婉儿', password='1', email='null', birthday=Fri Mar 13 00:00:00 CST 2020}
User{id=5, username='上官锅儿', password='1', email='null', birthday=null}
User{id=6, username='上官瓢儿', password='1', email='null', birthday=null}
User{id=21, username='上官盖儿', password='1', email='null', birthday=null}

⑦【扩展】:sql标签include标签

在映射配置文件中,对于重复的SQL片段,可以把重复的SQL片段抽取出来,以便重复使用。

    //sql标签与include的使用
   List<User> selectAllUser();
    <!--sql标签:在映射配置文件中定义SQL片段
           id属性:唯一标识
           标签体:sql语句片段
    -->
    <sql id="select">
        select * from t_user
    </sql>


    <select id="selectAllUser" resultType="user">
        <!-- include标签:在映射配置文件中使用SQL片段
               refid属性:写定义SQL片段的id
        扩展:如果想要引入其它映射配置文件中的sql片段,那么<include>标签的refid的值,需要在sql片段的id前指定namespace。
        -->
        <include refid="select"></include>
    </select>

    <!--相当于
     <select id="selectAllUser" resultType="user">
        select * from t_user
    </select>
    -->
  	

多表关联查询

在这里插入图片描述
Mybatis 把多对一当成一对一处理;把多对多当成一对多处理。


如有用户表(user)和帐户表(account),一个用户有多个帐户,一个帐户只属于一个用户,帐户表中有外键指向用户表主键。

  • 用户表(user)
    在这里插入图片描述
  • 帐户表(account)
    在这里插入图片描述
  1. 查询账户信息及关联的用户信息
    在这里插入图片描述
    在这里插入图片描述
  2. 查询用户信息,以及每个用户拥有的所有帐号信息
    在这里插入图片描述在这里插入图片描述

MyBatis实现一对一(多对一)关联查询

示例:查询账户信息及关联的用户信息

回顾:
MyBatis开发环境搭建
使用映射器接口代理对象的方式实现单表的增删改查

  • user表和account表的实体类
public class Account {
    private Integer id;
    private Integer uid;
    private Double money;
    /*在封装结果集时,使用resultMap设置字段和属性的对应关系,
    使用resultMap的子标签association封装关联的User对象。*/
    private User user;
    ......
    .......
}

public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    private List<Account> accountList;
	....
	......
}
  • 映射器接口AccountMapper
public interface AccountMapper {
    //一对一(多对一)关联查询:查询账户信息及关联的用户信息
    List<Account> queryAllAccount();
}

  • 映射器AccountMapper的配置文件AccountMapper.xml
<?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.mycode.mapper.AccountMapper">
    <!--SELECT * FROM account a LEFT JOIN USER u ON a.uid=u.id  account与USER有字段名相同,
    使用Mybatis帮我们封装结果集,需起别名-->
    <select id="queryAllAccount" resultMap="accountUserMap">
        SELECT u.*,a.id aid,a.uid,a.money FROM account a LEFT JOIN USER u ON a.uid=u.id
    </select>
    <!--
      resultMap标签:设置结果集中字段名和JavaBean属性的对应关系
              id属性:唯一标识
              type属性:结果集的数据要封装成的对象的全限定类名或别名
   -->
    <resultMap id="accountUserMap" type="account">
        <!--id标签:主键字段配置。  property属性:JavaBean的属性名  column属性:字段名-->
        <id property="id" column="aid"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>

        <!--
        association:用于封装关联的一个JavaBean对象   通过association实现一对一映射
             property:给哪个属性封装关联的JavaBean数据
             javaType:关联的JavaBean是什么类型的,写全限定类名或别名
         
        -->
        <association property="user" javaType="user">
            <id property="id" column="id"></id>
            <result property="username" column="username"></result>
            <result property="birthday" column="birthday"></result>
            <result property="sex" column="sex"></result>
            <result property="address" column="address"></result>
        </association>

    </resultMap>
</mapper>
  • 测试
public class MultiTableQueryTest {
    private InputStream inputStream;
    private SqlSession sqlSession;
    private AccountMapper accountMapper;
    @Before
    public void init() throws IOException {
        inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(inputStream);
        sqlSession = factory.openSession();
        accountMapper = sqlSession.getMapper(AccountMapper.class);
    }
    @After
    public void destory(){
        try {
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        sqlSession.close();
    }

    //一对一(多对一)关联查询:查询账户信息及关联的用户信息
    @Test
    public void queryAllAccount(){
        List<Account> accounts = accountMapper.queryAllAccount();
        for (Account account : accounts) {
            System.out.println(account);
        }
    }
}

在这里插入图片描述

MyBatis实现一对多(多对多)关联查询

示例:查询用户信息,以及每个用户拥有的所有帐号信息

  • user表和account表的实体类
public class Account {
    private Integer id;
    private Integer uid;
    private Double money;
    private User user;
    ......
    .......
}

public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
  
    private List<Account> accountList;
	....
	......
}
  • 映射器接口UserMapper
public interface UserMapper {
    //一对多(多对多)关联查询:查询用户(user)信息,以及每个用户拥有的所有帐号(account)信息
   List<User> queryAllUser();
}

  • 映射器UserMapper的配置文件UserMapper.xml
<?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.mycode.mapper.UserMapper">
    <select id="queryAllUser" resultMap="userAccountMap">
        select u.*,a.id aid,a.uid,a.money from user u left join account a on u.id=a.uid
    </select>

    <resultMap id="userAccountMap" type="user">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>
        
        <!--
          collection:用于封装JavaBean中某一属性关联的集合,用于一对多情形    通过collection实现一对多映射
                property:封装哪个属性关联的集合
                ofType:集合中的数据类型是什么,可使用别名
          -->
        <collection property="accountList" ofType="account">
            <id property="id" column="aid"></id>
            <result property="uid" column="uid"></result>
            <result property="money" column="money"></result>
        </collection>

    </resultMap>
</mapper>
  • 测试
public class MultiTableQueryTest {
    private InputStream inputStream;
    private SqlSession sqlSession;
    private UserMapper userMapper;
    @Before
    public void init() throws IOException {
        inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(inputStream);
        sqlSession = factory.openSession();
        userMapper = sqlSession.getMapper(UserMapper.class);
    }
    @After
    public void destory(){
        try {
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        sqlSession.close();
    }

    //一对多(多对多)关联查询:查询用户(user)信息,以及每个用户拥有的所有帐号(account)信息
    @Test
    public void queryAllUser(){
        List<User> userList = userMapper.queryAllUser();
        for (User user : userList) {
            System.out.println(user);
        }
    }

}

在这里插入图片描述

多对多关联查询-中间关系表

​如有用户表(user)和角色表(role),一个用户有多个角色,一个角色有多个用户,两表通过中间关系表(user_role)维护多对多关系。

用户表(user)
在这里插入图片描述
角色表(role)
在这里插入图片描述
中间关系表(user_role)
在这里插入图片描述

  1. 查询用户及关联的角色
    在这里插入图片描述
    在这里插入图片描述
  2. 查询角色及关联的用户
    在这里插入图片描述
    在这里插入图片描述

本质上多对多关联查询和一对多查询是一样的,都是查询一张表的数据,及其关联的数据的集合。

示例:查询用户及关联的角色

  • user表和role 表的实体类
public class User {
    private Integer id;
    private String username;
    private String birthday;
    private String sex;
    private String address;
    /*在结果集封装时,使用resultMap设置字段和属性的对应关系,
    使用resultMap的子标签collection封装关联的Role对象集合。*/
    private List<Role> roles;
 }

public class Role {
    private Integer id;
    private String roleName;
    private String roleDesc;
    /*在结果集封装时,使用resultMap设置字段和属性的对应关系,
    使用resultMap的子标签collection封装关联的User对象集合。*/
    private List<User> users;
}
  • 映射器接口UserMapper
public interface UserMapper {
    //查询用户及关联的角色集合
    List<User> selectUser();
}
  • 映射器UserMapper的配置文件UserMapper.xml
<?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.mycode.mapper.UserMapper">
    <select id="selectUser"  resultMap="userRoleMap">
      SELECT u.*,r.id rid,r.ROLE_NAME roleName, r.ROLE_DESC roleDesc FROM USER u LEFT JOIN user_role ur ON u.id=ur.uid LEFT JOIN  role r ON ur.rid=r.id
    </select>
    <!--
     resultMap标签:设置结果集中字段名和JavaBean属性的对应关系
         id属性:唯一标识
       type属性:要把查询结果的数据封装成什么对象,写全限定类名
     -->
    <resultMap id="userRoleMap" type="user">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>

        <!--
        collection:用于封装JavaBean中某一属性关联的集合,用于一对多情形
                property:封装哪个属性关联的集合
                ofType:集合中的数据类型是什么,可使用了别名
        -->
        <collection property="roles" ofType="role">
            <!--property:JavaBean的属性名;  column:字段名-->
            <id property="id" column="rid"></id>
            <result property="roleName" column="roleName"></result>
            <result property="roleDesc" column="roleDesc"></result>
        </collection>
    </resultMap>
</mapper>

示例:查询角色及关联的用户

  • user表和role 表的实体类
public class User {
    private Integer id;
    private String username;
    private String birthday;
    private String sex;
    private String address;

    private List<Role> roles;
 }

public class Role {
    private Integer id;
    private String roleName;
    private String roleDesc;

    private List<User> users;
}
  • 映射器接口RoleMapper
public interface RoleMapper {
    // 查询角色及关联的用户集合
    List<Role> selectRole();
}

  • 映射器RoleMapper的配置文件RoleMapper.xml
<?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.mycode.mapper.RoleMapper">
    <select id="selectRole"  resultMap="roleUserMap">
SELECT r.id rid,r.ROLE_NAME roleName, r.ROLE_DESC roleDesc,u.* FROM role r LEFT JOIN user_role ur ON r.id=ur.rid LEFT JOIN USER u ON ur.uid=u.id
    </select>

    <resultMap id="roleUserMap" type="role">
        <id property="id" column="rid"></id>
        <result property="roleName" column="roleName"></result>
        <result property="roleDesc" column="roleDesc"></result>

        <collection property="users" ofType="user">
            <id property="id" column="id"></id>
            <result property="username" column="username"></result>
            <result property="birthday" column="birthday"></result>

            <result property="sex" column="sex"></result>
            <result property="address" column="address"></result>
        </collection>
    </resultMap>

</mapper>

延迟加载策略(懒加载)

以上的多表关联查询,只要一调用相关方法就会把全部信息(用户信息与账户信息)查询出来。
在这里插入图片描述
但有时为了提高数据库性能,可以使用延迟加载策略,即当需要使用到数据,才发起查询,不需要使用的数据不发起查询。如:查询用户信息,不使用账户信息的时候,不查询帐号的数据,只有当使用了用户的账户信息,Mybatis再发起查询帐号的信息。

可以这样理解,以上的多表关联查询是一条SQL语句完成的,即一执行,就会加载全部数据。而使用延迟加载策略则可以把这一条SQL语句拆成单表查询的两条SQL语句。如:查询账户信息及关联的用户信息,可拆成一条查询所有帐号的SQL语句,一条查询帐号所属用户的SQL语句。配置延迟加载策略后,这样会首先查询帐号信息,执行查询帐号的SQL语句;当需要使用帐号关联的用户时,再执行查询关联的用户的SQL语句;

如有用户表(user)和帐户表(account),一个用户有多个帐户,一个帐户只属于一个用户,帐户表中有外键指向用户表主键。

  • 用户表(user)
    在这里插入图片描述
  • 帐户表(account)
    在这里插入图片描述

实现一对一(多对一)的延迟加载(使用Assocation实现延迟加载)

示例:查询帐号信息,及其关联的用户信息

  • user表和account表的实体类
public class Account {
    private Integer id;
    private Integer uid;
    private Double money;
	//使用Assocation实现延迟加载
    private User user;
    ......
    .......
}

public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    private List<Account> accountList;
	....
	......
}
  • 映射器接口AccountMapper
public interface AccountMapper {
    //查询账户信息及关联的(一个)用户信息
    List<Account> queryAllAccount();
}
  • 映射器AccountMapper的配置文件AccountMapper.xml
<?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.mycode.mapper.AccountMapper">

    <select id="queryAllAccount" resultMap="accountUserMap">
        SELECT * FROM account
    </select>

    <resultMap id="accountUserMap" type="account">
        <id property="id" column="id"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>

        <!--
        需要得到关联的用户信息:一个对象,调用其它功能,查询得到关联的User,封装进来  (用于封装关联的一个JavaBean对象)
            property:给哪个属性封装关联的JavaBean数据
            javaType:关联的JavaBean是什么类型的,可使用别名
            column:调用功能时,传递的参数从哪个字段里取
            select:调用哪个功能(方法),查询得到关联的JavaBean对象

        -->
        <association property="user" javaType="user" column="uid"  
                     select="com.mycode.mapper.UserMapper.findById" >
            
        </association>
    </resultMap>
</mapper>
  • 编写AccountMapper.xml中需要提供的查询关联的用户信息功能 映射器接口 UserMapper
public interface UserMapper {
    //根据用户的id,得到用户信息
    User findById(Integer id);
}
  • 映射器接口UserMapper 的配置文件UserMapper.xml
<?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.mycode.mapper.UserMapper">

    <select id="findById" parameterType="int" resultType="user">
        SELECT * FROM USER WHERE id=#{id}
    </select>
    
</mapper>
  • 在核心配置文件SqlMapConfig.xml中,开启懒加载
    <settings>
        <!--懒加载的全局开关,设置为true,开启-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--设置为false,表示禁用积极加载,使用懒加载-->
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

  • 测试1
    @Test
    public void testQueryAllAccount(){
        List<Account> accounts = accountMapper.queryAllAccount();
        for (Account account : accounts) {
            //只使用了帐号信息,只执行查询帐号的SQL语句
            System.out.println(account.getId() +", " +account.getMoney());
        }
    }

测试中只使用了账户信息,所以只会查询帐号信息,执行查询帐号的SQL语句
在这里插入图片描述

  • 测试2
    @Test
    public void testQueryAllAccount(){
        List<Account> accounts = accountMapper.queryAllAccount();
        for (Account account : accounts) {
            //只使用了帐号信息,只执行查询帐号的SQL语句
            System.out.println(account.getId() +", " +account.getMoney());

            //当需要使用用户信息时,才会执行查询用户的SQL语句
            System.out.println(account.getUser());
        }
    }

在这里插入图片描述

实现一对多(多对多)的延迟加载(使用Collection实现延迟加载)

示例:查询用户信息,以及每个用户拥有的所有帐号信息

  • user表和account表的实体类
public class Account {
    private Integer id;
    private Integer uid;
    private Double money;
    private User user;
    ......
    .......
}

public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
  	//使用Collection实现延迟加载
    private List<Account> accountList;
	....
	......
}
  • 映射器接口UserMapper
public interface UserMapper {
    //查询用户信息,以及每个用户拥有的所有帐号信息
   List<User> queryAllUser();
}

  • 映射器UserMapper的配置文件UserMapper.xml
<?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.mycode.mapper.UserMapper">
    <select id="queryAllUser" resultMap="userAccountMap">
        SELECT * FROM USER
    </select>
    <!--
       resultMap标签:设置结果集中字段名和JavaBean属性的对应关系
               id属性:唯一标识
               type属性:结果集的数据要封装成的对象的全限定类名或别名
    -->
    <resultMap id="userAccountMap" type="user">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>

        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>
        <!--
         collection:用于封装JavaBean中某一属性关联的集合,用于一对多情形
               property:封装哪个属性关联的集合
               ofType:集合中的数据类型是什么,可使用别名
               select:调用哪个功能(方法),查询得到关联的JavaBean集合
               column:调用功能时,传递的参数从哪个字段里取
         -->
        <collection property="accountList" ofType="account"
                    select="com.mycode.mapper.AccountMapper.queryAccountsByUid" column="id"
        ></collection>
    </resultMap>
    
</mapper>
  • 编写UserMapper.xml中需要提供的查询 关联的账户信息功能 映射器接口 AccountMapper
public interface AccountMapper {
	//通过用户id查询账户信息
    List<Account> queryAccountsByUid(Integer uid);
}
  • 映射器接口AccountMapper 的配置文件AccountMapper .xml
<?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.mycode.mapper.AccountMapper">

    <select id="queryAccountsByUid" parameterType="int" resultType="account">
        select * from account where uid=#{id}
    </select>
    
</mapper>
  • 在核心配置文件SqlMapConfig.xml中,开启懒加载
    <settings>
        <!--懒加载的全局开关,设置为true,开启-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 禁用积极加载,使用懒加载 -->
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

  • 测试1
    @Test
    public void testQueryAllUser(){
        List<User> userList = userMapper.queryAllUser();
        for (User user : userList) {
            //这里只获取用户信息,只执行查询用户信息的SQL语句
            System.out.println(user.getUsername() +", " + user.getSex());
        }
    }

测试中只使用了用户信息,所以只会查询用户信息,执行查询用户的SQL语句
在这里插入图片描述

  • 测试2
    @Test
    public void testQueryAllUser(){
        List<User> userList = userMapper.queryAllUser();
        for (User user : userList) {
            //这里只获取用户信息,只执行查询用户信息的SQL语句
            System.out.println(user.getUsername() +", " + user.getSex());
           
            //当使用用户关联帐号信息时,会执行查询帐号信息的SQL语句,只查询用户id为32的账号信息(即按需查询)
            if (user.getId() == 32) {
                System.out.println(user.getAccountList());
            }
        }
    }

测试中查询了用户信息,并判断了用户id为32的,查询其账户信息。(这里其他用户的账户信息并不会查询)
在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Thinking in Coder

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值