1. mybatis连接池
- 通过SqlMapConfig.xml设置
dataSource type
实现连接池的配置
1.1 dataSource标签type属性值含义
type=”POOLED”
: MyBatis 会创建 PooledDataSource 实例
type=”UNPOOLED”
: MyBatis 会创建 UnpooledDataSource 实例
type=”JNDI”
: MyBatis 会从 JNDI 服务上查找 并获取DataSource 实例
1.2 mybatis连接池及dataSource
1.2.1 mybatis连接池分类及关系
-
Mybatis 的数据源分为三类:
- UNPOOLED 不使用连接池的数据源
- POOLED 使用连接池的数据源
- JNDI 使用 JNDI 实现的数据源
-
Mybaits连接池关系
PooledDataSource通过UnPooledDataSource实例对象,实际上是一种缓存连接池机制
1.2.2 UnPooledDataSource连接池
获取连接方式有两种
- 通过用户名和密码获取连接(实际上是将用户名、密码封装到属性集,再调用第二种方法获取)
- 通过配置文件获取连接
1.2.3 PooledDataSource连接池(建议使用)
- PooledDataSource在创建连接时候,会自动调用UnPooledDataSource中创建连接的方法
1.2.4 连接获取时机
- 在实例数据库时不会创建连接,而是在真正进行数据库操作的时候再获取连接
2. Mybatis映射文件的动态sql
部分业务逻辑是动态变化的,需要使用动态sql满足需求。Mybatis框架中sql是写在dao接口映射文件中,Mybatis提供多种标签用以使用动态sql
2.1 <if>
标签
- 使用if标签进行条件判断
- 属性test:用于判断表达式,返回boolean类型。其值为条件表达式
2.1.1 映射文件
<!--
使用if条件语句 test属性用以判断,其属性值为条件判断语句
-->
<select id="findByConditon" parameterType="user" resultType="com.azure.entity.User">
select * from user where 1=1
<!--条件判断并拼接sql语句-->
<if test="id != 0">
and id=#{id}
</if>
<if test="username != null">
and username=#{username}
</if>
</select>
2.2 <where>
标签
- 使用where标签拼接where条件
<!--使用where标签拼接where条件-->
<select id="findByConditon" parameterType="user" resultType="com.azure.entity.User">
select * from user
<where>
<!--条件判断并拼接sql语句-->
<if test="id != 0">
and id=#{id}
</if>
<if test="username != null">
and username=#{username}
</if>
</where>
</select>
2.3 <foreach>
标签
-
使用foreach标签遍历参数
属性值:
- collection:遍历的集合(参数列表)
- open:sql语句最先拼接的部分,遍历开始
- close:sql语句最后拼接的部分,遍历结束
- separator:每次遍历时,sql语句拼接时以指定的分割符分割
- item:存储每次遍历的结果
- index:当前遍历元素的索引
标签体:给每个item赋值,可使用#{}赋值
案例需求:根据多个id查询用户返回用户集合
2.3.1 定义查询扩展对象
- 用于封装多个参数
- 对原有的QueryVo进行拓展优化
/**
* 通过继承User类,QueryVO类可以实现User所有属性查询以及扩展属性
*/
public class QueryVo extends User{
/*
使用一个List封装多个id值
*/
private List<Integer> ids;
public QueryVo(){
}
//getter&setter
public List<Integer> getIds() {
return ids;
}
public void setIds(List<Integer> ids) {
this.ids = ids;
}
}
2.3.2 dao层接口
List<User> findByCondition2(QueryVo queryVo);
2.3.3 dao映射文件
<!--使用foreach标签进行多id查询
使用代码格式select * from user where 1=1 and id in (xx,xx,...)
格式:select * from user where 1=1 and id=xx orid=xx,...不能使用foreach标签
-->
<select id="findByCondition2" resultType="user" parameterType="user">
select * from user
<where>
<if test="ids != null and ids.size() > 0">
<foreach collection="ids" open="id in (" separator="," close=")" item="id">
#{id}
</foreach>
</if>
</where>
</select>
2.3.4 测试类
@Test
public void find2() {
//封装条件
QueryVo queryVo = new QueryVo();
List<Integer> ids = new ArrayList<Integer>();
ids.add(47);
ids.add(48);
ids.add(49);
queryVo.setIds(ids);
//执行方法
List<User> list = userDao.findByCondition2(queryVo);
System.out.println(list);
}
2.4 Mybatis简化sql语句
-
sql标签:定义sql片段,可以将公用的sql抽取出来
- 属性值:id,为sql片段命名
-
include标签:用于引用sql片段
- 属性值:refid,与引用的sql标签的id对应
-
如果引用其它 mapper.xml 的 sql 片段,则在引用时需要加上 namespace,如下:
<include refid=*"*namespace.sql 片段”/>
<!--抽取公共sql语句-->
<sql id="selectUser">
select * from user
</sql>
<select id="findByCondition2" resultType="user" parameterType="queryVo">
<!--引用sql片段-->
<include refid="selectUser"></include>
<where>
<if test="ids != null and ids.size() > 0">
<foreach collection="ids" open=" id in (" separator="," close=")" item="id">
#{id}
</foreach>
</if>
</where>
</select>
- 注意事项:
- 何时进行抽取?如果相同sql代码出现2次以上,就要将相同代码抽取出来,便于后期维护
- 强烈建议将sql抽取出来!!!!!
3. Mybatis的多表关联查询
以用户、账户和角色表之间查询为例
准备工作:
-
账户表
DROP TABLE IF EXISTS account; CREATE TABLE account ( accountId INT(11) NOT NULL COMMENT '编号', UID INT(11) DEFAULT NULL COMMENT '用户编号', MONEY DOUBLE DEFAULT NULL COMMENT '金额', PRIMARY KEY (accountId), KEY FK_Reference_8 (UID), CONSTRAINT FK_Reference_8 FOREIGN KEY (UID) REFERENCES USER (id) ) ENGINE=INNODB DEFAULT CHARSET=utf8; INSERT INTO account(accountId,UID,MONEY) VALUES (1,46,1000),(2,45,1000),(3,46,2000);
-
角色表
CREATE TABLE role ( ID int(11) NOT NULL COMMENT '编号', ROLE_NAME varchar(30) default NULL COMMENT '角色名称', ROLE_DESC varchar(60) default NULL COMMENT '角色描述', PRIMARY KEY (ID) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; insert into role(ID,ROLE_NAME,ROLE_DESC) values (1,'院长','管理整个学院'),(2,'总裁','管理整个公司'),(3,'校长','管理整个学校');
3.1 一对一
需求:查询账户表,并把账户对应的用户信息显示出来
3.1.1 需求分析
账户与用户是一对一关系,一个账户只能对应一个用户
实现一对一关系步骤:
-
创建项目;
-
创建实体类(账户)
-
账户表属性
-
封装User数据
-
-
接口编写
-
配置映射文件,里面配置1to1的关系,让Mybatis自动封装多表数据
-
测试
3.1.2 创建实体类
public class Account implements Serializable {
private int accountId;
private int uid;
private double money;
//封装User数据,账户与用户是一对一关系
private User user;
public Account() {
}
/*省略有参、toString、getter&setter*/
}
3.1.3 创建dao接口
public interface IAccountDao {
//查询所有账户及对应的用户信息
List<Account> findAll();
}
3.1.4 配置dao接口映射文件
-
一对一关系中,返回值类型必须使用resultMap,用来封装多表查询的数据
-
使用
<association>
标签表示一对一关系的映射配置。属性值有:
- property:一对一关系的对应对象属性名(本例是指账户对象对应的user对象属性);
- JavaType:对应对象属性类型
- column:外键字段
<?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.azure.dao.IAccountDao">
<!--返回集的类型是Account类型,里面需封装account表数据和User对象-->
<resultMap id="accountResultMap" type="account">
<!--先建立account对象属性与account表字段的映射关系-->
<id property="accountId" column="accountId"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!--建立user对象与User表字段的映射关系-->
<!--
使用association标签标示一对一关系
- property:一对一关系的对应对象属性名(本例是指账户对象对应的user对象属性);
- JavaType:对应对象属性类型(本例即User类型)
- column:外键字段
-->
<association property="user" javaType="user" column="uid">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
</association>
</resultMap>
<!--使用resultMap明确一对一关系。
使用左外连接确保account表数据能全部显示
-->
<select id="findAll" resultMap="accountResultMap">
select * from account a left join user u on a.uid=u.id
</select>
</mapper>
3.1.5 测试类
public class AccountDaoTest {
private InputStream is;
private SqlSession session;
private IAccountDao accountDao;
/**
* 每次执行Junit前都会执行
* @throws IOException
*/
@Before
public void before() throws IOException {
is = Resources.getResourceAsStream("SqlMapConfig.xml");
session = new SqlSessionFactoryBuilder().build(is).openSession();
accountDao = session.getMapper(IAccountDao.class);
}
/**
* 每次执行Junit后都会执行,提交事务和关闭资源
* @throws IOException
*/
@After
public void close() throws IOException {
//手动提交事务
session.commit();
//关闭资源
session.close();
is.close();
}
@Test
public void find() throws IOException{
List<Account> accounts = accountDao.findAll();
System.out.println(accounts);
}
}
3.2 一对多
需求:查询用户,并将用户对应的账户显示
3.2.1 需求分析
- 用户与账户是一对多的关系,一个用户可以有多个账户
-
创建项目;
-
创建实体类(用户)
-
用户表属性
-
封装Account数据
-
-
接口编写
-
配置映射文件,里面配置1ton的关系,让Mybatis自动封装多表数据
-
测试
3.2.2 创建实体类
public class User implements Serializable{
private int id;
private String username;
private Date birthday;
private String sex;
private String address;
//封装account账户表数据,用一个list封装所有账户
private List<Account> accounts;
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
/*省略有参、toString、getter&setter*/
}
3.2.3 dao接口
public interface IUserDao {
/*查询所有用户*/
List<User> findAll();
}
3.2.4 接口映射文件
-
需要使用resultMap封装多表查询结果
-
使用
<collection>
标签表示一对一关系的映射配置。属性值有:
- property:指定集合在实体类中的属性名;(本例是User类中的
List<Account>
的属性名accounts) - ofType:指定集合元素的java类型
- column:外键
- property:指定集合在实体类中的属性名;(本例是User类中的
<!--返回集的类型是User类型,里面需封装user表数据和Account对象-->
<resultMap id="usersMap" type="user">
<!--先建立user对象属性与user表字段的映射关系-->
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
<!--建立user对象与account表字段的映射关系-->
<!--
使用<collection>标签表示一对一关系的映射配置。
属性值有:
- property:对应的集合名;
- ofType:指定集合元素的类型(本例指List<Account> 的Account类型)
-->
<collection property="accounts" ofType="account">
<id property="accountId" column="accountId"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
</collection>
</resultMap>
<select id="findAll" resultMap="usersMap">
select * from user u left join account a ON u.id = a.UID
</select>
3.2.5 测试类
public class UserDaoTest {
private InputStream is;
private SqlSession session;
private IUserDao userDao;
/**
* 每次执行Junit前都会执行
* @throws IOException
*/
@Before
public void before() throws IOException {
is = Resources.getResourceAsStream("SqlMapConfig.xml");
session = new SqlSessionFactoryBuilder().build(is).openSession();
userDao = session.getMapper(IUserDao.class);
}
/**
* 每次执行Junit后都会执行,提交事务和关闭资源
* @throws IOException
*/
@After
public void close() throws IOException {
//手动提交事务
session.commit();
//关闭资源
session.close();
is.close();
}
@Test
public void findAll() {
List<User> list = userDao.findAll();
System.out.println(list);
}
}
3.3 多对多
- 所谓的多对多关系,实际上是有多个一对多关系组合起来,所以处理多对多关系,要转换成一对多关系。
需求:查询所有用户,并显示对应的角色
3.3.1 需求分析
用户与角色是多对多的关系,一个用户有多种角色,一种角色也有多个用户,需要分别描述
3.3.1.1 用户对角色的一对多
- 创建项目;
- 创建实体类
- User类(User本身数据,List
<Role>
roles) - Role类(Role本身数据)
- User类(User本身数据,List
- 接口编写
- 配置映射文件,里面配置1ton的关系,让Mybatis自动封装多表数据
- 测试
3.3.1.2 角色对用户一对多
- 创建项目;
- 创建实体类
- User类(User本身数据,List
<Role>
roles) - Role类(Role本身数据,List
<User>
users)
- User类(User本身数据,List
- 接口编写
- 配置映射文件,里面配置1ton的关系,让Mybatis自动封装多表数据
- 测试
3.3.1.3 用户表与角色表分析
两个表之间并无主键连接,无法单纯用两个表去描述表之间的关系,故需要添加用户角色关系表用以描述关系。
DROP TABLE IF EXISTS user_role;
CREATE TABLE user_role (
UID int(11) NOT NULL COMMENT '用户编号',
RID int(11) NOT NULL COMMENT '角色编号',
PRIMARY KEY (UID,RID),
KEY FK_Reference_10 (RID),
CONSTRAINT FK_Reference_10 FOREIGN KEY (RID) REFERENCES role (ID),
CONSTRAINT FK_Reference_9 FOREIGN KEY (UID) REFERENCES user (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into user_role(UID,RID) values (41,1),(45,1),(41,2);
3.3.2 实体类
Role类
public class Role implements Serializable {
private int id;
private String roleName;
private String roleDesc;
//封装User用户表数据,使用List封装
private List<User2> user2s;
/*省略构造函数、toString、getter&setter
}
User类
这里新建一个User2,避免与上面的User类冲突
public class User2 implements Serializable {
private int id;
private String username;
private Date birthday;
private String sex;
private String address;
//封装role角色表数据,使用List封装
private List<Role> roles;
/*省略构造函数、toString、getter&setter*/
}
3.3.3 用户对角色一对多
3.3.3.1 dao层接口
/*
查询所有用户,Role有关
*/
List<User2> findAll2();
3.3.3.2 dao接口映射文件
注意:
外连接不能同时连接三个表,下面的写法是错误的:
SELECT u.*,r.* FROM USER u LEFT JOIN user_role ur LEFT JOIN role r on u.id=ur.uid and r.ID=ur.rid
正确写法是,先连接user和user_role表,然后将所得的表再与role表连接:
SELECT u.*,r.* FROM USER u LEFT JOIN user_role ur ON u.id=ur.uid LEFT JOIN role r ON r.ID=ur.rid
以下的代码有误,请看3.3.3.4优化后代码!!
<!--返回集的类型是User2类型,里面需封装user2表数据和Role对象-->
<resultMap id="user2sMap" type="user2">
<!--先建立user2对象属性与user表字段的映射关系-->
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
<!--建立user对象与role表字段的映射关系-->
<collection property="roles" ofType="role">
<id property="id" column="id"></id>
<result property="roleName" column="role_name"></result>
<result property="roleDesc" column="role_desc"></result>
</collection>
</resultMap>
<select id="findAll2" resultMap="user2sMap">
SELECT u.*,r.* FROM USER u LEFT JOIN user_role ur ON u.id=ur.uid LEFT JOIN role r ON r.ID=ur.rid
</select>
3.3.3.3 测试类
@Test
public void findAll2() {
List<User2> list2 = userDao.findAll2();
System.out.println(list2);
}
3.3.3.4 dao接口映射文件(重要)
虽然运行后并没有报错,但是仔细查看结果会发现,有多个角色的用户仅显示一个角色,其余角色都没有显示。
原因:user表的id值与role表的id值冲突!!!在resultMap中标签属性column是不会自动区分多表,所以上述配置中column重复封装了id值。
解决方法:给其中一个表的id值起别名
优化后代码
<!--返回集的类型是User2类型,里面需封装user2表数据和Role对象-->
<resultMap id="user2sMap" type="user2">
<!--先建立user2对象属性与user表字段的映射关系-->
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
<!--建立user对象与role表字段的映射关系-->
<collection property="roles" ofType="role">
<id property="id" column="roleId"></id><!--使用别名封装role的id值,由于Role类的id属性名就是id,所以property属性不用改-->
<result property="roleName" column="role_name"></result>
<result property="roleDesc" column="role_desc"></result>
</collection>
</resultMap>
<select id="findAll2" resultMap="user2sMap">
SELECT u.*,r.id roleId,r.ROLE_NAME,r.ROLE_DESC FROM USER u LEFT JOIN user_role ur ON u.id=ur.uid LEFT JOIN role r ON r.ID=ur.rid
</select><!--sql语句要修改,给role表的id值起别名-->
3.3.4 角色对用户的一对多
3.3.4.1 dao接口
public interface IRoleDao {
/*查询用户及角色*/
List<Role> findAll();
}
3.3.4.2 dao接口映射
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namesppace名称空间,用于定义是哪个类的映射文件,这里需要写映射接口的类全名-->
<mapper namespace="com.azure.dao.IRoleDao">
<resultMap id="rolesMap" type="role">
<id property="id" column="roleId"></id><!--使用别名封装role的id值,由于Role类的id属性名就是id,所以property属性不用改-->
<result property="roleName" column="role_name"></result>
<result property="roleDesc" column="role_desc"></result>
<collection property="user2s" ofType="user2">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
</collection>
</resultMap>
<select id="findAll" resultMap="rolesMap">
SELECT u.*,r.id roleId,r.ROLE_NAME,r.ROLE_DESC FROM role r LEFT JOIN user_role ur ON r.ID=ur.rid LEFT JOIN USER u ON u.id=ur.uid
</select>
</mapper>
3.3.4.3 测试类
public class RoleDaoTest {
/*
查询所有角色及其对应用户信息
*/
private InputStream is;
private SqlSession session;
private IRoleDao roleDao;
/**
* 每次执行Junit前都会执行
* @throws IOException
*/
@Before
public void before() throws IOException {
is = Resources.getResourceAsStream("SqlMapConfig.xml");
session = new SqlSessionFactoryBuilder().build(is).openSession();
roleDao = session.getMapper(IRoleDao.class);
}
/**
* 每次执行Junit后都会执行,提交事务和关闭资源
* @throws IOException
*/
@After
public void close() throws IOException {
//手动提交事务
session.commit();
//关闭资源
session.close();
is.close();
}
@Test
public void findAll() {
List<Role> list = roleDao.findAll();
System.out.println(list);
}
}