任务3 掌握MyBatis的核心对象
通过前面的学习,我们对MyBatis有了初步认识,下面继续介绍MyBatis的三个基本要素。
➢ 核心接口和类。
➢ MyBatis核心配置文件(mybatis-config.xml)。
➢ SQL映射文件(mapper.xml)。
MyBatis的核心接口和类,如图1.19所示。
图1.19 MyBatis核心接口和类的结构图
(1)每个MyBatis的应用程序都以一个SqlSessionFactory对象的实例为核心。
(2)首先获取SqlSessionFactoryBuilder对象,可以根据XML配置文件或Configuration类的实例构建该对象。
(3)然后获取SqlSessionFactory对象,该对象实例可以通过SqlSessionFactoryBuilder对象获得。
(4)有了SqlSessionFactory对象之后,进而可以获取SqlSession实例,SqlSession对象中完全包含以数据库为背景的所有执行SQL操作的方法。可以用该实例来直接执行已映射的SQL语句。
1.3.1 SqlSessionFactory的构造者——SqlSessionFactoryBuilder
1. SqlSessionFactoryBuilder的作用
SqlSessionFactoryBuilder负责构建SqlSessionFactory,并且提供了多个build()方法的重载,如图1.20所示。
图1.20 SqlSessionFactoryBuilder提供的build()方法
通过源码分析,可以发现都是在调用同一个签名方法。
build(InputStream inputStream, String environment, Properties properties)
由于方法参数environment和properties都可以为null,去除重复的,真正的重载方法其实只有如下三种。
➢ build(Reader reader, String environment, Properties properties)。
➢ build(InputStream inputStream, String environment, Properties properties)。
➢ build(Configuration config)。
通过上述分析,发现配置信息以三种形式提供给SqlSessionFactoryBuilder的build()方法,分别是InputStream(字节流)、Reader(字符流)、Configuration(类),由于字节流与字符流都属于读取配置文件的方式,所以从配置信息的来源去构建一个SqlSessionFactory有两种方式:读取XML配置文件构造方式和编程构造方式。本章我们采用读取XML配置文件的方式来构造SqlSessionFactory。
2. SqlSessionFactoryBuilder的生命周期和作用域
SqlSessionFactoryBuilder的最大特点是用过即丢。一旦创建了SqlSessionFactory对象,这个类就不需要存在了,因此SqlSessionFactoryBuilder的最佳使用范围就是存在于方法体内,也就是局部变量。
1.3.2 SqlSession的工厂——SqlSessionFactory
1. SqlSessionFactory的作用
SqlSessionFactory就是创建SqlSession实例的工厂。所有的MyBatis应用都是以Sql SessionFactory实例为中心,SqlSessionFactory的实例可以通过SqlSessionFactoryBuilder对象来获得。有了它之后,就可以通过SqlSessionFactory提供的openSession()方法来获取SqlSession实例,如图1.21所示。
图1.21 SqlSessionFactory提供的openSession( )方法
注意
openSession()方法的参数为boolean值时,若传入true表示关闭事务控制,自动提交;若传入false表示开启事务控制。若不传入参数,默认为true。
openSession(boolean autoCommit)
openSession()//若不传入参数,默认为true,自动提交
2. SqlSessionFactory的生命周期和作用域
SqlSessionFactory对象一旦创建,就会在整个应用运行过程中始终存在。没有理由去销毁或再创建它,并且在应用运行中也不建议多次创建SqlSessionFactory。因此SqlSessionFactory的最佳作用域是Application,即随着应用的生命周期一同存在。那么这种“存在于整个应用运行期间,并且同时只存在一个对象实例“的模式就是所谓的单例模式(指在应用运行期间有且仅有一个实例)。
下面优化获取SqlSessionFactory的代码,最简单的实现方式就是放在静态代码块下,以保证SqlSessionFactory对象只被创建一次,实现步骤如下。
(1)创建工具类MyBatisUtil.java,在静态代码块中创建SqlSessionFactory对象。关键代码如示例4所示。
示例4
package cn.smbms.utils; import java.io.IOException; import java.io.InputStream; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; public class MyBatisUtil { private static SqlSessionFactory factory; static{//在静态代码块中,factory只会被创建一次 try { InputStream is = Resources.getResourceAsStream("mybatis-config.xml"); factory = new SqlSessionFactoryBuilder().build(is); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
(2)创建SqlSession对象和关闭SqlSession。关键代码如示例5所示。
示例5
public static SqlSession createSqlSession(){ return factory.openSession(false); //true为自动提交事务 } public static void closeSqlSession(SqlSession sqlSession){ if(null ! = sqlSession) sqlSession.close(); }
通过以上静态类的方式来保证SqlSessionFactory实例只被创建一次,当然,最佳的解决方案是使用Spring框架来管理SqlSessionFactory的单例模式生命周期。关于和Spring的集成,我们会在后续章节中进行讲解。
注意
设计模式中的单例模式,我们会在后续的SpringMVC章节中具体展开讲解,此处稍作了解即可。
1.3.3 使用SqlSession进行数据持久化操作
1. SqlSession的作用
SqlSession是用于执行持久化操作的对象,类似于JDBC中的Connection。它提供了面向数据库执行SQL命令所需的所有方法,可以通过SqlSession实例直接运行已映射的SQL语句,其方法如图1.22所示。
图1.22 SqlSession提供的方法
2. SqlSession的生命周期和作用域
SqlSession对应着一次数据库会话,由于数据库会话不是永久的,因此SqlSession的生命周期也不是永久的。相反,在每次访问数据库时都需要创建它(注意:并不是说在SqlSession里只能执行一次SQL,它是完全可以执行多次的,但是若关闭了SqlSession,那么就需要重新创建它)。创建SqlSession的方式只有一个,那就是使用SqlSessionFactory对象的openSession()方法。
注意
每个线程都有自己的SqlSession实例,SqlSession实例不能被共享,也不是线程安全的。因此最佳的作用域范围是request作用域或者方法体作用域。
关闭SqlSession是非常重要的。必须确保SqlSession在finally语句块中正常关闭。可以使用下面的标准方式来关闭。
SqlSession session = sqlSessionFactory.openSession(); try { // do work } finally { session.close(); }
3. SqlSession的两种使用方式
(1)通过SqlSession实例来直接执行已映射的SQL语句。例如,通过调用selectList方法执行用户表的查询操作,步骤如下。
➢ 修改UserMapper.xml文件,增加查询用户列表的select节点,关键代码如示例6所示。
示例6
<! -- 查询用户列表 --> <select id="getUserList" resultType="cn.smbms.pojo.User"> select * from smbms_user </select>
➢ 修改测试类UserMapperTest.java,调用selectList方法执行查询操作,关键代码如示例7所示。
示例7
@Test public void testGetUserList(){ SqlSession sqlSession = null; List<User> userList = new ArrayList<User>(); try { sqlSession = MyBatisUtil.createSqlSession(); //调用selectList方法执行查询操作 userList = sqlSession.selectList( "cn.smbms.dao.user.UserMapper.getUserList"); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); }finally{ MyBatisUtil.closeSqlSession(sqlSession); } for(User user: userList){ logger.debug("testGetUserList userCode: " + user.getUserCode() + " and userName: " + user.getUserName()); } }
(2)基于mapper接口方式操作数据。修改上一个演示示例,步骤如下。
➢ 创建绑定映射语句的接口UserMapper.java,并提供接口方法getUserList(),关键代码如示例8所示。该接口称为映射器。注意:接口的方法必须与SQL映射文件中SQL语句的ID一一对应。
示例8
package cn.smbms.dao.user; import java.util.List; import cn.smbms.pojo.User; public interface UserMapper { /** * 查询用户列表 * @return */ public List<User> getUserList(); }
➢ 修改测试类UserMapperTest.java,调用getMapper(Mapper.class)执行Mapper接口方法来实现对数据的查询操作,关键代码如示例9所示。
示例9
@Test public void testGetUserList(){ SqlSession sqlSession = null; List<User> userList = new ArrayList<User>(); try { sqlSession = MyBatisUtil.createSqlSession(); //调用getMapper(Mapper.class)执行Mapper接口方法 userList = sqlSession.getMapper(UserMapper.class).getUserList(); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); }finally{ MyBatisUtil.closeSqlSession(sqlSession); } for(User user: userList){ logger.debug("testGetUserList userCode: " + user.getUserCode() + " and userName: " + user.getUserName()); } }
注意
第一种方式是旧版本的MyBatis提供的操作方式,现在也可以正常工作;第二种方式是MyBatis官方推荐使用的,其表达方式也更加直白,代码更加清晰,类型更加安全,也不用担心易错的字符串字面值以及强制类型转换。
技能训练
上机练习2——在超市订单管理系统中实现供应商表的查询操作
需求说明
(1)在上机练习1搭建的环境中,使用MyBatis实现对供应商表的查询操作(查询出全部数据)。
(2)要求编写工具类MyBatisUtil.java,获取SqlSessionFactory实例。
(3)要求分别使用两种方式(通过SqlSession实例直接运行已映射的SQL语句;基于mapper接口方式操作数据)实现对数据的操作,并对比两种方式的区别。
提示
(1)修改SQL映射文件ProviderMapper.xml,增加select元素节点,编写查询语句。
(2)编写MyBatisUtil.java,在静态代码块中实现SqlSessionFactory对象的创建,并且在该类中增加创建SqlSession和关闭SqlSession的静态方法。
(3)创建绑定映射语句的Mapper接口:ProviderMapper.java。
(4)修改测试类ProviderMapperTest.java,按照两种方式分别实现对数据的操作,并在后台运行输出结果。