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的关系与源码分析
-
UnpooledDataSource
和PooledDataSource
都实现了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也支持自动提交事务,操作方法如下:
- 获取SqlSession对象:
factory.openSession(true)
- 操作数据库,事务会自动提交
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的环境准备
-
创建Maven的java项目,配置好坐标,引入Mybatis的依赖
-
创建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 + '\'' + '}'; } }
-
创建映射器接口IUserDao(只是准备好备用,暂时不需要加方法)
public interface IUserDao { }
-
创建映射配置文件IUserDao.xml(准备好备用)
<mapper namespace="com.viking.dao.IUserDao"> </mapper>
-
创建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>
-
准备好单元测试类(准备好备用)
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 使用示例
需求:根据用户的名称和性别搜索用户信息
- 在映射器接口IUserDao中增加方法
public interface IUserDao {
//根据user搜索用户信息
List<User> search(User user);
}
- 在映射配置文件中配置映射信息
<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中取值进行判断,不需要加#{}或者${}
- 编写测试代码
@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>
标签。
使用示例:
- 只需要把刚刚的映射配置文件进行修改,修改后:
<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>
注意:
<where>
标签代替了where 1=1
<where>
标签内拼接的SQL没有变化,每个if的SQL中都有and
- 使用
<where>
标签时,Mybatis会自动处理掉条件中的第1个and
,以保证SQL语法正确
- 再次运行测试代码,查看结果仍然正常
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的集合,查询用户列表
- 创建QueryVO类
public class QueryVO {
private Integer[] ids;
public Integer[] getIds() {
return ids;
}
public void setIds(Integer[] ids) {
this.ids = ids;
}
}
- 在映射器接口中增加方法
List<User> findByIds(QueryVO vo);
- 在映射配置文件中添加配置信息
<!--在核心配置文件中已经使用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>
- 编写测试代码
@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片段抽取出来,以达到重复复用的目的。
- 在映射配置文件IUserDao.xml中定义SQL片段
<sql id="selectUser">
select * from user
</sql>
- 在映射配置文件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 多表关系回顾
-
多表关系有:
- 一对一:一个人只能有一个身份证号
- 一对多:一个用户可以有多个银行帐号, 一个用户可以下多个订单
- 多对一:一个银行帐号只能属于一个用户,一个订单只能属于一个用户
- 多对多:一个用户可以有多个角色、一个角色可以有多个用户
-
Mybatis多表查询时的关系分类
Mybatis 把多对一当成一对一处理;把多对多当成一对多处理了。所以Mybatis的多表关联查询只有两类:
- 一对一(多对一)
- 一对多(多对多)
1.2 多表查询回顾
-
内连接查询
-
表示查询两张表必定有关联的数据
-
显式内连接: select * from 表1 inner join 表2 on 表关联条件
-
隐式内连接: select * from 表1, 表2 where 表关联条件
-
-
外连接查询
-
左外连接查询:查询左表的全部数据,以及右表的关联数据
select * from 表1 left join 表2 on 表关联条件
-
右外连接查询:查询右表的全部数据,以及左表的关联数据
select * from 表1 right join 表2 on 表关联条件
-
2. 一对一(多对一)关联查询
需求:查询帐户表(account)信息,及其关联的用户(user)信息
2.1 准备工作
-
创建Maven的java项目,配置项目坐标,并引入Mybatis的依赖
-
分别创建好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 +
'}';
}
}
- 在dao中创建映射器接口IAccountDao(准备好 备用,不需要添加方法)
public interface IAccountDao {
}
- 创建映射器的配置文件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>
- 创建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>
- 编写好单元测试类(准备好备用)
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,定义与所有字段对应的属性。可以使用继承的方式来减少代码量。例如:
-
创建UserAccount类,定义user表对应的属性,然后继承Account类
或者
-
创建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
- Account里应该有User的引用
- 映射配置文件里的,要使用resultMap手动设置对应关系。
- 设置User对象引用的属性时,要使用association来封装关联的一个JavaBean对象
3.7.2 一对多关联查询的重点
一个User,对多个Account
- User里应该有Account的集合
- 映射配置文件里,要使用resultMap手动设置列名和属性名的对应关系
- 设置到Account集合的属性时,要使用collection来封装关联的一个JavaBean集合
4. 多对多关联查询
多对多关联查询,本质上和一对多查询是完全一样的。都是查询一张表的数据,及其关联的数据的集合。
4.1 准备工作
-
创建Maven的java项目,配置好坐标,引入Mybatis的依赖
-
创建用户信息和角色信息的实体类
注意: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 + '}'; } }
-
创建映射器接口IUserDao和IRoleDao(准备好备用,暂时不需要加方法)
public interface IUserDao { }
public interface IRoleDao { }
-
创建映射配置文件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>
-
创建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>
-
准备单元测试类(准备好备用)
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 查询用户信息,及其关联的角色集合
-
在映射器接口IUserDao中增加方法
//查询所有用户信息,及其关联的角色集合 List<User> queryAllUsers();
-
在映射配置文件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>
-
编写测试代码
/** * 查询所有用户信息,及其关联的角色信息集合 */ @Test public void testQueryAllUsers(){ List<User> users = userDao.queryAllUsers(); for (User user : users) { System.out.println(user); } }
4.3 查询角色信息,及其关联的用户集合
-
在映射器接口IRoleDao中增加方法
//查询所有角色信息,及其关联的用户集合 List<Role> queryAllRoles();
-
在映射器配置文件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>
-
编写测试代码
/** * 查询所有角色信息,及其关联的用户信息集合 */ @Test public void testQueryAllRoles(){ List<Role> roles = roleDao.queryAllRoles(); for (Role role : roles) { System.out.println(role); } }