在 Mybatis 的 SqlMapConfig.xml 配置文件中,通过来实现 Mybatis 中连接池的配置。
在 Mybatis 中我们将它的数据源 dataSource 分为以下几类:
可以看出 Mybatis 将它自己的数据源分为三类:
UNPOOLED 不使用连接池的数据源
POOLED 使用连接池的数据源
JNDI 使用 JNDI 实现的数据源
具体结构如下:
相应地,MyBatis 内部分别定义了实现了 java.sql.DataSource 接口的 UnpooledDataSource,PooledDataSource 类来表示 UNPOOLED、POOLED 类型的数据源。
在这三种数据源中,我们一般采用的是 POOLED 数据源(很多时候我们所说的数据源就是为了更好的管理数据库连接,也就是我们所说的连接池技术)。
我们的数据源配置就是在 SqlMapConfig.xml 文件中,具体配置如下:
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
dataSource>
MyBatis 在初始化时,根据
MyBatis 是 通 过 工 厂 模 式 来 创 建 数 据 源 DataSource 对 象 的 , MyBatis 定 义 了 抽 象 的 工 厂 接口:org.apache.ibatis.datasource.DataSourceFactory,通过其 getDataSource()方法返回数据源DataSource。
下面是 DataSourceFactory 源码,具体如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.apache.ibatis.datasource;
import java.util.Properties;
import javax.sql.DataSource;
public interface DataSourceFactory {
void setProperties(Properties var1);
DataSource getDataSource();
}
MyBatis 创建了 DataSource 实例后,会将其放到 Configuration 对象内的 Environment 对象中, 供以后使用。
Mybatis 中事务的提交方式,本质上就是调用 JDBC 的 setAutoCommit()来实现事务控制。
@Before // 用于在测试方法执行之前执行 public void init() throws Exception { // 1. 读取配置文件,生成字节输入流 in = Resources.getResourceAsStream("SqlMapConfig.xml"); // 2. 获取SqlSessionFactory SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); // 3. 获取SqlSession对象 sqlSession = factory.openSession(); // 4. 获取dao的代理对象 userDao = sqlSession.getMapper(UserDao.class); } @Test public void testFindAll(){ // 5. 执行查询所有方法 List<User> users = userDao.findAll(); for (User user : users) { System.out.println(user); } } @After // 用于在测试方法执行之后执行 public void destroy() throws Exception { // 提交事务 sqlSession.commit(); // 6. 释放资源 sqlSession.close(); in.close(); }
在连接池中取出的连接,都会将调用 connection.setAutoCommit(false)方法,这样我们就必须使用 sqlSession.commit()方法,相当于使用了 JDBC 中的 connection.commit()方法实现事务提交。
@Before // 用于在测试方法执行之前执行
public void init() throws Exception {
// 1. 读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2. 获取SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 3. 获取SqlSession对象
// 在此次给openSession()方法添加参数true
sqlSession = factory.openSession(true);
// 4. 获取dao的代理对象
userDao = sqlSession.getMapper(UserDao.class);
}
@After // 用于在测试方法执行之后执行
public void destroy() throws Exception {
// 此时不需要再提交事务
// sqlSession.commit();
// 6. 释放资源
sqlSession.close();
in.close();
}
/**
* 根据传入参数条件查询
* @param user 查询的条件:有可能有用户名,有可能有性别,也有可能有地址,还有可能是都有或都没有
* @return
*/
List<User> findUserByCondition(User user);
<select id="findUserByCondition" resultMap="userMap" parameterType="User">
select * from user where 1=1
<if test="userName != null">
and username = #{userName}
if>
<if test="userSex != null">
and sex = #{userSex}
if>
select>
/**
* 测试查询所有
*/
@Test
public void testFindByCondition() {
User u = new User();
u.setUserName("老王");
u.setUserSex("女");
// 5. 执行查询所有方法
List<User> users = userDao.findUserByCondition(u);
for (User user : users) {
System.out.println(user);
}
}
<select id="findUserByCondition" resultMap="userMap" parameterType="User">
<include refid="defaultUser">include>
<where>
<if test="userName != null">
and username = #{userName}
if>
<if test="userSex != null">
and sex = #{userSex}
if>
where>
select>
传入多个 id 查询用户信息,用下边两个 sql 实现:
SELECT * FROM USERS WHERE username LIKE ‘%王%’ AND (id =41 OR id =42 OR id=45)
SELECT * FROM USERS WHERE username LIKE ‘%王%’ AND id IN (41,42,45)
这样我们在进行范围查询时,就要将一个集合中的值,作为参数动态添加进来。
在 QueryVo 中加入一个 List 集合用于封装参数
/**
* @author boy
* @create 2020-07-23 9:42
*/
public class QueryVo {
private User user;
private List<Integer> ids;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public List<Integer> getIds() {
return ids;
}
public void setIds(List<Integer> ids) {
this.ids = ids;
}
}
/**
* 根据QueryVo中提供的id集合,查询用户信息
* @param vo
* @return
*/
List<User> findUserInIds(QueryVo vo);
<select id="findUserInIds" resultMap="userMap" parameterType="QueryVo">
<include refid="defaultUser">include>
<where>
<if test="ids != null and ids.size() > 0">
<foreach collection="ids" open="and id in (" close=")" item="uid" separator=",">
#{uid}
foreach>
if>
where>
select>
/**
* 测试foreach标签的使用
*/
@Test
public void testFindInIds() {
QueryVo vo = new QueryVo();
List<Integer> list = new ArrayList<>();
list.add(41);
list.add(42);
list.add(43);
list.add(46);
list.add(80);
vo.setIds(list);
// 5. 执行查询所有方法
List<User> users = userDao.findUserInIds(vo);
for (User user : users) {
System.out.println(user);
}
}
Sql 中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的。
<sql id="defaultUser">
select * from user
sql>
<select id="findAll" resultMap="userMap">
<include refid="defaultUser">include>
select>
<select id="findById" parameterType="int" resultMap="userMap">
select * from user where id = #{uid}
select>
/**
* @author boy
* @create 2020-07-23 20:59
*/
public class Account implements Serializable {
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 +
'}';
}
}
实现查询账户信息时,也要查询账户所对应的用户信息。
Select a.*, u.username, u.address from account a, user u where a.uid = u.id
为了能够封装上面 SQL 语句的查询结果,定义 AccountCustomer 类中要包含账户信息同时还要包含用户信息,所以我们要在定义 AccountUser 类时可以继承 User 类。
/**
* @author boy
* @create 2020-07-23 21:07
*/
public class AccountUser 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() + " AccountUser{" +
"username='" + username + '\'' +
", address='" + address + '\'' +
'}';
}
}
/**
* @author boy
* @create 2020-07-23 21:00
*/
public interface AccountDao {
/**
* 查询所有账户,并且带有用户名称和地址信息
* @return
*/
List<AccountUser> findAll();
}
<mapper namespace="com.itheima.dao.AccountDao">
<select id="findAll" resultType="AccountUser">
select u.*, a.id as aid, a.uid, a.money from account a, user u where u.id = a.uid;
select>
mapper>
注意:因为上面查询的结果中包含了账户信息同时还包含了用户信息,所以我们的返回值类型 returnType的值设置为 AccountUser 类型,这样就可以接收账户信息和用户信息了。
/**
* @author boy
* @create 2020-07-23 21:02
*/
public class AccountTest {
private InputStream in;
private SqlSession sqlSession;
private AccountDao accountDao;
@Before // 用于在测试方法执行之前执行
public void init() throws Exception {
// 1. 读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2. 获取SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 3. 获取SqlSession对象
sqlSession = factory.openSession();
// 4. 获取dao的代理对象
accountDao = sqlSession.getMapper(AccountDao.class);
}
@After // 用于在测试方法执行之后执行
public void destroy() throws Exception {
// 提交事务
sqlSession.commit();
// 6. 释放资源
sqlSession.close();
in.close();
}
/**
* 测试查询所有
*/
@Test
public void testFindAll(){
// 5. 执行查询所有方法
List<Account> accounts = accountDao.findAll();
for (Account a : accounts) {
System.out.println("----------每个account的信息-----------");
System.out.println(a);
System.out.println(a.getUser());
}
}
}
/**
* @author boy
* @create 2020-07-23 20:59
*/
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
// 从表实体应该包含一个主表实体的对象引用
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.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;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", uid=" + uid +
", money=" + money +
'}';
}
}
/**
* @author boy
* @create 2020-07-23 21:00
*/
public interface AccountDao {
/**
* 查询所有账户,同时还要获取到当前账户的所属用户信息
* @return
*/
List<Account> findAll();
}
注意:第二种方式,将返回值改 为了 Account 类型。
因为 Account 类中包含了一个 User 类的对象,它可以封装账户所对应的用户信息。
<mapper namespace="com.itheima.dao.AccountDao">
<resultMap id="accountUserMap" type="Account">
<id property="id" column="aid">id>
<result property="uid" column="uid">result>
<result property="money" column="money">result>
<association property="user" column="uid">
<id property="id" column="id">id>
<result property="username" column="username">result>
<result property="address" column="address">result>
<result property="sex" column="sex">result>
<result property="birthday" column="birthday">result>
association>
resultMap>
<select id="findAll" resultMap="accountUserMap">
select u.*, a.id as aid, a.uid, a.money from account a, user u where u.id = a.uid;
select>
mapper>
/**
* @author boy
* @create 2020-07-23 21:02
*/
public class AccountTest {
private InputStream in;
private SqlSession sqlSession;
private AccountDao accountDao;
@Before // 用于在测试方法执行之前执行
public void init() throws Exception {
// 1. 读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2. 获取SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 3. 获取SqlSession对象
sqlSession = factory.openSession();
// 4. 获取dao的代理对象
accountDao = sqlSession.getMapper(AccountDao.class);
}
@After // 用于在测试方法执行之后执行
public void destroy() throws Exception {
// 提交事务
sqlSession.commit();
// 6. 释放资源
sqlSession.close();
in.close();
}
/**
* 测试查询所有
*/
@Test
public void testFindAll(){
// 5. 执行查询所有方法
List<Account> accounts = accountDao.findAll();
for (Account a : accounts) {
System.out.println("----------每个account的信息-----------");
System.out.println(a);
System.out.println(a.getUser());
}
}
}
SELECT u.*, a.id as aid, a.uid, a.money FROM user u LEFT JOIN account a ON u.id = a.uid
/**
* @author boy
* @create 2020-07-22 19:30
*/
public class User implements Serializable {
private Integer id;
private String username;
private String address;
private String sex;
private Date birthday;
// 一对多关系映射,主表实体应该包含从表实体的集合引用
private List<Account> accounts;
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = 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 String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", address='" + address + '\'' +
", sex='" + sex + '\'' +
", birthday=" + birthday +
'}';
}
}
/**
* 查询所有用户,同时获取到用户下所有账户的信息
* @return
*/
List<User> findAll();
<mapper namespace="com.itheima.dao.UserDao">
<resultMap id="userAccountMap" type="User">
<id property="id" column="id">id>
<result property="username" column="username">result>
<result property="address" column="address">result>
<result property="sex" column="sex">result>
<result property="birthday" column="birthday">result>
<collection property="accounts" ofType="Account">
<id property="id" column="aid">id>
<result property="uid" column="uid">result>
<result property="money" column="money">result>
collection>
resultMap>
<select id="findAll" resultMap="userAccountMap">
select u.*, a.id as aid, a.uid, a.money from user u left outer join account a on u.id = a.uid
select>
mapper>
collection:
部分定义了用户关联的账户信息。表示关联查询结果集
property=“accounts”:
关联查询的结果集存储在 User 对象的上哪个属性。
ofType=“account”:
指定关联查询的结果集中的对象类型即List中的对象类型。此处可以使用别名,也可以使用全限定名。
/**
* @author boy
* @create 2020-07-23 21:02
*/
public class UserTest {
private InputStream in;
private SqlSession sqlSession;
private UserDao userDao;
@Before // 用于在测试方法执行之前执行
public void init() throws Exception {
// 1. 读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2. 获取SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 3. 获取SqlSession对象
sqlSession = factory.openSession();
// 4. 获取dao的代理对象
userDao = sqlSession.getMapper(UserDao.class);
}
@After // 用于在测试方法执行之后执行
public void destroy() throws Exception {
// 提交事务
sqlSession.commit();
// 6. 释放资源
sqlSession.close();
in.close();
}
/**
* 测试查询所有
*/
@Test
public void testFindAll(){
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println("----------每个用户的信息-----------");
System.out.println(user);
System.out.println(user.getAccounts());
}
}
}
需求:
实现查询所有对象并且加载它锁分配的用户信息。
分析:
查询角色我们需要用到 Role 表,但角色分配的用户的信息我们并不能直接找到用户信息,而是要通过中间表(USER_ROLE 表)才能关联到用户信息。
下面是实现的 SQL 语句:
SELECT
r.*, u.id uid, u.username, u.birthday, u.sex,u.address
FROM
role r
INNER JOIN
user_role ur
ON ( r.id = ur.rid)
INNER JOIN
user u
ON (ur.uid = u.id);
/**
* @author boy
* @create 2020-07-23 21:47
*/
public class Role implements Serializable {
private Integer roleId;
private String roleName;
private String roleDesc;
// 多对多的关系映射,一个角色可以赋予多个角色
private List<User> users;
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
public Integer getRoleId() {
return roleId;
}
public void setRoleId(Integer roleId) {
this.roleId = roleId;
}
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;
}
@Override
public String toString() {
return "Role{" +
"roleId=" + roleId +
", roleName='" + roleName + '\'' +
", roleDesc='" + roleDesc + '\'' +
'}';
}
}
/**
* @author boy
* @create 2020-07-23 21:48
*/
public interface RoleDao {
/**
* 查询所有角色
* @return
*/
List<Role> findAll();
}
<mapper namespace="com.itheima.dao.RoleDao">
<resultMap id="roleMap" type="Role">
<id property="roleId" column="rid">id>
<result property="roleName" column="role_name">result>
<result property="roleDesc" column="role_desc">result>
<collection property="users" ofType="User">
<id property="id" column="id">id>
<result column="username" property="username">result>
<result column="address" property="address">result>
<result column="sex" property="sex">result>
<result column="birthday" property="birthday">result>
collection>
resultMap>
<select id="findAll" resultMap="roleMap">
select u.*, r.id as rid, r.role_name, r.role_desc from role r
left outer join user_role ur on r.id = ur.rid
left outer join user u on u.id = ur.uid
select>
mapper>
/**
* @author boy
* @create 2020-07-23 21:50
*/
public class RoleTest {
private InputStream in;
private SqlSession sqlSession;
private RoleDao roleDao;
@Before // 用于在测试方法执行之前执行
public void init() throws Exception {
// 1. 读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2. 获取SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 3. 获取SqlSession对象
sqlSession = factory.openSession();
// 4. 获取dao的代理对象
roleDao = sqlSession.getMapper(RoleDao.class);
}
@After // 用于在测试方法执行之后执行
public void destroy() throws Exception {
// 提交事务
sqlSession.commit();
// 6. 释放资源
sqlSession.close();
in.close();
}
/**
* 测试查询所有
*/
@Test
public void testFindAll(){
List<Role> roles = roleDao.findAll();
for (Role role : roles) {
System.out.println("----------每个角色的信息-----------");
System.out.println(role);
System.out.println(role.getUsers());
}
}
}
从 User 出发,我们也可以发现一个用户可以具有多个角色,这样用户到角色的关系也还是一对多关系。这样我们就可以认为 User 与 Role 的多对多关系,可以被拆解成两个一对多关系来实现。
/**
* @author boy
* @create 2020-07-22 19:30
*/
public class User implements Serializable {
private Integer id;
private String username;
private String address;
private String sex;
private Date birthday;
// 多对多的关系映射:一个用户可以具备多个角色
private List<Role> roles;
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = 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 String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", address='" + address + '\'' +
", sex='" + sex + '\'' +
", birthday=" + birthday +
'}';
}
}
/**
* @author boy
* @create 2020-07-23 21:48
*/
public interface RoleDao {
/**
* 查询所有角色
* @return
*/
List<Role> findAll();
}
<mapper namespace="com.itheima.dao.UserDao">
<resultMap id="userMap" type="User">
<id property="id" column="id">id>
<result property="username" column="username">result>
<result property="address" column="address">result>
<result property="sex" column="sex">result>
<result property="birthday" column="birthday">result>
<collection property="roles" ofType="Role">
<id property="roleId" column="rid">id>
<result property="roleName" column="role_name">result>
<result property="roleDesc" column="role_desc">result>
collection>
resultMap>
<select id="findAll" resultMap="userMap">
select u.*, r.id as rid, r.role_name, r.role_desc from user u
left outer join user_role ur on u.id = ur.uid
left outer join role r on r.id = ur.rid
select>
<select id="findById" parameterType="int" resultType="User">
select * from user where id = #{uid}
select>
mapper>
package com.itheima.test;
import com.itheima.dao.UserDao;
import com.itheima.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.util.List;
/**
* @author boy
* @create 2020-07-23 21:02
*/
public class UserTest {
private InputStream in;
private SqlSession sqlSession;
private UserDao userDao;
@Before // 用于在测试方法执行之前执行
public void init() throws Exception {
// 1. 读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2. 获取SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 3. 获取SqlSession对象
sqlSession = factory.openSession();
// 4. 获取dao的代理对象
userDao = sqlSession.getMapper(UserDao.class);
}
@After // 用于在测试方法执行之后执行
public void destroy() throws Exception {
// 提交事务
sqlSession.commit();
// 6. 释放资源
sqlSession.close();
in.close();
}
/**
* 测试查询所有
*/
@Test
public void testFindAll(){
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println("----------每个用户的信息-----------");
System.out.println(user);
System.out.println(user.getRoles());
}
}
}
import org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.util.List;
/**
* @author boy
* @create 2020-07-23 21:02
*/
public class UserTest {
private InputStream in;
private SqlSession sqlSession;
private UserDao userDao;
@Before // 用于在测试方法执行之前执行
public void init() throws Exception {
// 1. 读取配置文件,生成字节输入流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2. 获取SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 3. 获取SqlSession对象
sqlSession = factory.openSession();
// 4. 获取dao的代理对象
userDao = sqlSession.getMapper(UserDao.class);
}
@After // 用于在测试方法执行之后执行
public void destroy() throws Exception {
// 提交事务
sqlSession.commit();
// 6. 释放资源
sqlSession.close();
in.close();
}
/**
* 测试查询所有
*/
@Test
public void testFindAll(){
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println("----------每个用户的信息-----------");
System.out.println(user);
System.out.println(user.getRoles());
}
}
}