MyBatis-03

MyBatis-03

一、数据源和事务

1. Mybatis的数据源

Mybatis中的数据源,是指核心配置文件中<dataSouce></dataSouce>的配置。Mybatis为了提高数据库操作的性能,也使用了连接池的技术,但是它采用的是自己开发的连接池技术。

1.1 Mybatis中dataSouce的分类
1.1.1 三种dataSouce介绍
  • UNPOOLED:不使用连接池技术的数据源

    对应Mybatis的UnpooledDataSouce类,虽然也实现了javax.sql.DataSource接口,但是它的getConnection()方法中,并没有真正的使用连接池技术,而是直接从数据库中创建的连接对象,即:DriverManager.getConnection()方法

  • POOLED:使用连接池技术的数据源

    对应Mybatis的PooledDataSouce类,它实现了javax.sql.DataSouce接口,同时采用了Mybatis自己开发的连接池技术,是我们使用最多的一种数据源

  • JNDI:使用JNDI技术的数据源

    采用服务器软件提供的JNDI技术实现,从服务器软件中获取数据源。从不同服务器软件中得到的DataSouce对象不同。例如:Tomcat中配置了数据连接信息,我们的web应用部署到Tomcat里,就可以获取Tomcat中配置的数据源,而不需要web应用自己再配置数据库连接信息。

1.1.2 三种dataSouce的关系与源码分析
  • UnpooledDataSourcePooledDataSource都实现了javax.sql.DataSource接口

  • UnpooledDataSource没有使用连接池技术,它的getConnection()方法是从数据库中创建的连接

  • PooledDataSource采用了连接池技术

    • 它内部有UnpooledDataSource的引用,当需要创建新连接时,是调用UnpooledDataSource来获取的
    • 它只是提供了用于存放连接对象的池子
1.1.3 PooledDataSource获取新连接的过程源码分析
1.2 Mybatis中dataSouce的配置
<dataSource type="POOLED">
    <property name="driver" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql:///mybatis"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
</dataSource>

​ 当Mybatis读取核心配置文件时,会根据我们配置的dataSource标签的type,来生成对应的dataSource对象

2. Mybatis的事务

2.1 事务回顾
2.1.1 什么是事务

事务:是数据库里的一个概念,表示组成一个操作多个单元,要么全部成功,要么全部失败

2.1.2 事务的四大特性ACID(面试题)
  • Atomicity:原子性,指组成事务的多个单元,要么全部成功,要么全部失败。不可能存在成功一半的情况
  • Customary:一致性,指事务提交前后,数据是一致的,不可能凭空丢失或者增加数据
  • Isolation:隔离性,表示多个事务并发时,事务之间应该是相互独立、互不干扰的
  • Durability:持久性,表示事务提交之后,数据永久的保存到了磁盘文件上
2.1.3 事务并发存在的问题
  • 脏读:一个事务读取到了另外一个事务未提交的数据
  • 不可重复读:在一个事务里,多次读取的数据不一样–受到了其它事务里update操作的干扰
  • 虚读/幻读:在一个事务里,多次读取的数据不一样–受到了其它事务里insert、delete操作的干扰
2.1.4 事务的隔离级别
  • read uncommitted:读未提交
    • 解决了:无
    • 还存在:脏读、不可重复读、虚读/幻读
  • read committed:读已提交(Oracle的默认隔离级别)
    • 解决了:脏读
    • 还存在:不可重复读、虚读/幻读
  • repeatable read:重复读(MySql的默认隔离级别)
    • 解决了:脏读、不可重复读
    • 还存在:虚读/幻读
  • serializable:串行化
    • 解决了:脏读、不可重复读、虚读/幻读
    • 还存在:无
2.1.5 JDBC的事务管理

JDBC的事务管理是基于Connection对象实现的:

  • 开启事务:connection.setAutoCommit(false)
  • 提交事务:connection.commit()
  • 回滚事务:connection.rollback()
2.2 Mybatis的事务管理

因为Mybatis的是对JDBC的封装,所以Mybatis在本质上也是基于Connection对象实现的事务管理,只是把管理的代码封装起来了,是使用SqlSession对象进行事务管理的。

2.2.1 Mybatis的默认事务管理方式

默认情况下,我们使用工厂对象的openSession()方法得到的SqlSession对象,是关闭了事务自动提交的,即:默认情况下,SqlSession是开启了事务的。

操作完数据库之后,需要手动提交事务:sqlSession.commit();

如果要回滚事务,就使用方法:sqlSession.rollback();

2.2.2 Mybatis的自动提交事务实现

Mybatis也支持自动提交事务,操作方法如下:

  1. 获取SqlSession对象:factory.openSession(true)
  2. 操作数据库,事务会自动提交
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//获取的是自动提交事务的SqlSession,所以不需要再手动关闭事务
SqlSession session = factory.openSession(true);
IUserDao dao = session.getMapper(IUserDao.class);

//操作数据库
dao.delete(48);

//释放资源,事务会自动提交,所以不需要再手动提交事务
session.close();
is.close();

二、SQL深入

1. Mybatis的环境准备

  1. 创建Maven的java项目,配置好坐标,引入Mybatis的依赖

  2. 创建JavaBean实体类:User

    public class User {
        private Integer id;
        private String username;
        private Date birthday;
        private String sex;
        private String address;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public Date getBirthday() {
            return birthday;
        }
    
        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", birthday=" + birthday +
                    ", sex='" + sex + '\'' +
                    ", address='" + address + '\'' +
                    '}';
        }
    }
    
  3. 创建映射器接口IUserDao(只是准备好备用,暂时不需要加方法)

    public interface IUserDao {
    }
    
  4. 创建映射配置文件IUserDao.xml(准备好备用)

    <mapper namespace="com.viking.dao.IUserDao">
    
    </mapper>
    
  5. 创建Mybatis的核心配置文件,配置好类型别名和映射器

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <typeAliases>
            <package name="com.viking.bean"/>
        </typeAliases>
    
        <environments default="mysql_mybatis">
            <environment id="mysql_mybatis">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql:///mybatis"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                </dataSource>
            </environment>
        </environments>
    
        <mappers>
            <package name="com.viking.dao"/>
        </mappers>
    </configuration>
    
  6. 准备好单元测试类(准备好备用)

    public class MybatisSqlTest {
        private InputStream is;
        private SqlSession session;
        private IUserDao dao;
    
        @Before
        public void init() throws IOException {
            is = Resources.getResourceAsStream("SqlMapConfig.xml");
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory factory = builder.build(is);
            session = factory.openSession();
            dao = session.getMapper(IUserDao.class);
        }
    
        @After
        public void destory() throws IOException {
            //7. 释放资源
            session.close();
            is.close();
        }
    }
    

2. Mybatis的动态SQL拼接

在Mybatis中,SQL语句是写在映射配置的XML文件中的。Mybatis提供了一些XML的标签,用来进行逻辑判断、完成SQL的拼接。常用的标签有:

  • <if></if>:用来进行判断,相当于Java里的if判断
  • <where></where>:通常和if配合,用来代替SQL语句中的where 1=1
  • <foreach></foreach>:用来遍历一个集合,把集合里的内容拼接到SQL语句中。例如拼接:in (value1, value2, ...)
2.1 <if>标签:
2.1.1 语法
<if test="判断条件,使用OGNL表达式进行判断">
	SQL语句内容, 如果判断为true,这里的SQL语句就会进行拼接
</if>
2.1.2 使用示例

​ 需求:根据用户的名称和性别搜索用户信息

  1. 在映射器接口IUserDao中增加方法
public interface IUserDao {
	//根据user搜索用户信息
    List<User> search(User user);
}
  1. 在映射配置文件中配置映射信息
    <select id="search" parameterType="user" resultType="user">
        select * from user where 1=1
        <if test="username != null">
            and username like #{username}
        </if>
        <if test="sex != null">
            and sex = #{sex}
        </if>
    </select>

注意:

  1. SQL语句中   where 1=1 不能省略
  2. 在if标签的test属性中,直接写OGNL表达式,从parameterType中取值进行判断,不需要加#{}或者${}
  1. 编写测试代码
    @Test
    public void testSearch(){
        User user = new User();
        user.setUsername("%王%");
        user.setSex("男");
        
        List<User> users = dao.search(user);
        for (User u : users) {
            System.out.println(u);
        }
    }
2.2 <where>标签

在刚刚的练习的SQL语句中,我们写了where 1=1。如果不写的话,SQL语句会出现语法错误。Mybatis提供了一种代替where 1=1的技术:<where></where>标签。

使用示例:

  1. 只需要把刚刚的映射配置文件进行修改,修改后:
    <select id="search" parameterType="user" resultType="user">
        select * from user
        <where>
            <if test="username != null">
                and username like #{username}
            </if>
            <if test="sex != null">
                and sex = #{sex}
            </if>
        </where>
    </select>

注意:

  1. <where>标签代替了where 1=1
  2. <where>标签内拼接的SQL没有变化,每个if的SQL中都有and
  3. 使用<where>标签时,Mybatis会自动处理掉条件中的第1个and,以保证SQL语法正确
  1. 再次运行测试代码,查看结果仍然正常
2.3 <foreach>标签

foreach标签,通常用于循环遍历一个集合,把集合的内容拼接到SQL语句中。例如,我们要根据多个id查询用户信息,SQL语句:

select * from user where id = 1 or id = 2 or id = 3;
select * from user where id in (1, 2, 3);

​ 假如我们传参了id的集合,那么在映射配置文件中,如何遍历集合拼接SQL语句呢?可以使用foreach标签实现。

2.3.1 语法
foreach标签:
	属性:
		collection:被循环遍历的对象,注意不要加#{}
		open:SQL语句的开始部分
		item:代表被循环遍历中每个元素,生成的变量名
		separator:分隔符
		close:SQL语句的结束部分
	标签体:
		使用#{OGNL}表达式,获取到被循环遍历对象中的每个元素

扩展:如果映射器接口方法传参是List,foreach标签的配置如下:

<!-- parameterType属性可以不加;如果加的话,值应该是arraylist -->
<select id="方法名" parameterType="arraylist" resultType="结果集封装类型">
    select * from user where id in 
    
    <!-- collection的值是固定值,就是list -->
    <foreach collection="list" open="(" item="id" separator="," close=")">
    	#{id}
    </foreach>
</select>

扩展:如果映射器接口方法传参是数组,foreach标签的配置如下:

<!-- parameterType属性可以不加;如果加的话,值应该是arraylist -->
<select id="方法名" parameterType="arraylist" resultType="结果集封装类型">
    select * from user where id in 
    
    <!-- collection的值是固定值,就是array -->
    <foreach collection="list" open="(" item="id" separator="," close=")">
    	#{id}
    </foreach>
</select>
2.3.2 使用示例

根据id的集合,查询用户列表

  1. 创建QueryVO类
public class QueryVO {
    
    private Integer[] ids;

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

    public void setIds(Integer[] ids) {
        this.ids = ids;
    }
}
  1. 在映射器接口中增加方法
    List<User> findByIds(QueryVO vo);
  1. 在映射配置文件中添加配置信息
<!--在核心配置文件中已经使用package配置了类型别名-->
<select id="findByIds" resultType="user" parameterType="queryvo">
    select * from user 
    <where>
        <foreach collection="ids" open="and id in (" item="id" separator="," close=")">
            #{id}
        </foreach>
    </where>
</select>
  1. 编写测试代码
    @Test
    public void testFindUserByIdsQueryVO(){
        QueryVO vo = new QueryVO();
        vo.setIds(new Integer[]{41, 42});

        List<User> userList = dao.findByIds(vo);
        for (User user : userList) {
            System.out.println(user);
        }
    }

3. Mybatis的SQL片段-<sql>标签和<include>

在映射配置文件中,我们发现有很多SQL片段是重复的,比如:select * from user。Mybatis提供了一个<sql>标签,用来把重复的SQL片段抽取出来,以达到重复复用的目的。

  1. 在映射配置文件IUserDao.xml中定义SQL片段
    <sql id="selectUser">
        select * from user
    </sql>
  1. 在映射配置文件IUserDao.xml中使用SQL片段
<select id="findByIds" resultType="user" parameterType="queryvo">
    <!-- refid属性:要引用的sql片段的id -->
    <include refid="selectUser"></include> 
    <where>
        <foreach collection="ids" open="and id in (" item="id" separator="," close=")">
            #{id}
        </foreach>
    </where>
</select>

扩展:

​ 如果想要引入其它映射配置文件中的sql片段,那么<include>标签的refid的值,需要在sql片段的id前指定namespace。例如:

<include refid="com.viking.dao.IRoleDao.selectRole"></include>

​ 表示引入了namespace为com.viking.dao.IRoleDao的映射配置文件中id为selectRole的sql片段

三、多表关联查询

1. 多表关系和多表查询回顾

1.1 多表关系回顾
  1. 多表关系有:

    • 一对一:一个人只能有一个身份证号
    • 一对多:一个用户可以有多个银行帐号, 一个用户可以下多个订单
    • 多对一:一个银行帐号只能属于一个用户,一个订单只能属于一个用户
    • 多对多:一个用户可以有多个角色、一个角色可以有多个用户
  2. Mybatis多表查询时的关系分类

    Mybatis 把多对一当成一对一处理;把多对多当成一对多处理了。所以Mybatis的多表关联查询只有两类:

    • 一对一(多对一)
    • 一对多(多对多)
1.2 多表查询回顾
  1. 内连接查询

    • 表示查询两张表必定有关联的数据

    • 显式内连接: select * from 表1 inner join 表2 on 表关联条件

    • 隐式内连接: select * from 表1, 表2 where 表关联条件

  2. 外连接查询

    • 左外连接查询:查询左表的全部数据,以及右表的关联数据

      select * from 表1 left join 表2 on 表关联条件

    • 右外连接查询:查询右表的全部数据,以及左表的关联数据

      select * from 表1 right join 表2 on 表关联条件

2. 一对一(多对一)关联查询

需求:查询帐户表(account)信息,及其关联的用户(user)信息

2.1 准备工作
  1. 创建Maven的java项目,配置项目坐标,并引入Mybatis的依赖

  2. 分别创建好user表和account表的实体类:User和Account

public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}
public class Account {
    private Integer id;
    private Integer uid;
    private Double money;

    public Integer getId() {
        return id;
    }

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

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", uid=" + uid +
                ", money=" + money +
                '}';
    }
}
  1. 在dao中创建映射器接口IAccountDao(准备好 备用,不需要添加方法)
public interface IAccountDao {
}
  1. 创建映射器的配置文件IAccountDao.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.viking.dao.IAccountDao">

</mapper>
  1. 创建Mybatis核心配置文件,配置好类型别名和映射器
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <typeAliases>
        <package name="com.viking.bean"/>
    </typeAliases>

    <environments default="mysql_mybatis">
        <environment id="mysql_mybatis">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <package name="com.viking.dao"/>
    </mappers>
</configuration>
  1. 编写好单元测试类(准备好备用)
public class MybatisOne2OneTest {
    private InputStream is;
    private SqlSession session;
    private IAccountDao dao;


    @Before
    public void init() throws IOException {
        is = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(is);
        session = factory.openSession();
        dao = session.getMapper(IAccountDao.class);
    }

    @After
    public void destory() throws IOException {
        //7. 释放资源
        session.close();
        is.close();
    }
}
2.2 一对一(多对一)的第一种方案-类继承方式(不推荐)

这种方式的重点在于:创建新的JavaBean,定义与所有字段对应的属性。可以使用继承的方式来减少代码量。例如:

  1. 创建UserAccount类,定义user表对应的属性,然后继承Account类

    或者

  2. 创建UserAccount类,定义account表对应的属性,然后继承User类

2.2.1 创建新的JavaBean:UserAccount
public class UserAccount extends Account {
    private String username;
    private String address;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
    
    @Override
    public String toString() {
        return super.toString() + "UserAccount{" +
                "username='" + username + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}
2.2.2 在映射器接口IAccountDao中增加方法
//查询所有帐号,及其关联的用户信息--类继承的方式
List<UserAccount> queryAllAccounts1();
2.2.3 在映射器配置文件IAccountDao.xml中增加配置信息
<select id="queryAllAccounts1" resultType="userAccount">
    select a.*, u.username, u.address from account a left join user u on a.uid = u.id
</select>
2.2.4 编写测试代码
@Test
public void testQueryAllAccounts(){
    List<UserAccount> userAccounts = dao.queryAllAccounts1();
    for (UserAccount userAccount : userAccounts) {
        System.out.println(userAccount);
    }
}
2.2 一对一(多对一)的第二种方案-类引用方式(推荐)

这种方式的重点在于:JavaBean中要有另外一个JavaBean的引用。例如:在Account中增加一个属性user,指向User对象。

​ 在封装结果集时,使用resultMap设置字段和属性的对应关系;使用resultMap的子标签association封装关联的User对象。

2.2.1 修改JavaBean:Account类

注意:Account中要有User的引用

public class Account {
    private Integer id;
    private Integer uid;
    private Double money;
    
    private User user;

    public Integer getId() {
        return id;
    }

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

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }

    public User getUser() {
        return user;
    }

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

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", uid=" + uid +
                ", money=" + money +
                ", user=" + user +
                '}';
    }
}
2.2.2 在映射器接口IAccountDao中增加方法
//查询所有帐号,及其关联的用户信息-类引用方式
List<Account> queryAllAccounts2();
2.2.3 在映射配置文件IAccountDao.xml中增加配置信息
<select id="queryAllAccounts2" resultMap="AccountUserMap">
    SELECT a.id aid, a.uid uid, a.money money, u.* FROM account a LEFT JOIN USER u ON a.uid = u.id
</select>


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

    <!-- 
	association:用于把结果集中某些列的数据,封装到JavaBean中关联的一个对象上。用于一对一情形
		property:把数据封装到哪个属性关联的对象上
		javaType:关联的对象是什么类型的。是com.viking.bean.User,这里使用了别名
	-->
    <association property="user" javaType="user">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="birthday" column="birthday"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>
    </association>
</resultMap>

注意:

​ 在多表关联查询时,必须要保证SQL语句查询的结果中没有相同名称的列;否则就会丢失数据

2.2.4 编写测试代码
@Test
public void testQueryAllAccounts2(){
    List<Account> accounts = dao.queryAllAccounts2();
    for (Account account : accounts) {
        System.out.println(account);
    }
}

3. 一对多关联查询

需求:查询用户(user)信息,以及每个用户拥有的所有帐号(account)信息

一对多查询的重点在于:在User里添加Account的集合。以便查询出一个用户信息时,可以封装这个用户拥有的多个帐号信息

​ 在结果集封装时,使用resultMap设置字段和属性的对应关系;使用resultMap的子标签collection封装关联的Account对象集合。

3.1 创建Maven的java项目,配置项目坐标,并引入Mybatis的依赖
3.2 创建user表和account表的实体类:User和Account

注意:User类中要有List<Account>,用于保存用户拥有的帐号信息集合

public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    
    private List<Account> accounts;

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public List<Account> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                ", accounts='" + accounts + '\'' +
                '}';
    }
}
public class Account {
    private Integer id;
    private Integer uid;
    private Double money;

    public Integer getId() {
        return id;
    }

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

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", uid=" + uid +
                ", money=" + money +
                '}';
    }
}
3.3 在dao中创建映射器接口IUserDao,在接口中定义方法
public interface IUserDao {
    List<User> queryAllUsers();
}
3.4 创建映射器的配置文件IUserDao.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.viking.dao.IUserDao">

    <select id="queryAllUsers" resultMap="userAccountsMap">
        SELECT a.id aid, a.uid uid, a.money money, u.* FROM USER u LEFT JOIN account a ON u.id = a.uid
    </select>
    
    <resultMap id="userAccountsMap" type="user">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="birthday" column="birthday"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>

        <!--
 		collection:用于封装JavaBean中某一属性关联的集合,用于一对多情形
			property:封装哪个属性关联的集合
			ofType:集合中的数据类型是什么。这里是com.viking.bean.Account,使用了别名
		-->
        <collection property="accounts" ofType="account">
            <id property="id" column="aid"/>
            <result property="uid" column="uid"/>
            <result property="money" column="money"/>
        </collection>
    </resultMap>
</mapper>
3.5 创建Mybatis的核心配置文件,配置好类型别名和映射器
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeAliases>
        <package name="com.viking.bean"/>
    </typeAliases>

    <environments default="mysql_mybatis">
        <environment id="mysql_mybatis">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <package name="com.viking.dao"/>
    </mappers>
</configuration>
3.6 编写好单元测试代码
public class MybatisOne2ManyTest {
    private InputStream is;
    private SqlSession session;
    private IUserDao dao;

    @Test
    public void testQueryAllUsers(){
        List<User> users = dao.queryAllUsers();
        for (User user : users) {
            System.out.println(user);
        }
    }

    @Before
    public void init() throws IOException {
        is = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(is);
        session = factory.openSession();
        dao = session.getMapper(IUserDao.class);
    }

    @After
    public void destory() throws IOException {
        //7. 释放资源
        session.close();
        is.close();
    }
}
3.7 ★★★★★一对多和一对一关联查询的重点★★★★★
3.7.1 一对一关联查询的重点

​ 一个Account,对一个User

  1. Account里应该有User的引用
  2. 映射配置文件里的,要使用resultMap手动设置对应关系。
    • 设置User对象引用的属性时,要使用association来封装关联的一个JavaBean对象
3.7.2 一对多关联查询的重点

​ 一个User,对多个Account

  1. User里应该有Account的集合
  2. 映射配置文件里,要使用resultMap手动设置列名和属性名的对应关系
    • 设置到Account集合的属性时,要使用collection来封装关联的一个JavaBean集合

4. 多对多关联查询

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

4.1 准备工作
  1. 创建Maven的java项目,配置好坐标,引入Mybatis的依赖

  2. 创建用户信息和角色信息的实体类

    注意:User中要有Role的集合; Role中要有User的集合

    public class User {
        private Integer id;
        private String username;
        private Date birthday;
        private String sex;
        private String address;
    
        private List<Role> roles;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public Date getBirthday() {
            return birthday;
        }
    
        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        public List<Role> getRoles() {
            return roles;
        }
    
        public void setRoles(List<Role> roles) {
            this.roles = roles;
        }
        
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", birthday=" + birthday +
                    ", sex='" + sex + '\'' +
                    ", address='" + address + '\'' +
                    ", roles=" + roles +
                    '}';
        }
    }
    
    public class Role {
        private Integer id;
        private String roleName;
        private String roleDesc;
    
        private List<User> users;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getRoleName() {
            return roleName;
        }
    
        public void setRoleName(String roleName) {
            this.roleName = roleName;
        }
    
        public String getRoleDesc() {
            return roleDesc;
        }
    
        public void setRoleDesc(String roleDesc) {
            this.roleDesc = roleDesc;
        }
    
        public List<User> getUsers() {
            return users;
        }
    
        public void setUsers(List<User> users) {
            this.users = users;
        }
        
        @Override
        public String toString() {
            return "Role{" +
                    "id=" + id +
                    ", roleName='" + roleName + '\'' +
                    ", roleDesc='" + roleDesc + '\'' +
                    ", users=" + users +
                    '}';
        }
    }
    
  3. 创建映射器接口IUserDao和IRoleDao(准备好备用,暂时不需要加方法)

    public interface IUserDao {
    }
    
    public interface IRoleDao {
    }
    
  4. 创建映射配置文件IUserDao.xml和IRoleDao.xml

    创建IUserDao.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.viking.dao.IUserDao">
    
    </mapper>
    

    创建IRoleDao.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.viking.dao.IRoleDao">
    
    </mapper>
    
  5. 创建Mybatis的核心配置文件,配置好别名和映射器

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    
        <typeAliases>
            <package name="com.viking.bean"/>
        </typeAliases>
    
        <environments default="mysql_mybatis">
            <environment id="mysql_mybatis">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql:///mybatis"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                </dataSource>
            </environment>
        </environments>
    
        <mappers>
            <package name="com.viking.dao"/>
        </mappers>
    </configuration>
    
  6. 准备单元测试类(准备好备用)

    public class MybatisMany2ManyTest {
        private InputStream is;
        private SqlSession session;
        private IUserDao userDao;
        private IRoleDao roleDao;
        
        
        
        @Before
        public void init() throws IOException {
            is = Resources.getResourceAsStream("SqlMapConfig.xml");
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory factory = builder.build(is);
            session = factory.openSession();
            userDao = session.getMapper(IUserDao.class);
            roleDao = session.getMapper(IRoleDao.class);
        }
    
        @After
        public void destory() throws IOException {
            //7. 释放资源
            session.close();
            is.close();
        }
    }
    
4.2 查询用户信息,及其关联的角色集合
  1. 在映射器接口IUserDao中增加方法

    //查询所有用户信息,及其关联的角色集合
    List<User> queryAllUsers();
    
  2. 在映射配置文件IUserDao.xml中增加配置信息

        <select id="queryAllUsers" resultMap="userRolesMap">
            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 id="userRolesMap" type="user">
            <id property="id" column="id"/>
            <result property="username" column="username"/>
            <result property="address" column="address"/>
            <result property="birthday" column="birthday"/>
            <result property="sex" column="sex"/>
    		
            <!--
     		collection:用于封装JavaBean里关联的角色集合
    		-->
            <collection property="roles" ofType="role">
                <id property="id" column="rid"/>
                <result property="roleName" column="roleName"/>
                <result property="roleDesc" column="roleDesc"/>
            </collection>
        </resultMap>
    
  3. 编写测试代码

        /**
         * 查询所有用户信息,及其关联的角色信息集合
         */
        @Test
        public void testQueryAllUsers(){
            List<User> users = userDao.queryAllUsers();
            for (User user : users) {
                System.out.println(user);
            }
        }
    
4.3 查询角色信息,及其关联的用户集合
  1. 在映射器接口IRoleDao中增加方法

    //查询所有角色信息,及其关联的用户集合
    List<Role> queryAllRoles();
    
  2. 在映射器配置文件IRoleDao.xml中增加配置信息

    <select id="queryAllRoles" resultMap="roleUsersMap">
        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="roleUsersMap" type="role">
        <id property="id" column="rid"/>
        <result property="roleName" column="roleName"/>
        <result property="roleDesc" column="roleDesc"/>
    
        <collection property="users" ofType="user">
            <id property="id" column="id"/>
            <result property="username" column="username"/>
            <result property="birthday" column="birthday"/>
            <result property="sex" column="sex"/>
            <result property="address" column="address"/>
        </collection>
    </resultMap>
    
  3. 编写测试代码

    /**
     * 查询所有角色信息,及其关联的用户信息集合
     */
    @Test
    public void testQueryAllRoles(){
        List<Role> roles = roleDao.queryAllRoles();
        for (Role role : roles) {
            System.out.println(role);
        }
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值