Mybatis框架(03)

本文深入探讨MyBatis框架的连接池技术和事务控制机制,包括连接池的配置与使用、事务的手动提交与自动提交设置。此外,还介绍了动态SQL语句的应用,如<if>、<where>、<foreach>标签的使用方法及SQL片段的定义与引用技巧。

1.Mybatis连接池与事务深入

1.1Mybatis的连接池技术

Mybatis有连接池技术,在Mybatis的SqlMapConfig.xml配置文件中,通过来实现Mybatis中连接池的配置。

1.1.1 Mybatis连接池的分类

在这里插入图片描述

在这里插入图片描述

1.1.2 Mybatis中数据源的配置

在这里插入图片描述

1.1.3 Mybatis中DataSource的存取

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

package org.apache.ibatis.datasource;
 import java.util.Properties; 
 import javax.sql.DataSource; 
 /** * @author Clinton Begin */ 
 public interface DataSourceFactory { 
 void setProperties(Properties props); 
 DataSource getDataSource(); 
 }

MyBatis创建了DataSource实例后,会将其放到Configuration对象内的Environment对象中, 供以后使用。 具体分析过程如下:
在这里插入图片描述

1.1.4 Mybatis中连接的获取过程分析

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

@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()); }

只有当第4句sqlSession.selectList(“findUserById”),才会触发MyBatis在底层执行下面这个方法来创建java.sql.Connection对象。 如何证明它的加载过程呢? 我们可以通过断点调试,在PooledDataSource中找到如下popConnection()方法,如下所示:

在这里插入图片描述
分析源代码,得出PooledDataSource工作原理如下:

在这里插入图片描述
下面是连接获取的源代码:
在这里插入图片描述
最后我们可以发现,真正连接打开的时间点,只是在我们执行SQL语句时,才会进行。其实这样做我们也可以进一步发现,数据库连接是我们最为宝贵的资源,只有在要用到的时候,才去获取并打开连接,当我们用完了就再立即将数据库连接归还到连接池中。

1.2 Mybatis的事务控制

1.2.1 JDBC中事务的回顾

在JDBC中我们可以通过手动方式将事务的提交改为手动方式,通过setAutoCommit()方法就可以调整。 通过JDK文档,我们找到该方法如下:
在这里插入图片描述
那么我们的Mybatis框架因为是对JDBC的封装,所以Mybatis框架的事务控制方式,本身也是用JDBC的setAutoCommit()方法来设置事务提交方式的。

1.2.2 Mybatis中事务提交方式

Mybatis中事务的提交方式,本质上就是调用JDBC的setAutoCommit()来实现事务控制。 我们运行之前所写的代码:

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

    @Before//在测试方法执行之前执行
    public void init() throws Exception {
        //1.读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.创建构建者对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //3.创建SqlSession工厂对象
        factory = builder.build(in);
        //4.创建SqlSession对象
        session = factory.openSession();
        // 5.创建Dao的代理对象
        userDao = session.getMapper(IUserDao.class);
    }

    @After//在测试方法执行完成之后执行
    public void destroy() throws Exception {
        //7.提交事务
        session.commit();
        //8.释放资源 
        session.close();
        in.close();
    } 

观察在它在控制台输出的结果:
在这里插入图片描述
这是我们的Connection的整个变化过程,通过分析我们能够发现之前的CUD操作过程中,我们都要手动进行事务的提交,原因是setAutoCommit()方法,在执行时它的值被设置为false了,所以我们在CUD操作中,必须通过sqlSession.commit()方法来执行提交操作。

1.2.3 Mybatis自动提交事务的设置

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

    @Before
//在测试方法执行之前执行
    public void init() throws Exception {
// 1.读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2.创建构建者对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 3.创建SqlSession工厂对象
        factory = builder.build(in);
// 4.创建SqlSession对象
        session = factory.openSession(true);
// 5.创建Dao的代理对象
        userDao = session.getMapper(IUserDao.class);
    }

    // @After//在测试方法执行完成之后执行
    public void destroy() throws Exception {
// 7.释放资源
        session.close();
        in.close();
    } 

所对应的DefaultSqlSessionFactory类的源代码:
在这里插入图片描述
在这里插入图片描述
此时事务就设置为自动提交了,同样可以实现CUD操作时记录的保存。虽然这也是一种方式,但就编程而言,设置为自动提交方式为false再根据情况决定是否进行提交,这种方式更常用。因为我们可以根据业务情况来决定提交是否进行提交。

2.Mybatis的动态SQL语句

有些时候业务逻辑复杂时,SQL是动态变化的。

2.1 <.if>标签

我们根据实体类的不同取值,使用不同的SQL语句来进行查询。比如在id如果不为空时可以根据id查询,如果username不同空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到。

2.1.1 持久层Dao接口

/** * 根据用户信息,查询用户列表 
* @param user 
* * @return 
*/ 
List<User> findByUser(User user);

2.1.2 持久层Dao映射配置

<select id="findByUser" resultType="user" parameterType="user"> 
select * from user where 1=1 
<if test="username!=null and username != '' "> 
and username like #{username} 
</if> 
<if test="address != null"> 
and address like #{address} 
</if> 
</select>

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

2.1.3 测试

@Test public void testFindByUser() { 
User u = new User(); 
u.setUsername("%王%"); 
u.setAddress("%顺义%"); 
//6.执行操作 
List<User> users = userDao.findByUser(u); 
for(User user : users) { 
System.out.println(user); 
} 
}

2.2<.where>标签

为简化上面where 1=1的条件拼装,可以采用标签来简化开发。

2.2.1 持久层Dao映射配置

<!--根据用户信息查询-->
<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>

2.3 <.foreach>标签

2.3.1 需求

传入多个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)

这样我们在进行范围查询时,就要将一个集合中的值,作为参数动态添加进来。 这样我们将如何进行参数的传递?

2.3.1.1 在QueryVo中加入一个List集合用于封装参数

/**
 * * <p>Title: QueryVo</p> 
 * * <p>Description: 查询的条件</p> 
 * * <p>Company: http://www.itheima.com/ </p>
 */
public class QueryVo implements Serializable {
    private List<Integer> ids;

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

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

2.3.2 持久层Dao接口

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

2.3.3 持久层Dao映射配置

在这里插入图片描述

2.3.3.1 编写测试方法

在这里插入图片描述

2.4 Mybatis中简化编写的SQL片段

Sql中可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的。

2.4.1 定义代码片段

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

2.4.2 引用代码片段

<!-- 配置查询所有操作 --> 
<select id="findAll" resultType="user"> 
<include refid="defaultSql"></include> 
</select> 
<!-- 根据id查询 --> 
<select id="findById" resultType="UsEr" parameterType="int">
<include refid="defaultSql"></include>
where id = #{uid}
</select>

3.Mybatis 多表查询之一对多

本次案例主要以最为简单的用户和账户的模型来分析Mybatis 多表关系。用户为User 表,账户为Account
表。一个用户(User)可以有多个账户(Account)。具体关系如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值