抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

本章学习MyBatis中的连接池技术与事务深入。MyBatis基于xml配置动态sql语句的使用。MyBatyis中的多表操作。

MyBatis中的连接池

一、连接池的基本介绍

1、优点

使用连接池技术可以使我们的开发效率提高,可以减少获取连接数据库的所消耗的时间减少。


2、作用

  • 连接池就是用于存储连接的一个容器;
  • 容器其实本质是一个集合对象,而且该集合对象必须是线程安全的,不能让两个线程拿到同一个连接;
  • 该集合还必须实现队列的特性(先进先出)。

MyBatis中的连接池技术


3、MyBatis连接池的分类

MyBatis中的数据源dataSource分为以下几类

连接池分类

由上图可以看出MyBatis的数据源分为三类:

  • UNPOOLED 不使用连接池的数据源
  • POOLED 使用连接池的数据源
  • JBDI 使用JDNI实现的数据源

MyBatis 内部分别定义了实现了 java.sql.DataSource 接口的 UnpooledDataSource、PooledDataSource 类来表示 UNPOOLED、POOLED 类型的数据源。

PooledDataSource和UnpooledDataSource都实现了java.lang.DataSource接口。并且PooledDataSource持有一个UnpooledDataSource的引用。当PooledDataSource需要创建java.lang.Connection实例对象时,还是通过UnpooledDataSource来创建的,PooledDataSource只是提供一种缓存连接池机制。


4、MyBatis中数据源的配置

数据源的配置在SqlMapConfig.xml文件中,具体配置如下

1
2
3
4
5
6
7
8
<!-- 配置数据源(也叫连接池) -->
<dataSource type="POOLED">
<!-- 配置连接数据库的4个基本信息 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/eesy_mybatis"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>

MyBatis在初始化时,根据的type属性来创建相应类型的数据源DataSource,即:

  • type=“POOLED”:MyBatis会创建PooledDataSource实例
  • **type=”UNPOOLED”**:MyBatis会创建UnpooledDataSource实例
  • **type=”JNDI”**:MyBatis会从JNDI服务上查找DataSource实例,然后返回使用。

5、MyBatis中DataSource的存取

MyBatis是通过工厂模式来创建数据源DataSource对象的,MyBatis定义了抽象的工厂接口org.apache.ibatis.datasource.DataSourceFactory,通过其getDataSource()方法返回数据源DataSource。

MyBatis 创建了 DataSource 实例后,会将其放到 Configuration 对象内的 Environment 对象中, 供以后使用。


6、MyBatis中连接的获取过程分析

当我们需要创建SqlSession对象并需要执行SQL语句时,这时候MyBatis才会去调用dataSource对象来创建java.lang.Connection对象。也就是说java.lang.Connection对象的创建会一直延迟到SQL语句执行的时候才创建。

1
2
3
4
5
6
7
8
@Test
public void testSql() throws Exception {
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession = factory.openSession();
List<User> list = sqlSession.selectList("findUserById",41);
System.out.println(list.size());
}

由上面的代码可知,只有知道第四条语句(sqlSession.selectList(“findUserById”,41))的时候,才会触发MyBatis在底层执行下面的方法来创建java.lang.Connection对象。

连接池创建连接对象

下面时获取连接的源码

获取连接的源码

总结:真正打开连接的时间点,只是在执行SQL语句的时候才会进行。这样做的原因是因为数据库连接是十分宝贵的资源,只有在要用到的时候才去获取并打开连接,当我们使用完毕之后再立即将数据库连接归还到连接池中。



二、MyBatis中的事务控制

1、MyBatis中的事务提交方式

MyBatis中的事务提交方式本质就是调用JDBC的setAutoCommit()来实现事务控制的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Before
public void init() throws Exception {
//读取配置文件,使用输入输出流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory工厂(用于创建数据库连接对象)
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//使用工厂生产SqlSession对象
session = factory.openSession();
//使用SqlSession创建Dao接口的代理对象(增强其中的方法)
userDao = session.getMapper(IUserDao.class);
}

@After
public void destroy() throws Exception {
//手动提交
session.commit();
//释放资源
session.close();
in.close();
}

@Test
public void testSaveUser() throws Exception {
User user = new User();
user.setUsername("mybatis user09");
int res = userDao.saveUser(user);
System.out.println(res);
System.out.println(user.getId());
}

在控制台的输出结果为:

控制台运行结果

由上面可知我们每次进行CUD操作时都要手动的进行事务提交。原因是因为setAutoCommit()方法在执行的时候的值被设置为false,所以在CUD操作时都需调用sqlSession()方法手动的提交操作。


2、MyBatis中自动提交事务

CUD过程中之所以必须使用sqlSession.commit()提交事务,主要原因是在连接池中取出的连接 都会调用connection.setAutoCommit(false)方法提交事务。这样就必须使用sqlSession.commit()方法手动提交,相当于使用了JDBC中的connection.commit()方法实现事务提交。

自动提交事务的修改代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Before
public void init() throws Exception {
//读取配置文件,使用输入输出流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建构建者对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//创建SqlSession工厂对象
factory = builder.build(in);
//使用工厂生产SqlSession对象
session = factory.openSession(true);//此处传入一个布尔类型的参数
//使用SqlSession创建Dao接口的代理对象(增强其中的方法)
userDao = session.getMapper(IUserDao.class);
}

@After
public void destroy() throws Exception {
//释放资源
session.close();
in.close();
}

此时事务便会被设置为自动提交,同样可以实现CUD操作时记录的保存。但就编程而言,设置为自动提交方式为false更为常用。



三、MyBatis的动态SQL语句

在刚开始入门的时候我们所学习的SQL都是比较简单的,但当我们的业务逻辑变得复杂时,我们的SQL是动态变化的。

1、动态SQL语句之标签

在根据实体类的不同取值,使用不同的SQL语句来进行查询。比如id不为空时可以根据id查询,如果username不为空时还需要加入username作为查询的约束条件。

(1)持久层Dao接口

1
2
3
4
5
/**
* 修改用户信息
* @param user
*/
int updateUser(User user);

(2)持久层Dao映射配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- 修改用户信息 -->
<update id="updateUser" parameterType="com.itheima.domain.User">
update user
<set>
<if test="username != null">
username = #{username},
</if>
<if test="sex != null">
sex = #{sex},
</if>
<if test="birthday != birthday">
birthday = #{birthday},
</if>
<if test="address != null">
address = #{address}
</if>
</set>
where id = #{id}
</update>

注意事项:标签的test属性写的是对象的属性名,如果是包装类的对象要使用OGNL表达式的写法

(3)测试

1
2
3
4
5
6
7
8
/**
* 修改数据库中一条信息
*/
@Test
public void updateUserTest(){
User user = new User(53,"wangwu",null,"男",null);
System.out.println(userDao.updateUser(user));//输出结果为1,说明数据库中有一条语句因为这条语句发生了改变
}

2、动态SQL语句之标签

where也可以使用动态的标签来简化开发

(1)持久层Dao的映射配置

1
2
3
4
5
6
7
8
9
10
11
12
<!-- 根据用户信息查询 --> 
<select id="findByUser" resultType="user" parameterType="user">
<include refid="defaultSql"></include>
<where>
<if test="username!=null and username != '' ">
and username like #{username}
</if>
<if test="address != null">
and address like #{address}
</if>
</where>
</select>

3、动态SQL语句之标签

(1)需求

当传入多个id查询用户信息,可以使用下面的sql语句实现:

SELECT * FROM USER WHERE username LIKE ‘%张%’ AND (id = 10 OR id = 98 OR id = 16);

SELECT * FROM USER WHERE username LIKE ‘%张%’ AND id IN (10,98,16);

在进行范围查询时就需要将一个集合中的值作为动态添加进来。

(2)实现方式——在QueryVo中加入一个List集合用于封装参数

1
2
3
4
5
6
7
8
public class QueryVo implements Serializable {
private List<Integer> ids;

public List<Integer> getIds() {return ids; }

public void setIds(List<Integer> ids) {this.ids = ids; }

}

(3)持久层Dao接口

1
2
3
4
5
6
/**
* 根据 id 集合查询用户
* @param vo
* @return
*/
List<User> findInIds(QueryVo vo);

(4)持久层Dao映射配置

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 查询所有用户在 id 的集合之中 --> 
<select id="findInIds" resultType="user" parameterType="queryvo">
<!-- select * from user where id in (1,2,3,4,5); -->
<!-- 简化编写的sql语句,具体操作在第四个知识点 -->
<include refid="defaultSql"></include>
<where>
<if test="ids != null and ids.size() > 0">
<foreach collection="ids" open="id in ( " close=")" item="uid" separator=",">
#{uid}
</foreach>
</if>
</where>
</select>

标签的属性:

  • collection:代表要遍历的集合元素,注意编写时不要写#{};
  • open:代表语句要开始的部分;
  • close:代表结束部分;
  • item:代表遍历集合的每个元素,生成的变量名;
  • sperator:代表分隔符。

(5)编写测试用例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void testFindInIds() {
QueryVo vo = new QueryVo();
List<Integer> ids = new ArrayList<Integer>();
ids.add(41);
ids.add(42);
ids.add(43);
ids.add(46);
ids.add(57);
vo.setIds(ids);
List<User> users = userDao.findInIds(vo);
for(User user : users) {
System.out.println(user);
}
}


四、MyBatis中简化编写的SQL片段

1、定义重复出现的sql语句片段

1
2
3
4
<!-- 抽取重复的语句代码片段 -->
<sql id="defaultSql">
select * from user
</sql>

2、引用代码片段

1
2
3
4
5
6
7
8
9
10
<!-- 根据上面配置的sql语句配置查询所有的操作 -->
<select id="findAll" resultType="user">
<include refid="defaultSql"></include>
</select>

<!-- 根据id查询 -->
<select id="findById" resultType="User" parameterType="int">
<include refid="defaultSql"></include>
where id = #{id}
</select>


五、MyBatis多表查询之一对一

通过Account查询对应的User信息

1、环境搭建

(1)数据库搭建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
--在之前的数据库中再次建一个account表和三条账户信息--
DROP TABLE IF EXISTS `account`;

CREATE TABLE `account` (
`ID` int(11) NOT NULL COMMENT '编号',
`UID` int(11) default NULL COMMENT '用户编号',
`MONEY` double default NULL COMMENT '金额',
PRIMARY KEY (`ID`),
KEY `FK_Reference_8` (`UID`),
CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


insert into `account`(`ID`,`UID`,`MONEY`) values (1,46,1000),(2,45,1000),(3,46,2000);

数据库中数据详情

  • user表

    数据库User

  • account表

    数据库account

(2)创建maven工程


2、创建account表的实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package com.itheima.domain;

import java.io.Serializable;

public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;

// 从表关系映射应该包含主表的实体对象(一对一关系映射)
private User user;

@Override
public String toString() {
return "Account{" +
"id=" + id +
", uid=" + uid +
", money=" + money +
", 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;
}
}

创建账户表与用户表的另一实体类,用于封装数据

数据格式(account[id,uid,money] accountUser[username,address])

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.itheima.domain;

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 + '\'' +
'}';
}
}

3、创建account表的持久层接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.itheima.dao;

import com.itheima.domain.Account;
import com.itheima.domain.AccountUser;
import java.util.List;

public interface IAccountDao {
/**
* 查询所有账户信息
* @return
*/
List<Account> findAll();

/**
* 查询所有的AccountUser信息
* @return
*/
List<AccountUser> findAllAccount();
}

4、编写持久层Dao接口的映射配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?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.itheima.dao.IAccountDao">
<!-- 定义封装了Account和User的resultMap -->
<resultMap id="accountUserMap" type="account">
<id property="id" column="aid"></id>
<result column="uid" property="uid"></result>
<result column="money" property="money"></result>
<!-- 一对一的关系映射:配置封装user的内容 -->
<association property="user" column="uid" javaType="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>
</association>
</resultMap>


<!-- 查询所有并封装到一个List集合 -->
<select id="findAll" resultMap="accountUserMap">
select a.*,u.username,u.address from account a , user u where u.id = a.uid
</select>

<!-- 查询所有账户信息和用户名以及地址信息 -->
<select id="findAllAccount" resultType="com.itheima.domain.AccountUser">
select a.*,u.username,u.address from account a , user u where u.id = a.uid
</select>
</mapper>

association标签:当实体类中包含另一个实体类对象作为成员变量时,封装结果集时使用该标签。

总结:封装的结果集中包含另外一张表的信息,且另外一张表的实体类作为查询表的成员变量时使用该标签封装另一张表的数据。

标签内部的标签与resultMap标签一致,id标签为另外一张表的主键信息,result标签为其余封装的列的信息。


5、编写测试用例用于结果测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* 查询所有
*/
@Test
public void findByAllTest(){
List<Account> accounts = accountDao.findAll();
for (Account account: accounts) {
System.out.println("---------------------------");
System.out.println(account);
System.out.println(account.getUser());
}
}

/*运行结果:
---------------------------
Account{id=null, uid=46, money=1000.0, user=User{id=1, username='老王', birthday=null, sex='null', address='北京'}}
User{id=1, username='老王', birthday=null, sex='null', address='北京'}
---------------------------
Account{id=null, uid=45, money=1000.0, user=User{id=2, username='传智播客', birthday=null, sex='null', address='北京金燕龙'}}
User{id=2, username='传智播客', birthday=null, sex='null', address='北京金燕龙'}
---------------------------
Account{id=null, uid=46, money=2000.0, user=User{id=3, username='老王', birthday=null, sex='null', address='北京'}}
User{id=3, username='老王', birthday=null, sex='null', address='北京'}
*/

/**
* 查询所有账户和用户名与地址
*/
@Test
public void findByAccountTest(){
List<AccountUser> accountUsers = accountDao.findAllAccount();
for (AccountUser accountUser: accountUsers) {
System.out.println(accountUser);
}
}

/*运行结果
Account{id=1, uid=46, money=1000.0, user=null} AccountUser{username='老王', address='北京'}
Account{id=2, uid=45, money=1000.0, user=null} AccountUser{username='传智播客', address='北京金 燕龙'}
Account{id=3, uid=46, money=2000.0, user=null} AccountUser{username='老王', address='北京'}
*/


六、MyBatis多表查询之一对多查询

通过User查询Account的信息

1、User实体类的修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package com.itheima.domain;

import java.util.Date;
import java.util.List;

public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;

//一对多关系映射:主表的关系映射应该包含从表实体的集合引用
private List<Account> accounts;

public User() {
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}

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 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;
}
}

2、编写持久层Dao接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.itheima.dao;

import com.itheima.domain.User;

import java.util.List;

public interface IUserDao {
/**
* 查询所有用户,同时获取到用户下的所有账户信息
* @return
*/
List<User> findByAll();

/**
* 根据id查询用户
* @return
*/
User findById(int id);
}

3、编写持久层Dao接口的映射配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?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.itheima.dao.IUserDao">
<!-- 定义User的resultMap -->
<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>
<!-- 配置user对象中accounts集合的映射 -->
<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>


<!-- 查询所有并封装到一个List集合 -->
<select id="findByAll" resultMap="userMap">
select * from user u left outer join account a on u.id = a.uid
</select>

<!-- 根据id查询User对象 -->
<select id="findById" resultType="com.itheima.domain.User" parameterType="int">
select * from user
where id = #{id}
</select>
</mapper>

collection标签:与上面的association标签大致一样,不过该标签使用的对象是集合对象而不是实体类对象。


4、编写接口Dao的测试用例

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 查询所有
*/
@Test
public void findByAll(){
List<User> users = userDao.findByAll();
for (User user: users) {
System.out.println("----------------------");
System.out.println(user);
System.out.println(user.getAccounts());
}
}

运行的结果为

运行结果

七、MyBatis多表查询之多对多

通过角色表Role查询User信息多对多

1、环境搭建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
DROP TABLE IF EXISTS `role`;

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);

数据库中数据详情

  • role表

    数据库role

  • role_user中间表

    数据库role_user

(2)创建maven工程


2、编写角色表的实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package com.itheima.domain;

import java.io.Serializable;
import java.util.List;

public class Role implements Serializable {
private Integer roleId;
private String roleName;
private String roleDesc;


//多对多的关系映射:一个角色可以赋予多个用户(所以多对多可以简化成一对多去查询)
private List<User> 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;
}

public List<User> getUsers() {
return users;
}

public void setUsers(List<User> users) {
this.users = users;
}

@Override
public String toString() {
return "Role{" +
"roleId=" + roleId +
", roleName='" + roleName + '\'' +
", roleDesc='" + roleDesc + '\'' +
'}';
}
}

3、编写角色表的持久层Dao接口

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.itheima.dao;

import com.itheima.domain.Role;

import java.util.List;

public interface IRoleDao {
/**
* 查询所有
* @return
*/
List<Role> findAll();
}

4、编写持久层Dao接口的配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?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.itheima.dao.IRoleDao">
<resultMap id="roleMap" type="role">
<id property="roleId" column="rid"/>
<result property="roleName" column="role_name"/>
<result property="roleDesc" column="role_desc"/>
<collection property="users" ofType="user">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="sex" column="sex"/>
<result property="address" column="address"/>
<result property="birthday" column="birthday"/>
</collection>
</resultMap>

<!-- 查询所有 -->
<select id="findAll" resultMap="roleMap">
<!-- 此处的r.id的别名rid是为了和上面的rid相匹配,若不起此别名数据查询会出现问题 -->
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>

5、编写测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.itheima.test;

import com.itheima.dao.IRoleDao;
import com.itheima.domain.Role;
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;

public class IRoleDaoTest {
private InputStream in = null;
private SqlSession session = null;
private IRoleDao iRoleDao;

@Before
public void init() throws Exception {
//读取配置文件,使用输入输出流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory工厂(用于创建数据库连接对象)
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//使用工厂生产SqlSession对象
session = factory.openSession(true);
//使用SqlSession创建Dao接口的代理对象(增强其中的方法)
iRoleDao = session.getMapper(IRoleDao.class);
}

@After
public void destroy() throws Exception {
//手动提交
// session.commit();
//释放资源
session.close();
in.close();
}

@Test
public void findAllTest(){
List<Role> roles = iRoleDao.findAll();
for (Role role : roles) {
System.out.println("-----------------------");
System.out.println(role);
System.out.println(role.getUsers());
}
}
}

运行结果为;

role运行结果



通过User表进行多对多查询Account表信息

1、编写用户表的实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package com.itheima.domain;

import java.util.Date;
import java.util.List;

public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;

//多对多关系映射:主表的关系映射应该包含从表的集合引用
private List<Role> roles;

public User() {
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}

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 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;
}
}

2、编写角色表的持久层Dao接口

1
2
3
4
5
/**
* 查询用户表中对应的角色表中的所有信息
* @return
*/
List<User> findAll();

3、编写持久层Dao接口的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?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.itheima.dao.IUserDao">
<resultMap id="userRoleMap" type="user">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="sex" column="sex"/>
<result property="address" column="address"/>
<result property="birthday" column="birthday"/>
<collection property="roles" ofType="role">
<id property="roleId" column="id"/>
<result property="roleName" column="role_name"/>
<result property="roleDesc" column="role_Desc"/>
</collection>
</resultMap>

<!-- 查询用户表中的所有角色信息 -->
<select id="findAll" resultMap="userRoleMap">
select u.*,r.id as rid,r.role_name,r.role_desc from user u
left outer join user_role ur on ur.uid = u.id
left outer join role r on r.id = ur.rid
</select>
</mapper>

4、编写测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.itheima.test;

import com.itheima.dao.IUserDao;
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;

public class IUserDaoTest {
private InputStream in = null;
private SqlSession session = null;
private IUserDao userDao;

@Before
public void init() throws Exception {
//读取配置文件,使用输入输出流
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory工厂(用于创建数据库连接对象)
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//使用工厂生产SqlSession对象
session = factory.openSession(true);
//使用SqlSession创建Dao接口的代理对象(增强其中的方法)
userDao = session.getMapper(IUserDao.class);
}

@After
public void destroy() throws Exception {
//手动提交
// session.commit();
//释放资源
session.close();
in.close();
}

@Test
public void findAllTest(){
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println("---------------------");
System.out.println(user);
System.out.println(user.getRoles());
}
}
}

运行结果:

user运行结果

八、JNDI搭建maven的war工程

1、JNDI的基本概念

JNDI(Java Naming and Directory Interface,Java命名和目录接口)是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,通过不同的访问提供者接口JNDI服务供应接口(SPI)的实现,由管理者将JNDI API映射为特定的命名服务和目录系统,使得Java应用程序可以和这些命名服务和目录服务之间进行交互。

目录服务是命名服务的一种自然扩展。两者之间的关键差别是目录服务中对象不但可以有名称还可以有属性(例如,用户有email地址),而命名服务中对象没有属性。


2、创建一个war工程

(1)create new modules

创建maven下的webapp工程

JNDI新建项目1

(2)填写坐标信息

JNDI新建项目2

(3)在创建好的项目的main目录下创建文件夹

在main目录下新建java和resoures,并将两个文件设置为source root和resoures root源码文件

在test目录下新建一个java文件夹,设置为test source root文件夹

(4)导入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.itheima</groupId>
<artifactId>day03_eesy_jndi</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>

<name>day03_eesy_jndi Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>

<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
</dependency>
</dependencies>

<build>
<finalName>day03_eesy_jndi</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

(5)修改SqlMapConfig.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?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">
<!-- mybatis的主配置文件 -->
<configuration>
<!-- 配置实体类的别名 -->
<typeAliases>
<package name="com.itheima.domain"/>
</typeAliases>

<!-- 配置环境 -->
<environments default="development">
<!-- 配置mysql环境 -->
<environment id="development">
<!-- 配置事务类型 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源(也叫连接池) -->
<dataSource type="JNDI">
<property name="data_source" value="java:comp/env/jdbc/eesy_mybatis"/>
</dataSource>
</environment>
</environments>
<!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件
如果是用注解来配置的话,此处应该使用class属性指定被注解的dao全限定类名
<mapper class="com.itheima.dao.IUserDao"/>
-->
<mappers>
<mapper resource="com/itheima/dao/IAccountDao.xml"/>
<mapper resource="com/itheima/dao/IUserDao.xml"/>
</mappers>

</configuration>

(6)在webapp目录下新建配置文件

在webapp目录下新建META-INF文件,并在文件夹中导入数据库配置文件context.xml

以下是context.xml文件配置的数据库文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<!--
<Resource
name="jdbc/eesy_mybatis" 数据源的名称
type="javax.sql.DataSource" 数据源类型
auth="Container" 数据源提供者
maxActive="20" 最大活动数
maxWait="10000" 最大等待时间
maxIdle="5" 最大空闲数
username="root" 用户名
password="1234" 密码
driverClassName="com.mysql.jdbc.Driver" 驱动类
url="jdbc:mysql://localhost:3306/eesy_mybatis" 连接url字符串
/>
-->
<Resource
name="jdbc/eesy_mybatis"
type="javax.sql.DataSource"
auth="Container"
maxActive="20"
maxWait="10000"
maxIdle="5"
username="root"
password="root"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/eesy_mybatis"
/>
</Context>

(7)修改index.jsp文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<%@ page import="java.io.InputStream" %>
<%@ page import="org.apache.ibatis.io.Resources" %>
<%@ page import="org.apache.ibatis.session.SqlSessionFactoryBuilder" %>
<%@ page import="org.apache.ibatis.session.SqlSessionFactory" %>
<%@ page import="org.apache.ibatis.session.SqlSession" %>
<%@ page import="com.itheima.dao.IUserDao" %>
<%@ page import="com.itheima.domain.User" %>
<%@ page import="java.util.List" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html>
<body>
<h2>Hello World!</h2>
<%
//此处的代码是之前测试的main方法中的代码
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.根据配置文件构建SqlSessionFactory
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.使用SqlSessionFactory创建SqlSession对象
SqlSession sqlSession = factory.openSession();
//4.使用SqlSession构建Dao的代理对象
IUserDao userDao = sqlSession.getMapper(IUserDao.class);
//5.执行dao中的findAll方法
List<User> users = userDao.findAll();
for(User user : users){
System.out.println(user);
}
//6.释放资源
sqlSession.close();
in.close();
%>
</body>
</html>

(8)配置Tomcat项目

![JDNI Tomcat](MyBatis框架学习(三)/JDNI Tomcat.JPG)

启动服务器打开浏览器即可在控制台看到如下内容

JNDI Tomcat运行结果







参考资料

传智黑马MyBatis

[https://www.bilibili.com/video/BV1SJ411679L]:

评论