-
了解mybatis连接池技术
-
掌握mybatis事务操作
-
掌握mybatis动态sql
-
掌握mybatis多表关联查询
mybatis连接池技术【了解】
总结jdbc开发问题中,我们说mybatis内部提供了连接池。在sqlMapConfig.xml中配置数据源:
POOLED即是mybatis内部提供的连接池。
连接池分类
mybatis中将连接池(数据源)分为三类:
-
jndi:使用jndi实现数据源
-
pooled:使用连接池的数据源
-
unpooled:不使用连接池的数据源
三类数据源中我们通常使用的是pooled。mybatis框架提供了实现javax.sql.DataSource接口的实现类:PooledDataSource、UnpooledDataSource来表示pooled和unpooled类型的数据源。具体结构如下:
在这三种数据源中,我们一般采用的是 POOLED 数据源(很多时候我们所说的数据源就是为了更好的管理数据库连接,也就是我们所说的连接池技术)。
mybatis动态sql
动态sql指的是运行时sql,根据传入的参数情况,决定最终执行的sql语句。在动态sql中,主要关心以下标签的使用:
-
if
-
where
-
set
-
sql
-
include
-
foreach
动态sql案例
需求
根据用户名称和性别查询用户数据。
声明mapper接口方法
package com.dao;
import com.po.QueryPo;
import com.po.User;
import java.util.ArrayList;
import java.util.List;
public interface UserDao {
//使用if标签
List<User> findByUser1(User user);
//使用where标签
List<User> findByUser2(User user);
//使用foreach标签1
List<User> findByIds(QueryPo queryPo);
//使用foreach标签2
int deleteUsers(ArrayList<Integer> ids);
//使用set标签
int update(User user);
}
if标签(不建议单独使用)
作用:判断传入的参数情况,拼装sql语句片段。
单独使用if标签时,因为sql语句约束条件不能以and开头,所以我们使用了where 1=1的多余语句。也因此,不建议单独使用if标签。
<select id="findByUser1" parameterType="com.po.User" resultType="com.po.User">
select * from user where 1=1
<!--if:判断用户名称不为null,且不为空字符串,则作为查询条件-->
<if test="username != '' and username != null">
and username like #{username}
</if>
<!--if:判断用户性别不为null,且不为空字符串,则作为查询条件-->
<if test="sex != '' and sex !=null">
and sex = #{sex}
</if>
</select>
where标签
作用:
-
相当于sql语句中的where关键字
-
根据传入的参数情况,智能的去掉多余的and、or和where关键字
<select id="findByUser2" parameterType="com.po.User" resultType="com.po.User">
select * from user
<where>
<if test="username != '' and username != null">
username like #{username}
</if>
<if test="sex != '' and sex != null">
sex like #{sex}
</if>
</where>
</select>
set标签
作用:
-
相当于sql语句中的set关键字
-
根据传入的参数情况,智能的去掉最后一个多余的逗号
<!-- 动态修改用户数据 -->
<update id="update" parameterType="com.po.User">
update user
<set>
<if test="username != null and username !=''">
username=#{username},
</if>
<if test="sex != null and sex !=''">
sex=#{sex},
</if>
</set>
<where>
id=#{id}
</where>
</update>
sql、include标签
作用:
-
提取公共的sql语句片段
-
include标签用于引用sql标签
<!--sql:提取公共的sql语句片段,说明:
id:唯一标识名称,通过id引用该sql片段
-->
<sql id="selectSql">
select * from user
</sql>
<select id="findByUser2" parameterType="com.po.User" resultType="com.po.User">
<!--使用提取出来的sql语句-->
<include refid="selectSql"/>
<where>
<if test="username != '' and username != null">
username like #{username}
</if>
<if test="sex !=‘’ and sex != null">
sex like #{sex}
</if>
</where>
</select>
foreach标签
作用:循环处理参数集合(list、数组)。
使用方法一:
查找多个id下对应的User
声明mapper接口方法
package com.po;
import java.util.ArrayList;
public class QueryPo {
private ArrayList<Integer> ids;
public QueryPo() {
}
public QueryPo(ArrayList<Integer> ids) {
this.ids = ids;
}
public ArrayList<Integer> getIds() {
return ids;
}
public void setIds(ArrayList<Integer> ids) {
this.ids = ids;
}
@Override
public String toString() {
return "QueryPo{" +
"ids=" + ids +
'}';
}
}
配置mapper映射文件
<!--查找多个id下对应的User-->
<select id="findByIds" parameterType="com.po.QueryPo" resultType="com.po.User">
<!-- select * from user-->
<include refid="selectSql"/>
<where>
<if test="ids != null and ids.size() > 0 ">
<!-- foreach:循环处理参数集合
collection:参数集合,来自parameterType的属性名
item:当前遍历的对象
separator:指定分割符
-->
<foreach collection="ids" open=" id in (" close=" )" separator="," item="id">
#{id}
</foreach>
</if>
</where>
</select>
使用方法2
实现批量删除用户
声明mapper接口方法
//使用foreach标签2
int deleteUsers(ArrayList<Integer> ids);
配置mapper映射文件
<!-- 批量删除用户,说明:
parameterType:当参数传递的是list和数组,都建议写成list
-->
<delete id="deleteUsers" parameterType="list">
delete from `user`
where
<!--foreach:循环处理参数集合,说明:
collection:参数集合,来自parameterType
open:拼装的sql语句片段的开始部分
close:拼装的sql语句片段的结束部分
item:当前遍历的元素
-->
<foreach collection="list" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</delete>
mybatis多表关联查询
关联关系分类
一对一关联关系
举一个例子:人和身份证的关系。一个人只能有一张身份证,一张身份证只能属于一个人。【双向】一对一关联关系。
一对多关联关系
举一个例子:人和账户的关系。一个人可以有多个账户,从人到账户是【一对多】关联关系。一个账户只能属于一个人,从账户到人是【一对一】的关联关系。
多对多关联关系–双向一对多就是多对多
举一个例子:业务系统中,用户和角色的关系。在实际项目中,多对多关系通过中间表,看成两个一对多关联关系。
分析用户账户数据模型
一对一关联查询
需求
查询全部账户数据,并且关联查询账户所属的用户数据。
需求实现
新建一张account表
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);
实现方式一:resultType(了解)
分析sql语句中,包含了账户的数据和用户数据。我们增加一个实体类对象,其中包含有账户的属性和用户的属性,来完成结果集的映射。
实现方式二:resultMap
通过在实体类(账户和用户)之间,建立关联关系,则可以通过resultMap进行配置一对一关联关系。
建立Acount2User类,建立账户到用户的一对一关联关系
声明mapper接口方法
package com.dao;
import com.po.Acount2User;
import java.util.List;
public interface Acount2UserDao {
List<Acount2User> findO2O();
}
配置mapper映射文件
<?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.dao.Acount2UserDao">
<!-- 配置账户到用户的一对一关联关系,说明:
type:要映射的类型
id:唯一标识名称,通过id引用该resultMap
-->
<resultMap id="o2omap" type="com.po.Acount2User">
<!-- 配置账户主键字段对应关系 -->
<id column="accountId" property="accountid"/>
<!-- 配置账户的普通字段对应关系 -->
<result column="UID" property="UID"/>
<result column="MONEY" property="money"/>
<!--建立user用户对象与查询的用户字段的映射关系-->
<!--
association 表示一对一关系映射配置
property 对用账户对象中的user对象属性
javaType 用户对象的类型(必须要指定)
column 外键字段
一对一关系中账户表关联用户表的外键字段
-->
<association property="user" javaType="com.po.User" column="UID">
<!-- 配置用户的主键字段对应关系 -->
<id column="id" property="id"/>
<!-- 配置用户的普通字段对应关系 -->
<result column="username" property="username"/>
<result column="birthday" property="birthday"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
</association>
</resultMap>
<select id="findO2O" resultMap="o2omap">
SELECT * FROM user u inner join account a WHERE u.id = a.UID;
</select>
</mapper>
测试
@Test
public void testO2O() throws Exception {
InputStream resourceAsStream = App.class.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
Acount2UserDao acount2UserDao = sqlSession.getMapper(Acount2UserDao .class);
List<Acount2User> acount2Users = acount2UserDao.findO2O();
for (Acount2User acount2User : acount2Users) {
System.out.println(acount2User);
}
sqlSession.close();
resourceAsStream.close();
}
一对多关联查询
需求
查询全部用户数据,并且关联查询出用户的所有账户数据。
修改User2AccountDao对象,建立用户到账户的一对多关联关系
声明mapper接口方法
package com.dao;
import com.po.User2Account;
import java.util.List;
public interface User2AccountDao {
List<User2Account> findO2M();
}
配置mapper映射文件
<?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.dao.User2AccountDao">
<!-- 配置用户到账户的一对多关联关系,说明:
type:要映射的类型
id:唯一标识名称,通过id引用该resultMap
-->
<resultMap id="O2Mmap" type="com.po.User2Account">
<!-- 配置用户的主键字段对应关系 -->
<id column="id" property="id"/>
<!-- 配置用户的普通字段对应关系 -->
<result column="username" property="username"/>
<result column="birthday" property="birthday"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
<!-- collection:配置一对多关联关系,说明:
property:要映射的属性名称
javaType:要映射的属性类型(可以指定,可以不指定,建议都指定)
ofType:指定集合中存放的类型(必须要指定)
-->
<collection property="accounts" ofType="com.po.Account" column="UID">
<!-- 配置账户的主键对应关系 -->
<id column="accountId" property="accountid"/>
<!-- 配置账户普通字段对应关系 -->
<result column="UID" property="uid"/>
<result column="MONEY" property="money"/>
</collection>
</resultMap>
<select id="findO2M" resultMap="O2Mmap">
select * from user u inner join account a on u.id = a.UID
</select>
</mapper>
测试
@Test
public void testO2M() throws Exception {
InputStream resourceAsStream = App.class.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User2AccountDao user2AccountDao = sqlSession.getMapper(User2AccountDao.class);
List<User2Account> user2Accounts = user2AccountDao.findO2M();
for (User2Account user2Account : user2Accounts) {
System.out.println(user2Account);
}
sqlSession.close();
resourceAsStream.close();
}
多对多关联查询(双向的一对多)
多对多关联关系,可以通过中间表看成两个双向的一对多关联关系。
用户与角色多对多关系模型
准备环境
创建角色表和中间表
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,'校长','管理整个学校');
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);