mybatis框架学习总结第三天

mybatis框架学习总结第三天

连接池的介绍

连接池的好处:我们在实际开发中都会使用连接池,因为它可以减少我们获取连接所消耗的时间。

在这里插入图片描述

mybatis中的连接池

配置

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连接池。

UNPOOLED与POOLED对比

UNPOOLED配置
<dataSource type="UNPOOLED">
    <property name="driver" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</dataSource>
POOLED配置及图解
<dataSource type="POOLED">
    <property name="driver" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</dataSource>

在这里插入图片描述

在这里插入图片描述

对比输出

在这里插入图片描述

mybatis的事务控制

JDBC中事务的回顾

在 JDBC 中我们可以通过手动方式将事务的提交改为手动方式,通过 setAutoCommit()方法就可以调整。

JDK API说明

void setAutoCommit(boolean autoCommit)
            throws SQLException
将此连接的自动提交模式设置为给定状态。如果连接处于自动提交模式,则其所有SQL语句将作为单个事务执行并提交。否则,它的SQL语句被分组成通过调用方法`commit`或方法`rollback` 。默认情况下,新连接处于自动提交模式。

当语句完成时,发生提交。 语句完成的时间取决于SQL语句的类型: 

  对于DML语句(如插入,更新或删除)和DDL语句,语句在执行完成后立即完成。 

  对于Select语句,当关联的结果集关闭时,该语句将完成。 

  对于`CallableStatement`对象或返回多个结果的语句,当所有关联的结果集都已关闭并且已检索到所有更新计数和输出参数时,该语句将完成。 

注意:如果在事务中调用此方法并更改了自动提交模式,则事务将被提交。 如果调用了`setAutoCommit`  ,并且自动提交模式没有改变,则调用是无操作的

若没有设置自动提交以及手动提交

private InputStream in;
private SqlSession sqlSession;
private IUserDao userDao;

@Before //用于在测试方法执行之前执行
public void init() throws Exception{
    //1.读取配置文件,生成字节输入流
    in = Resources.getResourceAsStream("SqlMapConfig.xml");
    //2.获取SqlSessionFactory
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    //3.获取SqlSession对象
    sqlSession = factory.openSession();
    //4.获取dao的代理对象
    userDao = sqlSession.getMapper(IUserDao.class);
}

@After //用于在测试方法执行之后执行
public void destroy() throws Exception {
    //6.释放资源
    sqlSession.close();
    in.close();
}

/**
 * 测试保存操作
 */
@Test
public void testSave() {
    User user = new User();
    user.setUserName("modify User property");
    user.setUserAddress("北京市顺义区");
    user.setUserSex("男");
    user.setUserBirthday(new Date());
    System.out.println("保存操作之前" + user);
    //5.执行保存方法
    userDao.saveUser(user);

    System.out.println("保存操作之后" + user);
}

控制台输出

在这里插入图片描述

此时主要原因就是在连接池中取出的连接,都会将调用 connection.setAutoCommit(false)方法,故此时新增操作无法成功

mybatis中事务提交方式

Mybatis 框架因为是对 JDBC 的封装,所以 Mybatis 框架的事务控制方式,本身也是用 JDBC 的setAutoCommit()方法来设置事务提交方式的。

运行之前所写的代码:

private InputStream in;
private SqlSession sqlSession;
private IUserDao userDao;

@Before //用于在测试方法执行之前执行
public void init() throws Exception{
    //1.读取配置文件,生成字节输入流
    in = Resources.getResourceAsStream("SqlMapConfig.xml");
    //2.获取SqlSessionFactory
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    //3.获取SqlSession对象
    sqlSession = factory.openSession();
    //4.获取dao的代理对象
    userDao = sqlSession.getMapper(IUserDao.class);
}

@After //用于在测试方法执行之后执行
public void destroy() throws Exception {
    //提交事务
    sqlSession.commit(); //没有这条语句就会产生事务回滚,无法正常添加
    //6.释放资源
    sqlSession.close();
    in.close();
}

/**
 * 测试保存操作
 */
@Test
public void testSave() {
    User user = new User();
    user.setUserName("modify User property");
    user.setUserAddress("北京市顺义区");
    user.setUserSex("男");
    user.setUserBirthday(new Date());
    System.out.println("保存操作之前" + user);
    //5.执行保存方法
    userDao.saveUser(user);

    System.out.println("保存操作之后" + user);
}

控制台输出

在这里插入图片描述

这是我们的 Connection 的整个变化过程,通过分析我们能够发现之前的 CUD 操作过程中,我们都要手动进行事务的提交,原因是 setAutoCommit()方法,在执行时它的值被设置为 false 了,所以我们在 CUD 操作中,必须通过 sqlSession.commit()方法来执行提交操作。

mybatis自动提交事务的设置

为什么CUD(增改删) 过程中必须使用 sqlSession.commit()提交事务?主要原因就是在连接池中取出的连接,都会将调用 connection.setAutoCommit(false)方法,这样我们就必须使用 sqlSession.commit()方法,相当于使用了 JDBC 中的connection.commit()方法实现事务提交,故我们可以connection.setAutoCommit(true)

可以使用所对应的 DefaultSqlSessionFactory 类的源代码:

@Override
public SqlSession openSession(boolean autoCommit) {
  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
}

上面代码修改为不使用手动提交,实现新增操作

private InputStream in;
private SqlSession sqlSession;
private IUserDao userDao;

@Before //用于在测试方法执行之前执行
public void init() throws Exception{
    //1.读取配置文件,生成字节输入流
    in = Resources.getResourceAsStream("SqlMapConfig.xml");
    //2.获取SqlSessionFactory
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    //3.获取SqlSession对象
    //openSession方法设置为true就可以不用提交事务
    //注意:在mybatis中独立的这种事务的设置是可以的,但是如转账这种多事务这样对每个事务的独立设置是不可以的
    sqlSession = factory.openSession(true);
    //4.获取dao的代理对象
    userDao = sqlSession.getMapper(IUserDao.class);
}

@After //用于在测试方法执行之后执行
public void destroy() throws Exception {
    //提交事务
    //sqlSession.commit(); //没有这条语句就会产生事务回滚,无法正常添加
    //6.释放资源
    sqlSession.close();
    in.close();
}

/**
 * 测试保存操作
 */
@Test
public void testSave() {
    User user = new User();
    user.setUserName("autocommit");
    user.setUserAddress("北京市顺义区");
    user.setUserSex("男");
    user.setUserBirthday(new Date());
    System.out.println("保存操作之前" + user);
    //5.执行保存方法
    userDao.saveUser(user);

    System.out.println("保存操作之后" + user);
}

控制台输出

在这里插入图片描述

此时自动提交设置为true,数据库新增成功

总结

如果连接处于自动提交模式下,则其所有的SQL语句将作为单个事务运行并提交。否则,其SQL语句将作为事务组,直到调用Commit方法或rollback方法为止。默认情况下,新连接处于自动提交模式。

故需要注意的是,单个CUD设置即独立的这种事务自动提交为true是没有问题的,如果多个sql的CUD如转账这种多事务,这样对每个事务的独立设置是不可以的,需要手动进行提交。

mybatis的动态SQL

引入

Mybatis 的映射文件中,前面我们的 SQL 都是比较简单的,有些时候业务逻辑复杂时,我们的 SQL 是动态变化的,此时在前面的学习中我们的 SQL 就不能满足要求了。

if标签

我们根据实体类的不同取值,使用不同的 SQL 语句来进行查询。比如在 id 如果不为空时可以根据 id 查询,如果 username 不同空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到。

dao
/**
 * 根据传入参数条件查询
 * @param user 查询的条件:有可能有用户名,有可能有性别,也有可能有地址,还有可能都有
 * @return
 */
List<User> findUserByCondition(User user);
xml
<!--根据条件查询-->
<select id="findUserByCondition" resultMap="userMap" parameterType="user">
    select * from user where 1=1
    <if test="userName != null and userName != ''">
        and username like #{userName}
    </if>
    <if test="userSex != null and userSex != ''">
        and sex = #{userSex}
    </if>
</select>

注意:标签的 test 属性中写的是对象的属性名,如果是包装类的对象要使用 OGNL 表达式的写法。另外要注意 where 1=1 的作用~!

测试
@Test
public void testFindUserByCondition() {
    User u = new User();
    u.setUserName("%王%");
    List<User> users = userDao.findUserByCondition(u);
    for (User user : users) {
        System.out.println(user);
    }
}

where标签

为了简化上面 where 1=1 的条件拼装,我们可以采用标签来简化xml开发。

xml
<!--使用where标签-->
<select id="findUserByCondition" resultMap="userMap" parameterType="user">
    select * from user
    <where>
        <if test="userName != null and userName != ''">
            and username like #{userName}
        </if>
        <if test="userSex != null and userSex != ''">
            and sex = #{userSex}
        </if>
    </where>
</select>

foreach标签

需求

传入多个 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中加入一个List集合用于封装参数
package com.itheima.domain;

import java.util.List;

public class QueryVo {

    private User user;

    private List<Integer> ids;

    public User getUser() {
        return user;
    }

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

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

    public void setIds(List<Integer> ids) {
        this.ids = ids;
    }
}
dao
/**
 * 根据queryvo中提供的id集合,查询用户信息
 * @param vo
 * @return
 */
List<User> findUserInIds(QueryVo vo);
xml
<!--根据queryvo中的id集合实现查询用户列表-->
<select id="findUserInIds" resultMap="userMap" parameterType="queryvo">
    select * from user
    <where>
        <if test="ids != null and ids.size() > 0">
            <foreach collection="ids" open="and id in (" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </if>
    </where>
</select>

SQL语句:select 字段 from user where id in (?)
<foreach>标签用于遍历集合,它的属性:
collection:代表要遍历的集合元素,注意编写时不要写#{}
open:代表语句的开始部分
close:代表结束部分
item:代表遍历集合的每个元素,生成的变量名
sperator:代表分隔符
测试
/**
 * 测试foreach标签的使用
 */
@Test
public void testFindInIds() {
    QueryVo vo = new QueryVo();
    List<Integer> list = new ArrayList<Integer>();
    list.add(41);
    list.add(42);
    list.add(46);
    vo.setIds(list);
    //5.执行查询所有方法
    List<User> users = userDao.findUserInIds(vo);
    for (User user : users) {
        System.out.println(user);
    }
}

mybatis中简化编写的SQL片段

作用

抽取重复的语句代码片段

xml

<!--了解的内容:抽取重复的sql语句 以查询所有为例,引用这个sql标签-->
<sql id="defaultUser">
    select * from user
</sql>

<select id="findAll" resultMap="userMap">
    <include refid="defaultUser"></include>
</select>

mybatis多表查询之一对多

本次案例主要以最为简单的用户和账户的模型来分析 Mybatis 多表关系。用户为 User 表,账户为Account表。一个用户(User)可以有多个账户(Account)。具体如图所示

在这里插入图片描述

一对一查询(多对一)

需求

查询所有账户信息,关联查询下单用户信息。

注意

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

方式一

账户实体类

package com.itheima.domain;

import java.io.Serializable;

public class Account implements Serializable {

    private Integer id;
    private Integer uid;
    private Double money;
    
}

编写Sql语句

SELECT
	account.*,
	user.username,
	user.address 
FROM
	account,
    user 
WHERE
	account.uid = user.id

定义AccountUser类

目的:为了能够封装上面 SQL 语句的查询结果,定义 AccounUser 类中要包含账户信息同时还要包含用户信息,所以我们要在定义 AccountUser 类时可以继承 Account 类。

package com.itheima.domain;

public class AccountUser extends Account {

    private String username;
    private String address;
    
}

持久层dao

package com.itheima.dao;

import com.itheima.domain.Account;
import com.itheima.domain.AccountUser;

import java.util.List;

public interface IAccountDao {
    /**
     * 查询所有账户,同时还要获取当前账户的所属用户信息
     * @return
     */
    List<Account> findAll();
}

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.itheima.dao.IAccountDao">
    <!--查询所有用户账户同时包含用户名和地址信息-->
    <select id="findAll" resultType="accountuser">
        select a.*,u.username,u.address from account a, user u where u.id = a.uid
    </select>
</mapper>

测试类

package com.itheima.test;

import com.itheima.dao.IAccountDao;
import com.itheima.dao.IUserDao;
import com.itheima.domain.Account;
import com.itheima.domain.AccountUser;
import com.itheima.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.InputStream;
import java.util.List;

/**
 * 测试mybatis的crud操作
 */
public class AccountTest {

    private InputStream in;
    private SqlSession sqlSession;
    private IAccountDao accountDao;

    @Before //用于在测试方法执行之前执行
    public void init() throws Exception{
        //1.读取配置文件,生成字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.获取SqlSessionFactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3.获取SqlSession对象
        //openSession方法设置为true就可以不用提交事务
        //注意:在mybatis中独立的这种事务的设置是可以的,但是如转账这种多事务这样对每个事务的独立设置是不可以的
        sqlSession = factory.openSession(true);
        //4.获取dao的代理对象
        accountDao = sqlSession.getMapper(IAccountDao.class);
    }

    @After //用于在测试方法执行之后执行
    public void destroy() throws Exception {
        //提交事务
        //sqlSession.commit(); //没有这条语句就会产生事务回滚,无法正常添加
        //6.释放资源
        sqlSession.close();
        in.close();
    }

    /**
     * 测试查询所有账户,同时包含用户名称和地址
     */
    @Test
    public void testFindAllAccount() {
        List<AccountUser> aus = accountDao.findAllAccount();
        for (Account au : aus) {
            System.out.println(au);
        }
    }
}

小结

定义专门的 po 类作为输出类型,其中定义了 sql 查询结果集所有的字段。此方法较为简单,企业中使用普遍。

方式二

使用 resultMap,定义专门的 resultMap 用于映射一对一查询结果。通过面向对象的(has a)关系可以得知,我们可以在 Account 类中加入一个 User 类的对象来代表这个账户是哪个用户的。

修改Account类

package com.itheima.domain;

import java.io.Serializable;

public class Account implements Serializable {

    private Integer id;
    private Integer uid;
    private Double money;

    //从表实体应该包含一个主表实体的对象引用
    private User user;
    
}

持久层dao

注意:第二种方式,将返回值改为了 Account 类型。因为 Account 类中包含了一个 User 类的对象,它可以封装账户所对应的用户信息

package com.itheima.dao;

import com.itheima.domain.Account;
import com.itheima.domain.AccountUser;

import java.util.List;

public interface IAccountDao {
    /**
     * 查询所有账户,同时还要获取当前账户的所属用户信息
     * @return
     */
    List<Account> findAllAccountUser();
}

xml

注意:association标签用于指定从表方的引用实体属性的,javaType属性提供user信息

<?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.dao.IAccountDao">

    <!--定义封装account和user的resultMap-->
    <resultMap id="accountUserMap" type="account">
        <id property="id" column="aid"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!--
        一对一的关系映射,配置封装user的内容
        用javaType属性提供user信息
        -->
        <association property="user" column="uid" javaType="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>
        </association>
    </resultMap>

    <!--查询所有-->
    <select id="findAllAccountUser" resultMap="accountUserMap">
        SELECT u.*,a.id AS aid,a.uid,a.money FROM account a, USER u WHERE u.id = a.uid
    </select>
  </mapper>

测试类

/**
 * 测试查询所有账户
 */
@Test
public void testFindAllAccountUser() {
    List<Account> accounts = accountDao.findAllAccountUser();
    for (Account account : accounts) {
        System.out.println("---------------每个account的信息--------------");
        System.out.println(account);
        System.out.println(account.getUser());
    }
}

一对多查询

需求

查询所有用户信息及用户关联的账户信息。

分析

用户信息和他的账户信息为一对多关系,并且查询过程中如果用户没有账户信息,此时也要将用户信息查询出来,我们想到了左外连接查询比较合适。

编写SQL语句
SELECT
	u.*,
	acc.id id,
	acc.uid,
	acc.money 
FROM
	user u
	LEFT JOIN account acc ON u.id = acc.uid
修改User类
package com.itheima.domain;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

/*
* 实体类名称跟数据库字段不一样
* */
public class User implements Serializable {

    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;

    //一对多关系映射,主表实体应该包含从表实体的集合引用
    private List<Account> accounts;
}
持久层dao
package com.itheima.dao;

import com.itheima.domain.User;

import java.util.List;

/**
 * 用户的持久层接口
 */
public interface IUserDao {
    /**
     * 查询所有用户,同时获取到用户下所有账户的信息
     * @return
     */
    List<User> findAll();
}
xml

注意:collection标签是用于建立一对多中集合属性的对应关系,property属性用于指定关联查询的结果集存储在实体类对象的上哪个属性,ofType属性用于指定集合元素的数据类型

collection标签:部分定义了用户关联的账户信息。表示关联查询结果集

property=“accList”:关联查询的结果集存储在 User 对象的上哪个属性。

ofType=“account”:指定关联查询的结果集中的对象类型即List中的对象类型。此处可以使用别名,也可以使用全限定名。

<?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.dao.IUserDao">

    <!--定义User的resultMap-->
    <resultMap id="userAccountMap" 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>
        <!--
        配置user对象中accounts集合的映射
        ofType属性是指在集合中元素的位置
        -->
        <collection property="accounts" ofType="account">
            <id property="id" column="aid"></id>
            <result property="uid" column="uid"></result>
            <result property="money" column="money"></result>
        </collection>
    </resultMap>

    <!--查询所有 mybatis框架可以识别重复的部分并且把对应的信息封装进入-->
    <select id="findAll" resultMap="userAccountMap">
        select * from user u left outer join account a on u.id = a.uid
    </select>
    
  </mapper>
测试类
/**
 * 测试查询所有账户
 */
@Test
public void testFindAll() {
    List<User> users = userDao.findAll();
    for (User user : users) {
        System.out.println("---------------每个用户的信息--------------");
        System.out.println(user);
        System.out.println(user.getAccounts());
    }
}

mybatis多表查询之多对多

实现Role到User多对多

多对多关系其实我们看成是双向的一对多关系。 数据库中添加角色表,用户角色的中间表:角色表用户与角色的关系模型如下

在这里插入图片描述

需求

实现查询所有对象并且加载它所分配的用户信息。

分析

查询角色我们需要用到Role表,但角色分配的用户的信息我们并不能直接找到用户信息,而是要通过中间表(USER_ROLE 表)才能关联到用户信息。

编写sql语句
SELECT
	r.*,
	u.id uid,
	u.username username,
	u.birthday birthday,
	u.sex sex,
	u.address address 
FROM
	ROLE r
	LEFT JOIN USER_ROLE ur ON ( r.id = ur.rid )
	LEFT JOIN USER u ON ( ur.uid = u.id );
编写角色实体类
package com.itheima.domain;

import java.io.Serializable;
import java.util.List;

public class Role implements Serializable {

    private Integer roleId;
    private String roleName;
    private String roleDesc;

    //多对多的关系映射,一个角色可以赋予多个用户
    private List<User> users;
    
}
编写Role持久层接口
package com.itheima.dao;

import com.itheima.domain.Role;

import java.util.List;

public interface IRoleDao {

    /**
     * 查询所有角色
     * @return
     */
    List<Role> findAll();
}
编写映射文件
<?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.dao.IRoleDao">
    <!--定义role表的resultMap-->
    <resultMap id="roleMap" type="role">
        <!--角色被重命名了,所以为rid-->
        <id property="roleId" column="id"></id>
        <result property="roleName" column="role_name"></result>
        <result property="roleDesc" column="role_desc"></result>
        <collection property="users" ofType="user">
            <id property="id" column="uid"></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>
    </resultMap>

    <!--查询所有-->
    <!--注意写较长的SQL语句时,换行需要在行后或者下一行前加一个空格-->
    <select id="findAll" resultMap="roleMap">
        SELECT
            r.*,
            u.id uid,
            u.username username,
            u.birthday birthday,
            u.sex sex,
            u.address address 
        FROM
            ROLE r
            LEFT JOIN USER_ROLE ur ON ( r.id = ur.rid )
            LEFT JOIN USER u ON ( ur.uid = u.id );
    </select>
</mapper>
编写测试类
/**
 * 测试查询所有角色
 */
@Test
public void testFindAll() {
    List<Role> roles = roleDao.findAll();
    for (Role role : roles) {
        System.out.println("---------每个角色的信息---------");
        System.out.println(role);
        System.out.println(role.getUsers());
    }
}

实现User到Role的多对多

从 User 出发,我们也可以发现一个用户可以具有多个角色,这样用户到角色的关系也还是一对多关系。这样我们就可以认为 User 与 Role 的多对多关系,可以被拆解成两个一对多关系来实现。

需求

实现查询所有用户信息并关联查询出每个用户的角色列表。

编写sql语句
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
编写角色实体类
package com.itheima.domain;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

/*
* 实体类名称跟数据库字段不一样
* */
public class User implements Serializable {

    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;

    //多对多的关系映射,一个用户可以具备多个角色
    private List<Role> roles;
    
}
编写User持久层接口
package com.itheima.dao;

import com.itheima.domain.User;

import java.util.List;

/**
 * 用户的持久层接口
 */
public interface IUserDao {

    /**
     * 查询所有用户,同时获取到用户下所有账户的信息
     * @return
     */
    List<User> findAll();
    
}
编写映射文件
<?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.dao.IUserDao">

    <!--定义User的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>

    <!--查询所有 mybatis框架可以识别重复的部分并且把对应的信息封装进入-->
    <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>

</mapper>
编写测试类
/**
 * 测试查询所有账户
 */
@Test
public void testFindAll() {
    List<User> users = userDao.findAll();
    for (User user : users) {
        System.out.println("---------------每个用户的信息--------------");
        System.out.println(user);
        System.out.println(user.getRoles());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值