Mybatis框架(7) —— 连接池与事务机制

连接池

  • 连接池是用于存储连接的容器
    • 容器必须是一个线程安全的集合对象,并且具有队列的特性:先进先出。

MyBatis连接池

  • 我们在前面的WEB课程中也学习过类似的连接池技术,而在Mybatis中也有连接池技术,但是它采用的是自
    己的连接池技术。
  • 在Mybatis中我们将它的数据源dataSource分为以下几类:
    • UNPOOLED采用传统的获取连接的方式
    • POOLED采用传统的 javax.sql.DataSource规范中的连接池
    • JNDI采用服务器提供的 JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到DataSource是不一样。

![](7.连接池与事务\MyBatis DataSource jar包.png)

UNPLLED

执行语句,控制台显示结果

底层代码

  • 虽然 UNPOOLED 并没有使用池的思想,但是 MyBatis框架依然定义了 java.sql.DataSource接口 的实现类 UnpooledDataSource 来表示 UNPOOLED类型 的数据源。

POOLED

执行语句,控制台显示结果

底层代码

  • MyBatis框架定义了 java.sql.DataSource接口 的是实现类 DataSource 来表示 POOLED类型 的数据源。

MyBatis连接池的配置

  • 在 Mybatis 的 SqlMapConfig.xml配置文件 中,通过 <dataSource type=”pooled”> 来实
    现 Mybatis 中连接池的配置。
1
2
3
4
5
6
7
8
9
10
11
12
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<!-- MyBatis连接池的配置 -->
<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>
</environment>
</environments>

Mybatis连接池的存取

  • MyBatis 通过工厂模式来创建 DataSource连接池对象,MyBatis 定义了抽象的工厂接
    口:org.apache.ibatis.datasource.DataSourceFactory,通过其 getDataSource()方法 返回
    DataSource连接池对象。
  • 当我们需要创建 SqlSession对象 并需要执行 SQL语句 时,这时候 MyBatis 才会去调用 dataSource对象
    来创建 connection数据库连接对象。也就是说,Connection对象的创建一直延迟到执行SQL语句
    的时候
  • 数据库连接是我们最为宝贵的资源,只有在要用到的时候,才去获取并打开连接,当我们用完了就再
    立即将数据库连接归还到连接池中。

事务控制

  • 在 JDBC 中我们可以通过手动方式将事务的提交改为手动方式,通过 setAutoCommit()方法 就可以调整。Mybatis框架 对 JDBC 进行了封装,所以 Mybatis框架 的事务控制方式,本身也是用 JDBC 的在·
    setAutoCommit()方法 来设置事务提交方式的

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class UserTest {

/* 成员变量 */
private InputStream inputStream;
private SqlSession session;
private UserDao dao;

/* 初始化操作 */
@Before
public void init() throws IOException {
/* 加载 MyBatis配置文件 */
inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
/* 获取 工厂类 */
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
/* 获取 产品类 */
session = factory.openSession();
/* 获取 代理对象 */
dao = session.getMapper(UserDao.class);
}

/* 销毁操作 */
@After
public void destroy() throws IOException {
session.close();
inputStream.close();
}


/** 添加用户 */
@Test
public void test01(){
dao.add( new User(100,"ADD",new Date(),"男","UserDao") );
session.commit();
}

/** 更新用户 */
@Test
public void test02(){
dao.update( new User(100,"UPDATE",new Date(),"男","UserDao") );
session.commit();
}

/** 删除用户 */
@Test
public void test03(){
dao.delete(100);
session.commit();
}


}
  • 控制台输出结果
    • 它显示了 Connection数据库连接对象 的整个变化过程

![](7.连接池与事务/MyBatis事务 控制台输出.png)

MyBatis自动提交事务的设置

  • 通过上面的研究和分析,现在我们一起思考,为什么 CUD过程 中必须使用 sqlSession.commit()提交事
    务?
    • 主要原因就是在连接池中取出的连接,都会将调用 connection.setAutoCommit(false)方法,这样我们
      就必须使用 sqlSession.commit()方法,相当于使用了 JDBC 中的 connection.commit()方法 实现事务提
      交。
    • 我们在通过 SqlSession工厂类 生产 SqlSession类 时,可以传递布尔值 true 来设置 自动提交事务。

MyBatis动态SQL语句的编写

if 标签

  • <if>标签 的 test属性 中写的是对象的属性名,如果是包装类的对象要使用OGNL表达式的写法。
    • 另外要注意where 1=1 的作用~!

持久层接口

1
2
/** 根据用户信息,查询用户列表 */
List<User> findByUser1(User user);

映射配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- SELECT * FROM user WHERE 1 = 1 AND username LIKE #{username} AND address LIKE #{address} -->
<select id="findByUser1" resultType="user" parameterType="user">

/* SQL语句 */
SELECT * FROM user WHERE 1 = 1

/* 动态SQL语句 */
<if test="username!=null and username!='' ">
AND username LIKE #{username}
</if>
<if test="address!=null ">
AND address LIKE #{address}
</if>

</select>

测试类

1
2
3
4
5
6
@Test
public void test01(){
for (User user : dao.findByUser1(new User("老王","北京"))) {
System.out.println(user);
}
}

where 标签

  • 为了简化上面where 1=1的条件拼装,我们采用<where>标签来简化开发。

持久层接口

1
2
/** 根据用户信息,查询用户 */
List<User> findByUser2(User user);

映射配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- 设置SQL语句 -->
<sql id="select">
SELECT * FROM user
</sql>

<!-- SELECT * FROM user WHERE username LIKE #{username} AND address LIKE #{address} -->
<select id="findByUser2" resultType="user" parameterType="user">

/* 使用设置的 SQL语句 */
<include refid="select"></include>

/* 动态SQL语句 */
<where>
<if test="username!=null and username!='' ">
AND username LIKE #{username}
</if>
<if test="address!=null ">
AND address LIKE #{address}
</if>
</where>

</select>

测试类

1
2
3
4
5
6
@Test
public void test02(){
for (User user : dao.findByUser2(new User("老王","北京"))) {
System.out.println(user);
}
}

foreach 标签

QueryVo类

  • 封装List集合,存储要查询的ID
1
2
3
4
5
6
7
8
9
10
11
12
public class QueryVo implements Serializable {

private List<Integer> ids;

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

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

持久层接口

1
2
/** 在ID范围,查询用户 */
List<User> findByIDs(QueryVo queryVo);

映射配置文件

  • SQL语句:
    • select 字段 from user where id in (?)
  • <foreach>标签 用于遍历集合,它的属性:
    • collection属性:代表要遍历的集合元素,注意编写时不要写#{}
    • open属性:代表语句的开始部分
    • close属性:代表结束部分
    • item属性:代表遍历集合的每个元素
    • sperator属性:代表分隔符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- 设置SQL语句 -->
<sql id="select">
SELECT * FROM user
</sql>

<!-- 在ID范围,查询用户 -->
<select id="findByIDs" resultType="user" parameterType="queryVo" >

/* 使用设置的 SQL语句 */
<include refid="select"></include>

/* 动态SQL语句 */
<where>
<if test="list!=null and list.size()>0 ">
<foreach collection="list" open=" id IN (" close=")" item="item" separator=",">
#{item}
</foreach>
</if>
</where>


</select>

测试类

1
2
3
4
5
6
7
8
@Test
public void test03(){
List<Integer> list = new ArrayList<Integer>();
Collections.addAll(list,41,42,43,46);
for (User user : dao.findByIDs(new QueryVo(list))) {
System.out.println(user);
}
}
-------------本文结束-------------
Donate comment here