mybatis第三天该文档是:mybatis框架学习...
日期:2021-01-04
1. mybatis连接池
1.1 连接池介绍
1.2 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连接池。
POOLED 原理:
2. Mybatis的事务控制
2.1 JDBC中事务的回顾
在JDBC中我们可以通过手动方式将事务的提交改为手动方式,通过setAutoCommit()方法就可以调整。 通过JDK文档,我们找到该方法如下:
那么我们的Mybatis框架因为是对JDBC的封装,所以Mybatis框架的事务控制方式,本身也是用JDBC的setAutoCommit()方法来设置事务提交方式的。
2.2 Mybatis中事务提交方式
Mybatis中事务的提交方式,本质上就是调用JDBC的setAutoCommit()来实现事务控制。 我们运行之前所写的代码:public class MyBatisTest {
private SqlSession sqlSession;
private InputStream in=null;
//用于返回一个UserDao的代理对象
public UserDao chuShi() throws IOException {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.使用工厂生产SqlSession对象
sqlSession = factory.openSession();
//4.使用SqlSession创建Dao接口的代理对象
UserDao userDao = sqlSession.getMapper(UserDao.class);
return userDao;
}
//用于关闭连接的方法
public void destroy() throws IOException {
sqlSession.close();
in.close();
}
//测试保存用户操作
@Test
public void Test010() throws IOException {
//直接调用方法得到代理对象
UserDao userDao = chuShi();
User user = new User("曹新千",new Date(),"男","河北");
userDao.saveUser(user);
//提交事务
sqlSession.commit();
//关闭连接
destroy();
}
}
观察在它在控制台输出的结果:
这是我们的Connection的整个变化过程,通过分析我们能够发现之前的CUD操作过程中,我们都要手动进行事务的提交,原因是setAutoCommit()方法,在执行时它的值被设置为false了,所以我们在CUD操作中,必须通过sqlSession.commit()方法来执行提交操作。
2.3 Mybatis自动提交事务的设置
通过上面的研究和分析,现在我们一起思考,为什么CUD过程中必须使用sqlSession.commit()提交事务?主要原因就是在连接池中取出的连接,都会将调用connection.setAutoCommit(false)方法,这样我们就必须使用sqlSession.commit()方法,相当于使用了JDBC中的connection.commit()方法实现事务提交。
明白这一点后,我们现在一起尝试不进行手动提交,一样实现CUD操作。
所对应的DefaultSqlSessionFactory类的源代码:
运行的结果如下:
我们发现,此时事务就设置为自动提交了,同样可以实现CUD操作时记录的保存。虽然这也是一种方式,但就编程而言,设置为自动提交方式为false再根据情况决定是否进行提交,这种方式更常用。因为我们可以根据业务情况来决定提交是否进行提交。
3. 动态sql语句
3.1 标签
我们根据实体类的不同取值,使用不同的SQL语句来进行查询。比如在id如果不为空时可以根据id查询,如果username不同空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到。
(1)UserDao接口增加方法/**
* 根据用户信息,查询用户列表
* @param user
* @return
*/
List findByUser(User user);
(2)UserDao.xml配置文件增加
注意:标签的test属性中写的是对象的属性名,如果是包装类的对象要使用OGNL表达式的写法。
SELECT * FROM user WHERE 1=1
AND username = #{username}
AND id = #{id}
(3)测试程序
这样的话只要id有值就根据id查,只要name有值就根据name查,两个都有就根据两个一起查//测试根据id或者名字
@Test
public void Test07() throws IOException {
UserDao userDao = chuShi();
//设置User只有名字
User user1=new User();
user1.setUsername("老王");
List users1 = userDao.findByUser(user1);
//设置User只有id
User user2=new User();
user2.setId(48);
List users2 = userDao.findByUser(user2);
System.out.println(users1);
System.out.println(users2);
destroy();
}
3.2 标签
为了简化上面where 1=1的条件拼装,我们可以采用标签来简化开发。
(1)UserDao.xml配置文件增加
SELECT * FROM user
AND username = #{username}
AND id = #{id}
3.3 标签
传入多个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)
这样我们在进行范围查询时,就要将一个集合中的值,作为参数动态添加进来。 这样我们将如何进行参数的传递?
(1)QueryVo实体类public class QueryVo {
private User user;
private List ids;
//get set方法
}
(2)UserDao接口增加方法/**
* 根据QueryVo中提供的id集合,查询用户信息
* @param queryVo
* @return
*/
List findUserInIds(QueryVo queryVo);
(3)UserDao.xml配置文件增加
SQL语句:select 字段 from user where id in (?)
标签用于遍历集合,它的属性:
collection:代表要遍历的集合元素,注意编写时不要写#{}
open:代表语句的开始部分
close:代表结束部分
item:代表遍历集合的每个元素,生成的变量名(会放到open和close的中间 每个数字用,隔开,类似于 id in (1,2,3)
sperator:代表分隔符
SELECT * FROM USER
#{id}
(4)测试程序//测试根据Query中的集合来进行查询
@Test
public void Test08() throws IOException {
UserDao userDao = chuShi();
//往QueryVo对象里边设置数组集合
QueryVo vo=new QueryVo();
List ids=new ArrayList<>();
ids.add(46);
ids.add(47);
ids.add(48);
vo.setIds(ids);
//根据集合内容查询
List users = userDao.findUserInIds(vo);
System.out.println(users);
destroy();
}
3.4 标签
Sql中可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的。
(1)UserDao.xml配置文件增加
SELECT * FROM user
#{id}
4. Mybatis多表查询
4.1 一对一和一对多查询
4.1.1 分析
4.1.2 数据库表
用户表:
账户表:
4.1.3 准备工作
(1)编写Account实体类:public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
//get set方法
}
(2)编写AccountDao接口:public interface AccountDao {
/**
* 查询所有账户
* @return
*/
List findAll();
}
(3)编写AccountDao.xml配置文件:<?xml version="1.0" encoding="UTF-8"?>
/p>
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
SELECT * FROM account
(4)编写测试:public class AccountTest {
private SqlSession sqlSession;
private InputStream in=null;
//用于返回一个AccountDao的代理对象
public AccountDao chuShi() throws IOException {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.使用工厂生产SqlSession对象
sqlSession = factory.openSession();
//4.使用SqlSession创建Dao接口的代理对象
AccountDao accountDao = sqlSession.getMapper(AccountDao.class);
return accountDao;
}
//用于关闭连接的方法
public void destroy() throws IOException {
sqlSession.close();
in.close();
}
/**
* 测试查询所有
*/
@Test
public void Test01() throws IOException {
AccountDao accountDao = chuShi();
List accounts = accountDao.findAll();
for (Account account : accounts) {
System.out.println(account);
}
}
}
执行结果:
4.1.4 实现一对一 - 方法①(用的不多)
通过写acoount子类的方式完成一对一。
(1)分析SQL语句该怎么做:
执行该语句可以得到想要的结果,根据结果编写一个实体类可以让数据封装进去。SELECT a.*,u.username,u.address FROM user u,account a WHERE u.id=a.uid;
执行结果:
(2)编写Account子类AccountUser:
从父类那继承了id,uid,money。所以只需要再加两个属性,就能把sql语句得到的结果封装进去。public class AccountUser extends Account{
private String username;
private String address;
@Override
public String toString() {
//这里先执行父类的toString方法
return super.toString()+" AccountUser{" +
"username='" + username + '\'' +
", address='" + address + '\'' +
'}';
}
//getset方法
}
(3)编写AccountDao接口:/**
* 查询所有账户,同时获取账户的所属用户名称以及它的地址信息
* @return
*/
List findAllAccountUser();
(4)编写AccountDao.xml配置文件:
SELECT a.*,u.username,u.address FROM user u,account a WHERE u.id=a.uid
(5)编写测试:/**
* 查询所有账户,同时获取账户的所属用户名称以及它的地址信息
*/
@Test
public void Test02() throws IOException {
AccountDao accountDao = chuShi();
List accounts = accountDao.findAllAccountUser();
for (AccountUser account : accounts) {
System.out.println(account);
}
}
执行结果:
4.1.5 实现一对一 - 方法②(常用)
建立实体类关系的方式。
(1)编写Account实体类:
在Account类中加入User类的对象作为Account类的一个属性。public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
private User user;
//get set方法
}
(2)编写AccountDao接口:/**
* 查询所有账户,同时获取账户的所属用户名称以及它的地址信息
* @return
*/
List findAll();
(3)编写AccountDao.xml配置文件:
SELECT a.*,u.username,u.address FROM user u,account a WHERE u.id=a.uid
(4)编写测试:/**
* 测试查询所有账户,同时获取账户的所属用户名称以及它的地址信息
*/
@Test
public void Test01() throws IOException {
AccountDao accountDao = chuShi();
List accounts = accountDao.findAll();
for (Account account : accounts) {
System.out.println("每一个账户:");
System.out.println(account);
System.out.println(account.getUser());
}
}
执行结果:
4.1.6 实现一对多
给数据库account表增加一个账户,uid和第一个相同:
需求和分析:
(1)分析SQL语句该怎么做:
执行该语句可以得到想要的结果,根据结果编写User类增加一个属性存放Account,这里使用List因为一个人可能有多个账户。SELECT u.*,a.id as aid,a.uid,a.money FROM user u LEFT JOIN account a ON u.id=a.uid;
执行结果:
(2)编写User类:public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//一对多映射关系:主表实体应该包含从表的集合引用
//这里用List是因为,一个用户可能有多个账户
private List accounts;
//get set方法
}
(3)编写UserDao接口:/**
* 查询所有用户,同时获取到用户下所有账户的信息
* @return
*/
List findAll();
(4)编写UserDao.xml配置文件:
SELECT u.*,a.id as aid,a.uid,a.money FROM user u LEFT JOIN account a ON u.id=a.uid
(5)编写测试:/**
* 查询所有用户包含账户信息
*/
@Test
public void Test01() throws IOException {
UserDao userDao = chuShi();
List users = userDao.findAll();
for (User user : users) {
System.out.println("-------每个用户的信息-------");
System.out.println(user);
System.out.println(user.getAccounts());
}
}
执行结果:
根据结果发现mybatis可以自动识别如果一个用户有多个账户的情况,并且会封装到集合中。
4.2 多对多查询
4.2.1 分析
通过前面的学习,我们使用Mybatis 实现一对多关系的维护。多对多关系其实我们看成是双向的一对多关系。
4.2.2 数据库表
用户表:
角色表:
用户角色中间表:
4.2.3 准备工作
(前情提要)这是User类:public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
}
(1)编写Role实体类:public class Role {
private Integer roleid;
private String roleName;
private String roleDesc;
//get set方法
}
(2)编写RoleDao接口:public interface RoleDao {
/**
* 查询所有角色
* @return
*/
List findAll();
}
(3)编写RoleDao.xml配置文件:
SELECT * FROM role
(4)编写测试:public class RoleTest {
private SqlSession sqlSession;
private InputStream in=null;
//用于返回一个RoleDao的代理对象
public RoleDao chuShi() throws IOException {
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.使用工厂生产SqlSession对象
sqlSession = factory.openSession();
//4.使用SqlSession创建Dao接口的代理对象
RoleDao roleDao = sqlSession.getMapper(RoleDao.class);
return roleDao;
}
//用于关闭连接的方法
public void destroy() throws IOException {
sqlSession.close();
in.close();
}
/**
* 查询所有角色
*/
@Test
public void Test01() throws IOException {
RoleDao roleDao = chuShi();
List roles = roleDao.findAll();
for (Role role : roles) {
System.out.println("-------每个角色的信息-------");
System.out.println(role);
}
}
}
执行结果:
4.2.4 实现查询角色获取角色下的用户信息
查询角色的同时获取到用户的信息。
(1)分析SQL语句该怎么做:
执行该语句可以得到想要的结果,根据结果在Role实体类中可以添加一个User类的集合。SELECT r.id as rid,r.role_name,r.role_desc,u.* FROM role r
LEFT JOIN user_role ur ON r.id = ur.rid
LEFT JOIN user u ON u.id=ur.uid;
执行结果:
(2)编写Role类:public class Role {
private Integer roleid;
private String roleName;
private String roleDesc;
//多对多的关系映射:一个角色可以赋予多个用户
private List users;
//get set方法
}
(3)编写RoleDao接口:public interface RoleDao {
/**
* 查询所有角色
* @return
*/
List findAll();
}
(4)编写RoleDao.xml配置文件:
SELECT r.id as rid,r.role_name,r.role_desc,u.* FROM role r
LEFT JOIN user_role ur ON r.id = ur.rid
LEFT JOIN user u ON u.id=ur.uid;
(5)编写测试:/**
* 查询所有角色
*/
@Test
public void Test01() throws IOException {
RoleDao roleDao = chuShi();
List roles = roleDao.findAll();
for (Role role : roles) {
System.out.println("-------每个角色和对应的用户信息-------");
System.out.println(role);
System.out.println(role.getUsers());
}
}
执行结果:
4.2.5 实现查询用户获取用户下的角色信息
(1)分析SQL语句该怎么做:SELECT u.*,r.id as rid,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;
执行结果:
(2)编写User类:public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//多对多的关系映射:一个用户可以具备多个角色
private List roles;
//get set方法
}
(3)编写UserDao接口:/**
* 查询所有用户,同时获取到用户下所有账户的信息
* @return
*/
List findAll();
(4)编写UserDao.xml配置文件:
SELECT u.*,r.id as rid,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;
(5)编写测试:/**
* 查询所有用户包含账户信息
*/
@Test
public void Test01() throws IOException {
UserDao userDao = chuShi();
List users = userDao.findAll();
for (User user : users) {
System.out.println("-------每个用户和对应的角色信息-------");
System.out.println(user);
System.out.println(user.getRoles());
}
}
执行结果: