MyBatis系列二 开发环境准备

一、下载 MyBatis

  输入网址 https://github.com/mybatis/mybatis-3/releases 进入 MyBatis 的官网,我们就可 下载MyBatis,如图2.1所示。

  我们可以在这里下载到MyBatis所需的jar包和源码包。讲解MyBatis运行原理和插件的时候常常会用到源码的内容。

  使用 MyBatis 项目可以参考 http://mybatis.org/mybatis-3/zh/index.html

  使用 MyBatis-Spring 项目可以参考 http://mybatis.org/spring/zh/index.html。

 

 

 二、搭建幵发环境

  无论使用哪一种Java IDE都可以轻松搭建开发环境。这里以Eclipse为例搭建我们的开发环境。我们打开下载得到的MyBatis开发包就可以得到如图所示的目录。

 

 

 

  这里的jar文件分为两类l类是MyBatis本身的jar包,另一类在lib文件夹里0MyBatis 项目所依赖的jar包,而pdf文件则是它提供的API文档。我们只需要在Eclipse中引入 MyBatis的jar包即可,如图所示。

 

 

 

  这样便完成了 MyBatis的环境搭建,我们便可以在项目中使用MyBatis 了。

三、MyBatis的基本构成

 

  认识往往是从表面现象到内在本质的一个探索过程,所以对于MyBatis的掌握,我们 从认识“表面现象” ——MyBatis的基本构成开始。我们先了解一下MyBatis的核心组件。

  • SqlSessionFactoryBuilder (构造器):它会根据配置信息或者代码来生成 SqlSessionFactory (工厂接口)。
  • SqlSessionFactory:依靠工厂来生成 SqlSession (会话)。
  • SqlSession:是一个既可以发送SQL去执行并返回结果,也可以获取Mapper的接口。
  • SQL Mapper:它是MyBaits新设计的组件,它是由一个Java接口和XML文件(或 注解)构成的,需要给出对应的SQL和映射规则。它负责发送SQL去执行,并返 回结果。

 

用一张图表达它们之间的关联,如图2.4所示。

 

 

 

这里我们不需要马上明白MyBatis的组件内容,先了解它们之间的先后顺序、流程和 基本功能,后面我们会详细讨论它们的用法。

四、构建 SqlSessionFactory

  每个MyBatis的应用都是以SqlSessionFactory的实例为中心的。SqlSessionFactory的实例可以通过SqlSessionFactoryBuilder获得。但是读者们需要注意SqlSessionFactory是一个工厂接口而不是现实类,它的任务是创建SqlSession。SqlSession类似于一个JDBC的 Connection对象。MyBatis提供了两种模式去创建SqlSessionFactory: 一种是XML配置的 方式,这是笔者推荐的方式;另一种是代码的方式。能够使用配置文件的时候,我们要尽 量的使用配置文件,这样一方面可以避免硬编码(hard code), 一方面方便日后配置人员的 修改,避免重复编译代码。

  这里我们的 Configuration 的类全限定名为 org.apache.ibatis.session. Configuration,它在MyBatis中将以一个Configuration类对象的形式存在,而这个对象将存在于整个MyBatis应用的生命期中,以便重复读取和运用。在内存中的数据是计算机系统中读取速度最快的, 我们可以解析一次配置的XML文件保存到Configuration类对象中,方便我们从这个对象 中读取配置信息,性能高。单例占用空间小,基本不占用存储空间,而且可以反复使用。Configuration类对象保存着我们配置在MyBatis的信息。在MyBatis中提供了两个 SqlSessionFactory 的实现类,DefaultSqlSessionFactory SqlSessionManager。不过 SqlSessionManager 目前还没有使用,MyBatis 中目前使用的是 DefaultSqlSessionFactory。让我们看看它们的关系图,如图2.5所示。

 

 

   1、使用XML方式构建

 

  这里我们配置一个简易的XML,包含获取数据库连接实例的数据源(DataSource)、 定事务范围和控制方式的事务管理器(TransactionManager)和映射器(SQL Mapper)。 XML 配置文件的内容后面会详细探讨,这里先给出一个简单的示例,如代码清单所示。

<?xml version=nl.0n encoding=nUTF-8n ?>
<!DOCTYPE configuration
PUBLIC n-//mybatis.org//DTD Config 3.0//EN”
”http://mybatis•org/dtd/mybatis-3-config•dtdn>
<configuration>
<! --定义别名--<typeAliases>
<typeAlias alias=nrolen type=ncom.learn.chapter2.po.Rolen/> </typeAliases>
<! --定义数据库信息,默认使用development数据库构建环境--> <environments default=”development”〉
<environment id="development”〉
。一釆用jdbc事务管理--<transactionManager type=nJDBCn/>
配置数据库链接信息--<dataSource type=nPOOLED11 >
<property name=ndrivern value=ncom.mysql•jdbc.Drivern/> <property name=nurln value=njdbc:mysql://localhost:3306/mybatisn/> <property name=nusernamen value=nrootn/>
<property name=npassword11 value="learnn/>
</dataSource>
</environment>
</environments>
<!--定义映射器--<mappers>
<mapper resource=ncom/learn/chapter2/mapper/roleMapper.xmln/> </mappers>/configuration〉

对上面的配置做一下说明。

•这里配置了一个别名role,它代表com.leam.chapter2.po.Role,这样我们就可以在 MyBatis ±下文中引用它了。

•我们配置了环境内容,它默认使用id是development的环境配置,包含以下两方 面的内容。

(1) 釆用JDBC的事务管理模式。

(2) 数据库的连接信息。

•配置映射器。

  这里引入了一个XML,它的作用是提供SQL和SQL对POJO的映射规则定义,它包 含了映射器里面的信息。MyBatis将解析这个XML,来为我们生成映射器。

现在我们用代码实现创建SqlSessionFactory,如代码清单2-2所示。

     

String resource = "mybatis-config.xml";
Inputstream inputstream = Resources.getResourceAsStream(resource); 
SqlSessionFactory SqlSessionFactory = null; SqlSessionFactory = new SqlSessionFactoryBuilder().build (inputstream);

  这里我们创建了一个XML文件输入流,用SqlSessionFactoryBuilder读取XML的信息 来创建SqlSessionFactory的对象。

  MyBatis的解析程序会将mybatis-config.xml文件配置的信息解析到Configuration 类对象里面,然后利用SqlSessionFactoryBuilder读取这个对象为我们创建SqlSession Factory。

  2、使用代码方式构建

 

  除了使用XML配置的方式创建代码外,也可以使用Java编码来实现,不过并不推荐 这个方式,因为修改环境的时候,我们不得不重新编译代码,这样不利于维护。

不过在本书我们依旧讨论其实现方法。和上面XML方式一样我们也要配置别名、数 据库环境和映射器。MyBatis已经为我们提供好了对象的类和方法,我们只要熟悉它们的 使用即可。首先,构建Configuration类对象。然后,往对象里面注册我们构建 SqlSessionFactory所需要的信息便可。

让我们看看代码是如何现实的,如代码清单所示。

//构建数据库连接池
PooledDataSource dataSource = new PooledDataSource ();
dataSource • setDriver (ncom.mysql . jdbc • Driver11);
dataSource . setUrl (n jdbc :mysql: //localhost: 3306/mybatis,f);
dataSource•setUsername(nrootn);
dataSource.setPassword(Hlearnn);
//构建数据库事务方式
TransactionFactory transactionFactory = new JdbcTransactionFactory();
//创建了数据库运行环境
Environment environment = new Environment ("developmentn, transactionFactory, dataSource);
//构建 Configuration 对象
Configuration configuration = new Configuration(environment);
//注册一个MyBatis上下文别名
configuration.getTypeAliasRegistry().registerAlias(nroleH, Role.class);
//加入一个映射器
configuration.addMapper(RoleMapper.class);
//使用 SqlSessionFactoryBuiIder 构建 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuiIder () .build (configuration);
return sqlSessionFactory;

让我们说明一下上面的代码做了什么。

•初始化了一个数据库连接池。

定义了 JDBC的数据库事务管理方式。

用数据库连接池和事务管理方式创建了一个数据库运行环境,并命名为

development o

•创建了一个Configuration类对象,并把数据库运行环境注册给它。

•注册一个role的别名。

•加入一个映射器。

SqlSessionFactoryBuilder 通过 Configuration 对象创建 SqlSessionFactory。

  显然用代码方式和用XML方式只是换个方法实现而已,其本质并无不同。采用代码 方式一般是在需要加入自己特性的时候才会用到,例如,数据源配置的信息要求是加密的 时候,我们需要把它们转化出来。在大部分的情况下,笔者都不建议你使用这个方式来创 MyBatis SqlSessionFactory 。

 

五、创建 SqISession

  SqlSession是一个接口类,它类似于你们公司前台的美女客服,它扮演着门面的作用, 而真正干活的是Executor接口,你可以认为它是公司的工程师。假设我是客户找你们公司 干活,我只需要告诉前台的美女客服(SqlSession)我要什么信息(参数),要做什么东西,过段时间,她会将结果给我。在这个过程中,作为用户的我所关心的是:

1) 要给美女客服SqlSession)什么信息(功能和参数)。

2) 美女客服会返回什么结果Result)。

而我不关心工程师Executor)是怎么为我工作的,只要前台告诉工程师Executor),工程师就知道如何为我工作,这个步骤对我而言是个黑箱操作。

   MyBatis SqlSession 口的实现类有两个,分别是 DefaultSqlSession SqlSessionManager。这里我们暂时不深入讨论Executor接口及其涉及的其他类,只关心SqlSession 的用法就好。我们构建了 SqlSessionFactory,然后生成MyBatis的门面接口 SqlSession。SqlSession接口类似于一个JDBC中的Connection接口对象,我们需要保证每次用完正常 关闭它,所以正确的做法是把关闭SqlSession接口的代码写在finally语句中保证每次都会关闭SqlSession,让连接资源归还给数据库。如果我们不及时关闭资源,数据库的连接资源将很快被耗尽,系统很快因为数据库资源的匮乏而瘫痪。让我们看看实现的伪代码,如代码清单所示。

//定义 SqlSession
SqlSession sqlSession = null;
try (
//打开SqlSession会话 sqlSession = sqlSessionFactory.openSession();
//some code ..•• sqlSession•commit();
} catch(Exception ex) (    .
System.err.printIn(ex.getMessage()); sqlSession.rollback();
}finally (
//在finally语句中确保资源被顺利关闭
if (sqlSession != null) ( sqlSession.close();
)
}

这样SqlSession就被我们创建出来了,在finally语句中我们保证了它的合理关闭,让 连接资源归还给数据库连接池,以便后续使用。

SqlSession的用途主要有两种。

1) 获取映射器,让映射器通过命名空间和方法名称找到对应的SQL,发送给数据库 执行后返回结果。

2) 直接通过命名信息去执行SQL返回结果,这是iBatis版本留下的方式。在SqlSession 层我们可以通过update> insert、select、delete等方法,带上SQL的id来操作在XML中配置好的SQL,从而完成我们的工作;与此同时它也支持事务,通过commit> rollback方法 提交或者回滚事务。

关于这两种用途我们会在映射器里面进行讨论,这两种用途的优劣我们也会进行研讨。

六、映射器

 

 

 

  映射器是由Java接口和XML文件(或注解)共同组成的,它的作用如下。

•定义参数类型。

•描述缓存。

•描述SQL语句。

•定义查询结果和POJO的映射关系。

  一个映射器的实现方式有两种,一种是通过XML文件方式实现,读者应该记得我们 mybatis-config.xml文件中已经描述了一个XML文件,它是用来生成Mapper的。另外 一种就是通过代码方式实现,在Configuration里面注册Mapper接口(当然里面还需要我 们写入Java注解)。当然它也是MyBatis的核心内容,同时也是最为复杂的。这两种方式都可以实现我们的需求,不过笔者强烈建议使用XML文件配置方式,理由如下。

• Java注解是受限的,功能较少,而MyBatis的Mapper内容相当多,而且相当复杂, 功能很强大,使用XML文件方式可以带来更为灵活的空间,显示出MyBatis功能的强大和灵活。

•如果你的SQL很复杂,条件很多,尤其是存在动态SQL的时候,写在Java文件里 面可读性较差,增加了维护的成本。

  所以本书主要介绍XML文件方式,而事实上使用注解也是可以完成SQL定义的,包 括动态SQL定义。但是使用SQL构造器可读性不佳且工作量巨大、实操性弱,因此本书 不介绍这种方式。

  1、XML文件配置方式实现Mapper

  使用XML文件配置是MyBatis实现Mapper的首选方式。它由一个Java接口和一个 XML文件构成。让我们看看它是如何实现的。

  第一步,给出Java接口,如代码清单所示。

package com.learn•chapter2.mapper;
import com.learn.chapter2.po.Role;
public interface RoleMapper (
  public Role getRole(Long id);
}

  这里我们定义了一个接口,它有一个方法getRole,通过角色编号找到角色对象。

  第二步,给出一个映射XML文件,如代码清单所示。

<?xml version=nl.0" encoding=”UTF-8” ?>
<!DOCTYPE mapper PUBLIC ,f-//mybatis.org//DTD Mapper 3.0//EN”"http://mybatis•org/dtd/mybatis-3-mapper.dtdn>
<mapper namespace="com.learn.chapter2.mapper.RoleMapper">
  <select id=ngetRolen parameterType="long" resultType="role"> 
    select id, role_name as roleName, note from t_role where id = # (id}
  </select> </mapper>

  描述一下上面的XML文件做了什么。

•这个文件是我们在配置文件mybatis-config.xml中配置了的,所以MyBatis会读取这 个配置文件,生成映射器。

定义了一个命名空间为 com.leam.chapter2.mapper.RoleMapper SQL Mapper,这个 命名空间和我们定义的接口的全限定名是一致的。

•用一个select元素定义了一个查询SQL, id为getRole,和我们接口方法是一致的, parameterType则表示我们传递给这条SQL的是一个java.lang.Long型参数,而 resultType则定义我们需要返回的数据类型,这里为role,而role是我们之前注册 com.leam.chapter2.po.Role 的另U 名。

  我们来看看这个P0J0,如代码清单所示。

package com.learn•chapter2.po;
public class Role (
private Long id;
private String roleName;
private String note;
public Long getld() (
return id;
}
public void setld(Long id) (
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) (
this•roleName = roleName;
)
public String getNote() (
return note;
)
public void setNote(String note) (
this.note = note;
}
}

这是一个十分简单的POJO,符合Java Bean的规范。下面是SQL的代码。

select id, role name as roleName, note from t role where role no = #{id}

  #{id}为这条SQL的参数。而SQL列的别名和POJO的属性名称保持一致。那么MyBatis 就会把这条语句查询的结果自动映射到我们需要的POJO属性上,这就是自动映射。我们 可以用SqlSession来获取这个Mapper,代码也比较简单,如代码清单2.8所示。

  这样就完成了 MyBatis的一次查询。

  2、Java注解方式实现Mapper

  Java注解方式实现映射方法不难,只需要在接口中使用Java注解,注入SQL即可。 我们不妨看看这个接口,如代码清单所示。

package com.learn.chapter2.mapper;
import org.apache.ibatis.annotations.Select;
import com.learn.chapter2.po.Role;
public interface RoleMapper2 (
  @Select (value="select id, role name as roleName, note from t_role where id = #(id}")
  public Role getRole(Long id);
}

  这里笔者解释一下,我们使用了@Select注解,注入了和XML 一样的select元素,这 MyBatis就会读取这条SQL,并将参数id传递进SQL。同样使用了别名,这样MyBatis会为我们自动映射,得到我们需要的Role对象。这里看起来比XML简单,但是现实中我 们遇到的SQL远比例子复杂得多。如果多个表的关联、多个查询条件、级联、条件分支等 显然这条SQL就会复杂得多,所以笔者并不建议读者使用这种方式。比如代码清单所示的这条SQL。

select u.id as userid, u.username, u.mobile, u.email, r.id as roleid, r•role_name from
t_username u left join t_user_role ur on u.id = ur.user_id
left join t_role r on ur.role_id = r.id
where 1=1
and u.username like concat (1 % 1, #(username}z 1 % 1)
and u.mobile like concat(1 % 1 , #{mobile}9 1 % 1)
and u.email like concat (f % 1z #(email}9 1 % 1)
and r.role name like concat(1 % 1, #(roleName), '%')

  如果我们只要写这条SQL还可以接受,但是如果我们还需要根据上下文判断where语句后面的条件语句,那么显然代码会相当的复杂。我们需要判断username是否为空,如果为空我们就不以username作为条件,这时候SQL就不要出现下面的语句。

and u.username like concat%, #{username},'')

同样的,如果mobile> email> role name都需要这样判断,都写在Java文件的注解里 面,会十分复杂,造成可读性下降。当然如果你的系统很简单,使用注释方式也不失为一个好办法,毕竟简易许多。我们只需要加入这一段代码即可注册这个接口为映射器,如下所示。

configuration.addMapper(RoleMapper2.class);

3、一些疑问

  在MyBatis中保留着iBatis,通过“命名空间(namespace)+SQL id”的方式发送SQL 返回数据的形式,而不需要去获取映射器,以下面的代码为例。

 

Role role=sqlSession.selectOne ("com.learn•chapter2.mapper.RoleMapper.getRole","role_no_l" —— ——> 1L;

 

  如果MyBatis上下文中只有一个SQL的id为getRole,那么我们将代码简写为:

Role role = sqlSession.selectOne ("getRole","role_no_l" -- --> 1L;

  注意,当SQL的id有两个或两个以上getRole的时候,第二种省略的办法就会失败。系统异常就会提示你写出“命名空间+SQLid”的全路径模式才可以。其实它们大同小异, 都是发送SQL并返回需要的结果,而MyBatis 一样会根据"com.leam.chapter2.mapper.RoleMapper.getRole"找到需要执行的接口和方法,进而找到对应的SQL,传递参数 "role_no_1" - -> 1L到SQL中,返回数据,完成一次查询。

  那么困惑是我们需要Mapper吗?答案是肯定的,Mapper是一个接口,相对而言它可 以进一步屏蔽SqlSession这个对象,使得它具有更强的业务可读性。因此笔者强烈建议釆 用映射器方式编写代码,其好处主要有两点。

  • sqlSession.selectOne是功能性代码,长长的字符串比较晦涩难懂,不包含业务逻辑 的含义,不符合面向对象的规范,而对于roleMapper.getRole这样的才是符合面向 对象规范的编程,也更符合业务的逻辑。

  •使用Mapper方式,IDE可以检查Java语法,避免不必要的错误。

  这是MyBatis特有的接口编程模式,而iBatis只能通过SqlSession用SQL的id过滤SQL 去执行。

  我们使用的仅仅是Java接口和一个XML文件(或者注解)去实现Mapper, Java接口 不是实现类,对于Java语言不熟悉的读者肯定会十分疑惑,一个没有实现类的接口怎么能 够运行呢?其实它需要运用到Java语言的动态代理去实现,而实现Java语言的动态代理的 方式有多种。这里我们还是集中于它的用法,所以可以这样理解:我们会在MyBatis上下 文中描述这个接口,而MyBatis会为这个接口生成代理类对象,代理对象会根据“接口全 路径+方法名”去匹配,找到对应的XML文件(或注解)去完成它所需要的任务,返回我 们需要的结果。

关于SqlSession和Mapper是MyBatis的核心内容和难点,它内部远远没有我们目前看 到的那么简单,只是在入门阶段我们暂时不需要讨论它的实现方式,知道它的作用和用法 就可以了。

七、生命周期

 

  我们在前面章节讨论了 MyBatis的主要组件和它们的基本用法,而现实中要想写出高效的 程序只掌握Mybatis的基本用法是远远不够的。我们还要掌握它们的生命周期,这是十分重要的,尤其是在Web应用,Socket连接池等多线程场景中,如果我们不了解MyBatis组件的 生命周期可能带来很严重的并发问题。这节的任务是正确理解SqlSessionFactoryBuilder> SqlSessionFactory> SqlSessionMapper的生命周期,并且重构上面的代码,使MyBatis 能够高效的工作。这对于MyBatis应用的正确性和高性能是极其重要的,我们必须掌握 它们。

1、SqISessionFactoryBuilder

  SqlSessionFactoryBuilder 是利用 XML 或者 Java 编码获得资源来构建 SqlSessionFactory 的,通过它可以构建多个SessionFactory。它的作用就是一个构建器,一旦我们构建了 SqlSessionFactory,它的作用就已经完结,失去了存在的意义,这时我们就应该毫不犹豫的 废弃它,将它回收。所以它的生命周期只存在于方法的局部,它的作用就是生成 SqlSessionFactory 对象。

2、SqlSessionFactory

  SqlSessionFactory的作用是创建SqlSession,而SqlSession就是一个会话,相当于JDBC 中的Connection对象。每次应用程序需要访问数据库,我们就要通过SqlSessionFactory SqlSession,所以SqlSessionFactory应该在MyBatis应用的整个生命周期中。而如果我们 多次创建同一个数据库的SqlSessionFactory,则每次创建SqlSessionFactory会打开更多的数 据库连接(Connection)资源,那么连接资源就很快会被耗尽。因此SqlSessionFactory的责 任是唯一的,它的责任就是创建SqlSession,所以我们果断釆用单例模式。如果我们采用多例,那么它对数据库连接的消耗是很大的,不利于我们统一的管理,这样便嗅到了代码的 坏味道。所以正确的做法应该是使得每一个数据库只对应一个SqlSessionFactory,管理好 数据库资源的分配,避免过多的Connection被消耗。

3、SqlSession

  SqlSession是一个会话,相当于JDBC的一个Connection对象,它的生命周期应该是 在请求数据库处理事务的过程中。它是一个线程不安全的对象,在涉及多线程的时候我们 需要特别的当心,操作数据库需要注意其隔离级别,数据库锁等高级特性。此外,每次创 建的SqlSession都必须及时关闭它,它长期存在就会使数据库连接池的活动资源减少,对系统性能的影响很大。正如前面的代码一样,我们往往通过finally语句块保证我们正确 的关闭SqlSession0它存活于一个应用的请求和操作,可以执行多条SQL,保证事务的一致性。

4、Mapper

  Mapper是一个接口,而没有任何实现类,它的作用是发送SQL,然后返回我们需要的结果,或者执行SQL从而修改数据库的数据,因此它应该在一个SqlSession事务方法之内, 是一个方法级别的东西。它就如同JDBC中的一条SQL语句的执行,它最大的范SqlSession是相同的。尽管我们想一直保存着Mapper,但是你会发现它很难控制,所以尽 量在一个SqlSession事务的方法中使用它们,然后废弃掉。

  有了上面的叙述,我们己经清楚了 MyBatis组件的生命周期,如图2.6所示。

 

 

 

  到这里我们比较完整地描述了 MyBatis框架和它所涉及的组件及其生命周期,接下来 我们来构建一个简单的实例,以加深对MyBatis基础框架的理解。

八、实例

  这里做一个实例,它可以使我们熟悉MyBatis主要组件的用法。我们需要满足MyBatis各个组件的生命周期。首先需要生成SqlSessionFacotry单例,然后让它生成SqlSession,进而拿到映射器中来完成我们的业务逻辑。确定文件所需要放置的路径,以便在上下文中读取配置文件,如图2.7所示。

各个实例文件的作用,如表2-1所示

 

 

  首先,提供log4j的配置文件,如代码清单2-11所示。它为我们打出MyBatis的运行 轨迹,给我们调试MyBatis应用带来了极大的帮助。

log4j.rootLogger=DEBUG , stdout
log4j•logger.org.mybatis=DEBUG
log4j.appender.stdout=org.apache.Iog4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.Iog4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p %d %C: %m%n

  其次,构建SessionFactory,我们需要配置文件,如代码清单所示。

<?xml version=nl.0" encoding=nUTF-8n?>
<!DOCTYPE configurationPUBLIC H-//mybatis.org//DTD Config 3.0//EN”
"http://mybatis•org/dtd/mybatis-3-config•dtd”>
<configuration>
<typeAliases>
<typeAlias alias=Hrolen type=Hcom. learn . chapter2 .po. Role,f/>
</typeAliases>
<environments defau 11=11 deve 1 opmentn>
<environment id=Hdevelopmentn>
<transactionManager type=HJDBCH>
<property name= HautoCommitn value=,,falsen/>
</transactionManager>
<dataSource type=11 POOLED11 >
<property name=,,driverH value=,*00111.mysql . jdbc • DriverH/> <property name=nurln value=fljdbc:mysql://localhost:3306/mybatisH/> <property name=,fusernamen value=nrootff/>
<property name=npasswordn value=nlearnn/> </dataSource>
</environment>
</environments>
<mappers>
<mapper resource=Hcom\learn\chapter2\mapper\roleMapper•xml,f/> </mappers>
</configuration>

  最后,构建SqlSessionFactory,并且给出创建SqlSession的方法,这里我们利用配置文 mybatis-config.xml完成SqlSessionFactory的构建。这里和上面有些变化,我们不妨先看看代码如何现实,如代码清单所示。

package com.learn.chapter2.util;
import java.io•lOException; import java•io.Inputstream;
import java.util.logging.Level; import java.util•logging.Logger;

org.apache.ibatis•io•Resources;
org•apache.ibatis.session.SqlSession;
org.apache•ibatis•session.SqlSessionFactory;
org.apache.ibatis.session.SqlSessionFactoryBuilder;
class SqlSessionFactoryUtil (
//SqlSessionFactory 对象
private static SqlSessionFactory SqlSessionFactory = null;
〃类线程锁
private static final Class CLASS_LOCK = SqlSessionFactoryUtil.class;
*私有化构造参数
★ /
private SqlSessionFactoryUtil() ()
*构建 SqlSessionFactory
★/
public static SqlSessionFactory initSqlSessionFactory() ( String resource = nmybatis-config • xml,f;
Inputstream inputstream = null;
try (
inputstream = Resources.getResourceAsStream(resource); } catch (lOException ex) (
Logger.getLogger(SqlSessionFactoryUtil•class•getName()).log(Level•SEVEREz null, ex);
}
synchronized(CLASS_LOCK) (
if (SqlSessionFactory == null) ( sqlSessionFactory=new SqlSessionFactoryBuilder().build Stream);
}
}
return SqlSessionFactory;
打开 SqlSession
public static SqlSession openSqlSession() ( if (SqlSessionFactory == null) ( initSqlSessionFactory();
return sqlSessionFactory.openSession();
}
}

  解释一下代码的含义,正如生命周期描述的一样,我们希望SqlSessionFactory对于一 个数据库而言只有一个实例,于是我们希望它是单例。现在我们学习一下代码如何实现单例模式。

  构建SessionFactory单例是通过方法initSqlSessionFactory实现的。首先,将构造方法 设置为私有(private),其目的是避免使用者使用new的方式去创建多个对象。然后,我们 使用synchronized对SqlSessionFactoryUtil类加锁,其目的是避免在多线程环境(MyBatis 多用于多线程环境,比如Web服务器,Socket请求等)中,多次初始化造成对象的不唯一。某一个对象在应用中承担唯一责任的时候使用单例模式,而本例中SqlSessionFactory 的唯一责任就是为我们创建SqlSession,所以采用单例模式。单例模式的好处在于可以重复使用这个唯一对象,而对象在内存中读取和运行速度都比较快,同时节约内存。我们的办 法往往是把构造方法私有化,并给一个静态(static)方法,让其返回唯一单例,而在多线 程环境初始化单例,往往需要加线程锁以避免类对象被多次初始化,正如本例一样。

  我们还实现了 openSqlSession方法,利用构建好的SqlSessionFactory创建SqlSession, 为将来所用奠定基础。

  这里我们要给一个POJO——Role.java,如代码清单所示,它就是一个很普通JavaBean。

package com.learn•chapter2.po;
public class Role {
private Long id;
private String roleName;
private String note;
public Long getld() (
return id;
)
public void setld(Long id) ( this.id = id;
}
public String getRoleName() (
return roleName;
}
public void setRoleName(String roleName) (
this•roleName = roleName;
)
public String getNote() (
return note;
)
public void setNote(String note) (
this.note = note;
}
}

  我们还需要一个映射器的描述,让我们编写XML映射文件,如代码清单所示。

<?xml version=,f 1.0” encoding=nUTF-8n ?>
<!DOCTYPE mapper
PUBLIC n-//mybatis•org//DTD Mapper 3.0//EN”
nhttp://mybatis.org/dtd/mybatis-3-mapper.dtdn>
<mapper namespace=ncom.learn•chapter2.mapper.RoleMappern>
<select id=,,getRole" parameterType=nlongf, resultType=nrolen> select id, role_name as roleName, note from t_role where id = # {id}
</select>
<insert id=ninsertRolef, parameterType=nrolen>
insert into t_role(role_name, note) values (#(roleName), #(note))
</insert>
<delete id=ndeleteRolen parameterType=,,longn> delete from t_role where id = #(id)
</delete>
</mapper> .

这里我们定义了 3条SQL,它们的作用有3个。

•查询一个角色对象。

•插入角色。

•删除角色表的数据。

这时候需要定义一个接口,注意接口的方法要和XML映射文件的id保持一致,于是得出下面的接口,如代码清单所示。

package com.learn.chapter2.mapper;
import com.learn•chapter2.po.Role;
public interface RoleMapper {
  public Role getRole(Long id);
  public int deleteRole(Long id);
  public int insertRole(Role role);
} 

为此我们已经构建好SqlSessionFactory,同时也提供了创建SqlSession的方法和映射 器,完成了对应的3个方法。现在我们可以利用这些东西完成下面两个任务了。

(1) 插入一^角色(Role)对象。

(2) 删除一个编号为1L的角色对象。

这里我们使用Chapter2Main.java类完成我们需要完成的任务,如代码清单所示。

package com.learn•chapter2.main;
import java.io.lOException; java.io•Inputstream;
import org•apache.ibatis.datasource.pooled.PooledDataSource;
import org.apache.ibatis•io.Resources;
import org.apache•ibatis.mapping.Environment;
import org•apache.ibatis.session.Configuration;
import org•apache•ibatis.session•SqlSession;
import org.apache.ibatis•session.SqlSessionFactory;
import org.apache.ibatis.session•SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache•ibatis.transaction.jdbc•JdbcTransactionFactory;
import com.learn.chapter2.mapper•RoleMapper;
import com.learn.chapter2.po.Role;
import com.learn.chapter2.util.SqlSessionFactoryUtil;
public class Chapter2Main{
    public static void main(String[] args) throws lOException {
      SqlSession sqlSession = null;*
    try (
      sqlSession = SqlSessionFactoryUtil.openSqlSession ();
      RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class); Role role = new Role();
      role.setRoleName(HtestNamen);
      role.setNote(HtestNoteH);
      roleMapper.insertRole(role);
      roleMapper.deleteRole(IL); sqlSession.commit();
    ) catch(Exception ex) ( System.err.printIn(ex.getMessage ());
    sqlSession.rollback();
    } finally (
      if (sqlSession != null) ( sqlSession.close();
    }
  }
}    

至此我们的代码完成了,让我们用Java Application的形式运行一下Chapter2Main.javao 我们可以看到如下结果。

DEBUG 2016-02-01    14:48:10,528 org.apache•ibatis•transaction.jdbc.
JdbcTransaction:Setting autocommit to false on JDBC Connection[com. mysql.jdbc•JDBC4Connection@7e0b0338]
DEBUG 2016-02-01 14:48:10,531 org. apache. ibatis . logging, jdbc . BaseJdbcLogger: ==> Preparing: insert into t_role(role_namez note) values (?, ?)
DEBUG    2016-02-01    14:48:10,563
JdbcLogger: ==> Parameters: testName
DEBUG    2016-02-01    14:48:10,564
JdbcLogger:<==Updates:1
DEBUG 2016-02-01    14:48:10,564
JdbcLogger: ==> Preparing: delete
DEBUG 2016-02-01    14:48:10,565
JdbcLogger:==> Parameters: 1(Long)
DEBUG 2016-02-01    14:48:10,566 org•apache•ibatis•logging.jdbc•BaseJdbc
Logger:<==Updates:1
DEBUG 2016-02-01    14:48:10,566 org.apache•ibatis•transaction.jdbc.
JdbcTransaction: Committing JDBC Connection [com.mysql•jdbc.JDBC4Connec tion@7e0b0338]

  这样Log4j为我们打印出了 MyBatis的运行轨迹,最为重要的是它为我们打印出了运 行的SQL和参数。在调试中这些信息是十分重要的,如果发生异常我们将可以找到问题的 所在,也能找到插入和删除角色的过程,运行完全成功。

 

 

 

 

 

 

 

 

posted @ 2020-09-02 18:58  跃小云  阅读(121)  评论(0编辑  收藏  举报