黑马就业班——mybatis框架:part3 -- mybatis的深入和多表

  • 本文参考文档:《Mybatis第三天讲义.pdf》
  • 本文代码项目:mybatis_day02_datasource、mybatis_day03_dynamicSQL、mybatis_day03_one2many、mybatis_day03_many2many、mybatis_day03_jndi(路径:G:\idea_java_project\)

今日内容

1. mybatis中的连接池以及事务控制原理部分了解
	1)mybatis中连接池使用及分析;
	2)mybatis事务控制的分析
	
2. mybatis基于XML配置的动态SQL语句使用
	1)mappers配置文件中的几个标签:

3. mybatis中的多表操作
		1) 一对多
		2) 一对一
		3) 多对多

1、Mybatis 连接池与事务深入
1.1 连接池
  连接池:我们在实际开发中都会使用连接池。因为它可以减少我们获取连接所消耗的时间。
黑马就业班——mybatis框架:part3 -- mybatis的深入和多表_第1张图片
  mybatis连接池提供了3种方式的配置,配置的位置是主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种连接池方式。

type属性的取值:
	POOLED:采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现;
	UNPOOLED:采用传统的获取连接的方式,虽然也实现javax.sql.DataSource接口,但是并没有使用池的思想,因此每次使用都会重新获取连接。
	JNDI:采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到DataSource是不一样。注意:如果不是web或者maven的war工程,是不能使用的。我们课程中使用的是tomcat服务器,采用连接池就是dbcp连接池。

  MyBatis 内部分别定义了实现了 java.sql.DataSource 接口的 UnpooledDataSource,
PooledDataSource 类来表示 UNPOOLED、 POOLED 类型的数据源。MyBatis 在初始化时, 根据的 type 属性来创建相应类型的的数据源 DataSource,即:

type="POOLED": MyBatis 会创建 PooledDataSource 实例
type="UNPOOLED" : MyBatis 会创建 UnpooledDataSource 实例
type="JNDI": MyBatis 会从 JNDI 服务上查找 DataSource 实例,然后返回使用

黑马就业班——mybatis框架:part3 -- mybatis的深入和多表_第2张图片
黑马就业班——mybatis框架:part3 -- mybatis的深入和多表_第3张图片

  • 在IDEA中,可以使用CTRL+N的方式来搜索想要显示的类

  MyBatis 是 通 过 工 厂 模 式 来 创 建 数 据 源 DataSource 对 象 的 , MyBatis 定 义了抽象的工厂接口:org.apache.ibatis.datasource.DataSourceFactory,通过其 getDataSource()方法返回数据源DataSource。(具体分析过程见文档——1.1.3 Mybatis 中 DataSource 的存取)

  Mybatis 中连接的获取过程分析(文档—1.1.4 Mybatis 中连接的获取过程分析,视频4,5。这部分比较重要,注意原理以及源码的分析!
  设置POOLED属性时的源码分析:
黑马就业班——mybatis框架:part3 -- mybatis的深入和多表_第4张图片
黑马就业班——mybatis框架:part3 -- mybatis的深入和多表_第5张图片
  其实连接池分为空闲池以及活动池,线程会先从空闲池中获取,如果空闲池没有连接,会再去活动池中获取,如果活动池中连接的数量小于我们设置的最大数量,会创建一个新的连接放到活动池,这样线程就可以获取新的连接。如果活动池中连接的数量等于我们设置的最大数量,就会获取最老的那个连接,判断这个最老的连接是否失效,失效了就对他进行一定的设置,然后返回给线程使用。(失效就是前面使用这个连接的线程已经不再使用)
黑马就业班——mybatis框架:part3 -- mybatis的深入和多表_第6张图片
  MYbatis不使用C3P0或者DBCP等连接池技术,而是使用DataSource接口,自己实现了连接池技术。
  当我们需要创建 SqlSession 对象并需要执行 SQL 语句时,这时候 MyBatis 才会去调用 dataSource 对象来创建java.sql.Connection对象。也就是说, java.sql.Connection对象的创建一直延迟到执行SQL语句的时候。

1.2 事务
  参考文档——1.2 Mybatis 的事务控制
  关于事务,我们需要注意的是:什么是事务,事务的四大特性ACID,不考虑隔离性会产生的3个问题,解决办法:四种隔离级别。(这些是在面试中需要注意的!
  事务:是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作;  这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行;事务是一组不可再分割的操作集合(工作逻辑单元)
参考文章:添加链接描述

  mybatis中的事务是通过sqlsession对象的commit方法和rollback方法实现事务的提交和回滚。我们每次执行方法的时候,都会取消自动提交,导致我们每次执行数据库DML操作的时候,都需要手动提交事务,我们可以在获取sqlSession对象的时候,设置可以自动提交:

        sqlSession = factory.openSession(true);

  当然,只有在每次只执行一个数据库的DML操作的时候,才可以自动提交,如果我们的事务有多个DML操作,设置自动提交,每一个DML语句都会自动提交执行,这样事务就无法控制。一般都会手动提交事务。


2、Mybatis中的动态SQL语句
2.1 if标签与where标签
  我们根据实体类的不同取值,使用不同的 SQL 语句来进行查询。比如在 id 如果不为空时可以根据 id 查询,如果 username 不同空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到。(具体见代码–mybatis_day03_dynamicSQL,文档以及视频的解析)

2.2 foreach标签与sql标签
  需求

传入多个 id 查询用户信息,用下边两个 sql 实现:
	SELECT * FROM USERS WHERE username LIKE '%张%' AND (id =10 OR id =89 OR id=16)
	SELECT * FROM USERS WHERE username LIKE '%张%' AND id IN (10,89,16)
这样我们在进行范围查询时,就要将一个集合中的值,作为参数动态添加进来。这样我们将如何进行参数的传递?

  这里使用到QueryVo包装对象,在QueryVo增加一个存储id属性的List集合属性,并添加getter与setter方法:(具体见代码–mybatis_day03_dynamicSQL,文档以及视频的解析)

 private List<Integer> ids;

  可以使用sql标签定义使用频次较多的sql语句,后面需要使用的时候进行引入即可。后面使用这条语句的时候,可以使用标签引入。(具体见代码–mybatis_day03_dynamicSQL,文档以及视频的解析)


3、Mybatis的多表关联查询——一对多/多对一
  具体来说,表之间的关系有4种:一对多、多对一、一对一、多对多。

//一对多与多对一
	用户和订单就是一对多,订单和用户就是多对一。因为一个用户可以下多个订单,多个订单属于同一个用户。
	如果拿出每一个订单,他都只能属于一个用户,所以Mybatis就把多对一看成了一对一。既Mybatis认为订单中的其中一个与用户之间是一对一的关系。

  mybatis中的多表查询(参考项目:mybatis_day03_one2many):

示例:用户和账户
	一个用户可以有多个账户,一个账户只能属于一个用户(多个账户也可以属于同一个用户)

步骤:
	1、建立两张表:用户表,账户表。让用户表和账户表之间具备一对多的关系,需要使用外键在账户表中添加;
	2、建立两个实体类:用户实体类和账户实体类,让用户和账户的实体类能体现出一对多的关系;
	3、建立两个配置文件,用户的配置文件账户的配置文件;
	4、实现配置:当我们查询用户时,可以同时得到用户下所包含的账户信息;当我们查询账户时,可以同时得到账户的所属用户信息息

  这部分具体见代码分析,视频12视频分析 ,及相关文档信息。


1、一对一查询(多对一)

需求
	查询所有账户信息,关联查询下单用户信息。
注意:
	因为一个账户信息只能供某个用户使用,所以从查询账户信息出发关联查询用户信息为一对一查询。如
果从用户信息出发查询用户下的账户信息则为一对多查询,因为一个用户可以有多个账户。

完成account表的一对一操作

  account表的一对一操作,比如我们在AccountDao的findAllAccountUser方法中,查询所有账户,并且带有用户名称与地址信息(一个account账户对应一个user用户)。

通过写Account子类的方式查询(不常用)
1、我们在AccountDao中添加一个查询所有账户,并且带有用户名称与地址信息的方法
	/**
     * 查询所有账户,并且带有用户名称与地址信息
     * @return
     */
    List<AccountUser> findAllAccountUser();
    
2、我们发现,直接使用如下代码:
	<select id="findAllAccountUser" resultType="account">
        SELECT a.* , u.username , u.address FROM USER u,account a WHERE u.id = a.uid;
    </select>
	findAllAccountUser查询出来的结果是User与Account的信息,Account类是无法封装用户信息的。因此我们创建一个AccountUser类,继承Account,包含了username以及address2个属性,使得AccountDao的findAllAccountUser方法返回的结果是AccountUser类。

3、我们在Account.xml中添加查询标签如下:
	<!--查询所有账户,并且带有用户名称与地址信息-->
    <select id="findAllAccountUser" resultType="accountuser">
        SELECT a.* , u.username , u.address FROM USER u,account a WHERE u.id = a.uid;
    </select>
	这样,findAllAccountUser方法查询出来的结果包含User的username与address信息,还有account表的所有信息,将这些信息封装到AccountUser的时候,由于AccountUser继承Account,因此account的信息可以封装到AccountUser的父类Account,而User的username与address信息可以封装到AccountUser的username与address属性中。这样,查询出来的Account与User的信息就可以封装到结果!!!
	
4、接下来我们测试:
	@Test
    public void testFindAllAccountUser()
    {
        List<AccountUser> allAccountUser = accountDao.findAllAccountUser();
        for (AccountUser accountUser : allAccountUser)
        {
            System.out.println(accountUser);
        }
    }
结果是:

在这里插入图片描述

建立实体类关系的方式
1、我们在AccountDao有查询所有账户的方法findAll,同时还要获取到当前账户的所属用户信息。最后查询出来的数据封装到Account类中
	List<Account> findAll();
	
2、我们之前提过,如果要实现多表查询,必须要让用户和账户的实体类能体现出一对多的关系。因此,从表(多,既account表)实体应该包含一个主表(一,user)实体的对象引用。
	因此,我们在从表account表对应的实体类Account中,创建主表user的实体类User类的对象引用,并创建getter与setter方法。
	private User user;

3、我们在AccountDao.xml中,设置一个查询标签
	<select id="findAll" resultType="account">
        SELECT u.* , a.id AS aid , a.uid , a.money FROM USER u,account a WHERE u.id = a.uid;
    </select>
     现在查询出来的结果包含account与user的信息,只能将查询出来的account表的信息封装到Account类的普通属性中,如果我们想让查询出来的对应User的数据封装到Account类的User属性,需要进行resultMap配置。

4、resultMap的配置:
	 <!-- 定义封装查询出来的 account和user 信息的resultMap -->
    <!--
        1、前面已经定义 com.lkj.domain 包下的所有实体类按其类名起别名,这里不需要再写 com.lkj.domain.Account ,直接写account,
            type="account" 表示要将结果封装到com.lkj.domain.Account类
        2、注意,在这个resultMap中,出现了2个id,为了避免冲突,我们给account表的id取一个别名aid。
         因为我们是在<id>标签里面设置,我们直接写 column="aid" ,系统会自动将account表的id属性起别名为aid。
         如果下面的SQL语句已经给id起别名为aid,那么这里必须强制起别名为aid。如果下面SQL语句没有起别名,那么这里可以取,也可以不取!!!
         (只有id可以这样起别名,其他属性都是在<result>标签中设置,不可以这样起别名,而且其他属性一般不会冲突,不需要起别名)
        补充:property 是实体类的属性,column 是数据库表的属性
    -->
    <resultMap id="accountUserMap" type="account">
        <!--首先,Account类中配置封装account表查询出来的内容-->
        <id property="id" column="aid"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>

        <!-- 一对一的关系映射:配置封装user的内容-->
        <!--
            1、使用<association>标签来将user表中查询出来的数据封装到Account类的user属性中。

            2、property="user" 表示查询出来的user表的信息封装到Account对象的user属性

            3、column="uid",表示user表的信息要通过account表的uid对应查询
            column="uid" , 表示通过account表的 uid 属性(外键),可以确定uid对应user表中id,既可以确定user表查询出来的内容。
            这样我们就可以知道查询出来的账户信息所对应的用户信息。(这里的column一般是从表的外键属性)
            这里也可以理解为,我们要将查询出来的user表的信息封装到Account类的user属性,首先要通过account表的uid属性来查询user表的信息。

            4、javaType属性:表示Account类中user属性的类型
            我们这里将查询出来的account表的信息的uid对应的user表的信息封装到com.lkj.domain.User类,
            前面已经定义 com.lkj.domain 包下的所有实体类按其类名起别名,这里不需要再写 com.lkj.domain.User ,直接写user
        -->
        <association property="user" column="uid" javaType="user">
            <!--里面写User类属性与user表属性的对应-->
            <id property="id" column="id"></id>
            <result column="username" property="username"></result>
            <result column="address" property="address"></result>
            <result column="sex" property="sex"></result>
            <result column="birthday" property="birthday"></result>
        </association>
    </resultMap>


5、查询的标签定义为:
	<!-- 查询所有账户,并且带有用户名称与地址信息 -->
    <!-- 
    我们将返回的结果设置为 resultMap="accountUserMap"
    -->
    <select id="findAll" resultMap="accountUserMap">
        SELECT u.* , a.id AS aid , a.uid , a.money FROM USER u,account a WHERE u.id = a.uid;
    </select>

6、测试:
	/**
     * 查询所有账户account,并且带有用户名称与地址信息
     */
    @Test
    public void testFindAll()
    {
        List<Account> accountList = accountDao.findAll();
        for (Account account : accountList)
        {
            System.out.println("--------每个account的信息------------");
            System.out.println(account);//只会打印Account类原有的属性,因为Account的toString只打印了它原有的属性
            /*
            我们查询出来的user表的信息封装到了Account类的user属性,因此我们这里需要打印Account类的user属性,
            这样才会打印查询出来的user表的信息。
             */
            System.out.println(account.getUser());//打印出查询出来的user表的信息
        }
    }

黑马就业班——mybatis框架:part3 -- mybatis的深入和多表_第7张图片


2、一对多查询

需求:
	查询所有用户信息及用户关联的账户信息。
分析:
	用户信息和他的账户信息为一对多关系,并且查询过程中如果用户没有账户信息,此时也要将用户信息
查询出来,我们想到了左外连接查询比较合适。(账户一定有对应的用户,但是用户不一定有对应的账户)如果不使用左外连接,那些没有账户信息的用户信息就无法查询出来。

完成user表的一对多操作

  user表的一对多操作,比如我们在UserDao的findAll方法中,查询所有用户,并且获取该用户下的所有账户信息(一个user用户对应一个/多个/0个account账户,)。

1、在UserDao下有一个方法:
	//查询所有用户,同时获取该用户下的所有账户信息
    List<User> findAll();

2、我们想通过主表查询到对应从表的信息(一对多关系映射),此时主表实体应该包含从表实体的集合引用
	private List<Account> account;//注意生成getter与setter
	注意区别于从表到主表查询的一对一,一对一从表实体应该包含一个主表实体的对象引用。(因为User可能对应多个Account,而Account只能对应一个User)

3、resultMap的设置:
	<!--同样,先定义封装查询出来的user(一)和account(多)信息的resultMap-->
    <!--
        1、查询出来的信息封装到com.lkj.domain.User中,已经起别名,使用user表示
    -->
    <resultMap id="userAccountMap" type="user">
        <!--首先是配置User对象原有属性的映射。信息从user表查询-->
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="address" column="address"></result>
        <result property="sex" column="sex"></result>
        <result property="birthday" column="birthday"></result>

        <!--其次,配置User对象中accounts集合的映射。信息从account表查询-->
        <!--
            1、这里一对多使用的是 collection 标签,一对一使用的是<association>标签

            2、property="accounts" 表示查询出来的account表的信息封装到User对象的accounts属性

            3、ofType="account" 表示User类中accounts集合中元素的类型(前面SqlMapConfig.xml已经取别名,否则使用全限定类名)

            4、前面在Account类中查询user属性,需要指定 column="uid" ,既通过account表的 uid 属性去查询user表,才能查询出账户对应的用户信息
                但是在这里,我们在User类中查询accounts属性,则不需要指定通过user表的哪一个属性可以查询account表的信息
                注意区分,主表对从表查询(一对多)配置resultMap的collection的时候不需要指定 column
        -->
        <collection property="accounts" ofType="account">
            <!--
                1、注意,在这个resultMap中,出现了2个id,为了避免冲突,我们给account表的id取一个别名aid。
                 因为我们是在<id>标签里面设置,我们直接写 column="aid" ,系统会自动将account表的id属性其别名为aid。
                 (只有id可以这样起别名,其他属性都是在<result>标签中设置,不可以这样起别名,,而且其他属性一般不会冲突,不需要起别名)
                补充:property 是实体类的属性,column 是数据库表的属性
            -->
            <id property="id" column="aid"></id>
            <result property="uid" column="uid"></result>
            <result property="money" column="money"></result>
        </collection>
    </resultMap>

4、 查询用户信息的标签如下:
	<!--  询所有用户,同时获取该用户下的所有账户信息 -->
    <!--
        1、这里需要查询所有用户的信息,不管有没有账户信息,用户信息都得有。
            但是想之前那样使用隐式内连接查询,没有账户的用户信息查询不到,因此使用左外链接
    -->
    <select id="findAll" resultMap="userAccountMap">
        select * from user u left outer join account a on u.id = a.uid
    </select>

5、测试
	/**
     * 查询所有用户,同时获取该用户下的所有账户信息
     */
    @Test
     public void testFindAll()
     {
         List<User> list = userDao.findAll();
         for (User user : list)
         {
             System.out.println("------每个用户信息------");
             System.out.println(user);
             System.out.println(user.getAccounts());
         }
         //结果,发现所有的用户信息都打印出来,包括有账户信息以及没有账户信息的
         // 对于重复的用户信息(因为一个用户有2个账户,出现对应2个账户的2个用户),mybatis会自动识别并将所有账户信息封装到一个用户中
     }

黑马就业班——mybatis框架:part3 -- mybatis的深入和多表_第8张图片

注意:
	collection:部分定义了用户关联的账户信息。表示关联查询结果集。
	property="accounts":关联查询的结果集存储在 User 对象的上哪个属性。
	ofType="account":指定关联查询的结果集中的对象类型即List中的对象类型。此处可以使用别名,也可以使用全限定名。

4、Mybatis 多表查询之多对多

示例:用户和角色
	一个用户可以有多个角色,	一个角色可以赋予多个用户

步骤:
	1、建立两张表:用户表,角色表:让用户表和角色表具有多对多的关系。需要使用中间表,中间表中包含各自的主键,在中间表中是外键。
	
	2、建立两个实体类:用户实体类和角色实体类。让用户和角色的实体类能体现出来多对多的关系——各自包含对方一个集合引用。
	
	3、建立两个配置文件:用户的配置文件角色的配置文件。
	
	4、实现配置:当我们查询用户时,可以同时得到用户所包含的角色信息;当我们查询角色时,可以同时得到角色的所赋予的用户信息。

  准备工作见视频15、16分析(项目:mybatis_day03_many2many)


多对多查询操作

准备
	1)创建3张表并插入数据:用户表user、角色表role、中间表user_role。3张表之间的关系如下图。
	2)创建role表的实体类(属性名与role表的属性名不相同),同时创建role的DAO接口:RoleDao,既其相应的xml文件RoleDao.xml。
	由于我们在SqlMapConfig.xml中使用 <package name="com.lkj.dao"></package> 配置所有 com.lkj.dao 包下面的dao.xml文件的映射,因此这里不需要再在SqlMapConfig.xml中导入RoleDao.xml。

黑马就业班——mybatis框架:part3 -- mybatis的深入和多表_第9张图片

  • 下面我们将多对多分解为2个反向的一对多,既一个用户对应多个角色,一个角色对应多个用户,我们可以从用户与角色2个角度进行查询。在

查询角色,获取角色下所属用户信息
1、在Role的实体类中,创建多对多的关系映射,既一个角色可以赋予多个用户。我们在Role中创建一个返回User类型集合的users属性。
    private List<User> users;

2、RoleDao中的查询所有角色以及角色所属用户信息的方法为:
	/**
     * 查询所有角色,获取角色下所属用户信息
     * @return
     */
    List<Role> findAll();

3、查询的SQL语句的分析(见视频15分析,这部分是难点)
	我们查询的时候,不仅要将角色信息全部查询出来,还要将角色对应的用户信息查询出来(不管角色有没有对应的用户,角色全部要显示)。此时应该利用中间表user_role。
	首先,我们使用左外连接,先依据 role.id = user_role.rid ,将满足条件的role表中的信息全部查询出来。语句如下,查询出来的信息如下图1SELECT * FROM role r LEFT OUTER JOIN user_role ur ON r.id = ur.rid;
	查询出来的是一个新的表。随后,我们根据新表中的所有uid对应user表的id,再去左外查询user表。语句如下,查询出来的信息如下图2SELECT u.* , r.id AS rid , r.role_name , r.role_desc FROM 
		role r LEFT OUTER JOIN user_role ur ON r.id = ur.rid
		LEFT OUTER JOIN USER u ON u.id = ur.rid;
	此处结果含有多个id,为了区分,我们将role表的id命名为rid。这样,查询出来的表就包含所有role表的信息以及role表中角色对应的用户的信息(没有对应用户信息的角色也查询出来)

	当然语句也可以写为下面的形式:
	SELECT u.* , r_ur.id AS rid , r_ur.role_name , r_ur.role_desc FROM
	(SELECT * FROM role r LEFT OUTER JOIN user_role ur ON r.id = ur.rid) r_ur
	LEFT OUTER JOIN USER u ON u.id = r_ur.uid;
	我们将第一次查询出来的信息命名为一个新的表r_ur,这个新的表再与user表进行左外连接的查询,我们获取user表的全部以及r_ur表的 id、role_name、role_desc 信息即可,中间表的信息不需要。(这种方法是比较容易理解的! )
	
4、resultMap的配置
	<!--定义role表的resultMap-->
    <!--
        1、Windows系统下mysql不区分大小写,可以直接写role_desc,而不必写ROLE_DESC
        2、为了避免结果id重复,将role表的id命名为rid
    -->
    <resultMap id="roleMap" type="role">
        <id property="roleId" column="rid"></id>
        <result property="roleName" column="role_name"></result>
        <result property="roleDesc" column="role_desc"></result>

        <!--其次,配置Role对象中users集合的映射。信息从user表查询-->
        <!--
            1、同样,一对多使用<collection>标签;

            2、property="users" 表示查询出来的user表的信息封装到Role对象的users属性

            3、ofType="user" 表示Role类中users集合中元素的类型为com.lkj.domain.user(前面SqlMapConfig.xml已经取别名,否则使用全限定类名)
        -->
        <collection property="users" ofType="user">
            <id property="id" column="id"></id>
            <result column="username" property="username"></result>
            <result column="address" property="address"></result>
            <result column="sex" property="sex"></result>
            <result column="birthday" property="birthday"></result>
        </collection>
    </resultMap>

5、查询语句
	<!--查询所有角色,获取角色下所属用户信息-->
    <select id="findAll" resultMap="roleMap">
        SELECT u.* , r.id AS rid , r.role_name , r.role_desc FROM
	    role r LEFT OUTER JOIN user_role ur ON r.id = ur.rid
	    LEFT OUTER JOIN USER u ON u.id = ur.uid;
    </select>

6、测试
	/**
     * 查询所有
     */
    @Test
     public void testFindAll()
     {
         List<Role> list = roleDao.findAll();
         for (Role role : list)
         {
             System.out.println("------每个角色信息------");
             System.out.println(role);
             System.out.println(role.getUsers());
         }
     }

黑马就业班——mybatis框架:part3 -- mybatis的深入和多表_第10张图片
在这里插入图片描述
在这里插入图片描述


查询用户,获取用户所属角色信息(同样是一对多,一个用户对应多个角色)
1、查询所有用户以及用户所对应的角色信息,没有对应角色的用户信息也要查询出来。
	
2SQL语句
	其实把刚才的SQL语句的角色与用户调转一下即可,既先使用用户与user_role表进行查询,查询出来的表再与role表进行查询
	SELECT u.* , r.id AS rid , r.role_name , r.role_desc FROM 
	USER u LEFT OUTER JOIN user_role ur ON u.id = ur.uid
	LEFT OUTER JOIN role r ON r.id = ur.rid;
	或者
	SELECT u_ur.id , u_ur.username , u_ur.birthday, u_ur.sex , u_ur.address,
	r.id AS rid , r.role_name , r.role_desc  FROM 
	 (SELECT * FROM USER u LEFT OUTER JOIN user_role ur ON u.id = ur.uid) u_ur 
	LEFT OUTER JOIN role r ON r.id = u_ur.rid; -- 起别名只在最后查询结果起作用,这里还是要使用r.id,而不是r.rid

3、同样,在User的实体类中,创建多对多的关系映射,既一个用户可以有多个角色。我们在User中创建一个返回Role类型集合的roles属性。
    private List<Role> roles;

4、resultMap
	<resultMap id="userMap" type="user">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="address" column="address"></result>
        <result property="sex" column="sex"></result>
        <result property="birthday" column="birthday"></result>

        <!--配置角色集合的映射-->
        <collection property="roles" ofType="role">
            <id property="roleId" column="rid"></id>
            <result property="roleName" column="role_name"></result>
            <result property="roleDesc" column="role_desc"></result>
        </collection>
    </resultMap>

5、查询语句
	<!-- 查询所有用户以及用户包含的角色信息-->
    <select id="findAll" resultMap="userMap">
      SELECT u.* , r.id AS rid , r.role_name , r.role_desc FROM
	  USER u LEFT OUTER JOIN user_role ur ON u.id = ur.uid
	  LEFT OUTER JOIN role r ON r.id = ur.rid;
    </select>

6、测试
	/**
     * 查询所有用户,同时获取该用户下的所有账户信息
     */
    @Test
     public void testFindAll()
     {
         List<User> list = userDao.findAll();
         for (User user : list)
         {
             System.out.println("------每个用户信息------");
             System.out.println(user);
             System.out.println(user.getRoles());    

黑马就业班——mybatis框架:part3 -- mybatis的深入和多表_第11张图片

5、JNDI补充
  这部分见视频17-19,以及资料中的JNDI文档。这部分不是主要内容,只对其中需要注意的部分进行说明。
  JNDI:Java Naming and Directory Interface(java命名和目录接口)。是SUN公司推出的一套规范,属于JavaEE技术之一,目的是模仿windows系统中的注册表。在我们这里,JNDI是用来在Tomcat服务器中注册数据源。
  下面是JNDI的结构,如下:

黑马就业班——mybatis框架:part3 -- mybatis的深入和多表_第12张图片
  注意:

1、我们需要创建一个Maven的web工程来演示。(项目mybatis_day03_jndi)
2、需要在webapps下创建一个META-INF,里面放context.xml文件,内容如下,注意将数据库部分修改为我们自己的数据库内容。
	<?xml version="1.0" encoding="UTF-8"?>
	<Context>
	<!-- 
	<Resource 
	name="jdbc/eesy_mybatis"						数据源的名称(可以随便起)
	type="javax.sql.DataSource"						数据源类型
	auth="Container"								数据源提供者(这里指的是由Tomcat提供)
	maxActive="20"									最大活动数
	maxWait="10000"									最大等待时间
	maxIdle="5"										最大空闲数
	username="root"									用户名
	password="root"									密码
	driverClassName="com.mysql.jdbc.Driver"			驱动类
	url="jdbc:mysql://localhost:3306/lkj_mybatis"	连接url字符串
	/>
	 -->
	<Resource 
	name="jdbc/lkj_mybatis"
	type="javax.sql.DataSource"
	auth="Container"
	maxActive="20"
	maxWait="10000"
	maxIdle="5"
	username="root"
	password="root"
	driverClassName="com.mysql.jdbc.Driver"
	url="jdbc:mysql://localhost:3306/lkj_mybatis"
	/>
	</Context>

3、修改SqlMapConfig.xml文件的内容,其实我们只修改了:
	<!-- 配置连接数据库的必备信息  type属性表示是否使用数据源(连接池)-->
     <dataSource type="JNDI">
         <property name="data_source" value="java:comp/env/jdbc/lkj_mybatis"/>
     </dataSource>
     我们原来是自己制定数据库连接信息,或者提供jdbcComfig.properties来指定,现在是通过指定data_source 来制定,其中 value="java:comp/env/jdbc/lkj_mybatis" ,“java:comp/env/” 是固定的,而 jdbc/lkj_mybatis 是context.xml中的 name 属性的值。

4、在测试之前,需要给项目添加tomcat服务器,参考之前的黑马旅游网的文章,里面对tomcat服务器的添加做了详细的解析,我们可以使用maven自带的tomcat,也可以使用我们本地的tomcat。(这里使用本地的tomcat)

5、此时我们运行之前测试类MybatisTest中的 testFindAll 方法,发现无法运行,因为我们的测试类没有经过Tomcat服务器,也就是无法使用Tomcat服务器给我们准备的 连接数据库的JNDI的Map数据源(我们这里使用的是JNDI配置),无法连接到数据库,因此无法运行。
	此时应该将测试类测试类的内容放到jsp的java代码块区域,由于jsp会经过服务器,这样它里面的java代码就会经过服务器,就可以使用JNDI准备的连接数据库的数据源Map,这样就可以访问数据库。我们访问服务器的index.jsp页面,控制台打印我们findAll方法查询的所有用户内容。

黑马就业班——mybatis框架:part3 -- mybatis的深入和多表_第13张图片

你可能感兴趣的:(Java资料,mybatis,mysql,JNDI,多表查询)