对于从事Java EE
的开发人员来说,iBatis
是一个再熟悉不过的持久层框架了,iBatis
可以算是在所有主流的持久层框架中学习成本最低,最容易上手和掌握的框架。虽说其他持久层框架也号称门槛低,容易上手,但是等到你真正使用时会发现,要想掌握并用好它是一件非常困难的事。
iBatis
自从在Apache
软件基金会网站上发布至今,和他的明星兄弟们(Http Server,Tomcat,Struts,Maven,Ant
等等)一起接受者万千 Java 开发者的敬仰。然而在当时,几乎是发布 3.0
版本的同时,iBatis
主页上的一则Apache iBATIS has been retired(退休)
的声明在社区引起了一阵不小的波澜。在Apache
寄居六年之后,iBatis
将代码托管到Google Code
。在声明中给出的主要理由是,和Apache
相比,Google Code
更有利于开发者的协同工作,也更能适应快速发布。于此同时,iBatis
更名为 MyBatis
从iBatis
到MyBatis
,不只是名称上的变化,MyBatis
提供了更为强大的功能,同时并没有损失其易用性,相反,在很多地方都借助于JDK
的泛型和注解特性进行了简化。iBatis
确实该退休了,因为一个更为出色的继任者经过 10
个 Beta
版本的蜕变已然出现在我们的面前。
如果接触过一些常用的Java EE
框架,应该都知道这些框架需要提供一个全局配置文件,用于指定程序正常运行所需的设置和参数信息。而针对常用的持久层框架而言(Hibernate
、JPA
、iBatis
等),则通常需要配置两类文件:一类用于指定数据源、事务属性以及其他一些参数配置信息(通常是一个独立的文件,可以称之为全局配置文件);另一类则用于指定数据库表和程序之间的映射信息(可能不止一个文件,称之为映射文件)。MyBatis
也不例外,虽然其中的一部分可以通过注解的形式进行,但是这两部分内容本身仍是必不可少的。
根据iBatis
的习惯,我们通常把全局配置文件命名为 sqlMapConfig.xml
,文件名本身并没有要求,在 MyBatis
中,也经常会将该文件命名为 Configuration.xml
(读完全文后也许会发现,在iBatis
中经常出现的sqlMap
在MyBatis
中被逐渐淡化了,除了此处,还比如iBatis
配置文件的根元素为
,指定映射文件的元素为
,以及 SqlMapClient
等等,这个变化正说明,iBatis
仅是以 SQL
映射为核心的框架,而在MyBatis
中多以Mapper
、Session
、Configuration
等其他常用ORM
框架中的名字代替,体现的无非是两个方面:首先是为了减少开发者在切换框架所带来的学习成本;其次,MyBatis
充分吸收了其他ORM
框架好的实践,MyBatis
现在已不仅仅是一个SQL
映射框架了)。
mybatis
在全局配置文件中可以配置的信息主要包括如下几个方面:
properties
— 用于提供一系列的键值对组成的属性信息,该属性信息可以用于整个配置文件中。settings
— 用于设置MyBatis
的运行时方式,比如是否启用延迟加载等。typeAliases
— 为Java
类型指定别名,可以在XML
文件中用别名取代Java
类的全限定名。typeHandlers
— 在MyBatis
通过PreparedStatement
为占位符设置值,或者从ResultSet
取出值时,特定类型的类型处理器会被执行objectFactory
— MyBatis
通过ObjectFactory
来创建结果对象。可以通过继承DefaultObjectFactory
来实现自己的ObjectFactory
类。plugins
— 用于配置一系列拦截器,用于拦截映射SQL
语句的执行。可以通过实现Interceptor
接口来实现自己的拦截器。environments
— 用于配置数据源信息,包括连接池、事务属性等。mappers
— 程序中所有用到的SQL
映射文件都在这里列出,这些映射SQL
都被MyBatis
管理MyBatis
会为没有显式设置的元素提供缺省值。一个简单的全局配置文件示例如下:简单的全局配置文件示例
<configuration>
<environments default="demo">
<environment id="demo">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="username" value="root"/>
<property name="password" value="root"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="footmark/mybatis/demo/UserInfoMapper.xml"/>
mappers>
configuration>
有了这些信息,MyBatis
便能够和数据库建立连接,并应用给定的连接池信息和事务属性。MyBatis
封装了这些操作,最终暴露一个 SqlSessionFactory
实例供开发者使用,从名字可以看出来,这是一个创建SqlSession
的工厂类,通过SqlSession
实例,开发者能够直接进行业务逻辑的操作,而不需要重复编写JDBC
相关的样板代码。根据全局配置文件生成SqlSession
的代码如下:
Reader reader = Resources.getResourceAsReader("Configuration.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession sqlSession = sqlSessionFactory.openSession();
可以把上面的三行代码看做是MyBatis
创建SqlSession
的样板代码。其中第一行代码在类路径上加载配置文件,Resources
是MyBatis
提供的一个工具类,它用于简化资源文件的加载,它可以访问各种路径的文件,不过最常用的还是示例中这种基于类路径的表示方式
在完成全局配置文件,并通过MyBatis
获得SqlSession
对象之后,便可以执行数据访问操作了。对于 iBatis/MyBatis
而言,要执行的操作其实就是在映射文件中配置的 SQL
语句。两者的配置基本相同,如下所示:
在映射文件中配置SQL
语句
<mapper namespace="mybatis.demo.UserInfoMapper">
<select id="selectUser" parameterType="int" resultType="mybatis.demo.UserInfo">
select * from UserInfo where userid =#{userid}
select>
mapper>
在iBatis
中,namespace
不是必需的,且它的存在没有实际的意义。在 MyBatis
中,namespace
终于派上用场了,它使得映射文件与接口绑定变得非常自然。
使用SqlSession
执行映射文件中配置的SQL
语句
try
{
UserInfo userinfo = (UserInfo) sqlSession.selectOne ("mybatis.demo.UserInfoMapper.selectUser", 2);
System.out.println(userinfo);
} finally
{
sqlSession.close();
}
需要注意的是,SqlSession
的使用必需遵守上面的格式,即在finally
块中将其关闭。以保证资源得到释放,防止出现内存泄露!
以上就是一个简单而完整的MyBatis
程序。其中涉及了全局配置文件,映射文件,构建SqlSession
对象,执行数据访问操作等四个步骤。下面将针对除构建SqlSession
对象之外的三块内容进行分解。
MyBatis
全局配置文件的改变
MyBatis
全局配置文件的各主要元素基本和iBatis
相同,只是在用法和个别名称上做了调整。元素的意义就不再描述,下面主要讲述针对 iBatis
和 MyBatis
配置文件的主要区别之处。
首先,两个版本的 DTD
约束不同,MyBatis
的DTD
文件已经包含在发布包下的 mybatis-3.0.x.jar
包中。这直接影响到的是,iBatis
配置文件的根元素是
,而MyBatis
使用的是
其次,
的用法发生了改变,之前的格式为:
iBatis
中设置属性的方式
要设置的属性直接以键值对的形式作为
的属性
MyBatis
中设置属性的方式 <settings>
<setting name="props1" value="value1"/>
<setting name="props2" value="value2"/>
……
settings>
iBatis
中配置事务管理器和数据源的方式<transactionManager type="JDBC" >
<dataSource type="SIMPLE">
<property name="JDBC.Driver" value="${driver}"/>
dataSource>
transactionManager>
MyBatis
中配置事务管理器和数据源的方式<environments default="demo">
<environment id="demo">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="JDBC.Driver" value="${driver}"/>
dataSource>
environment>
environments>
通过
来进行数据源管理,主要是为了简化在多套数据源配置之间的切换,比如开发和发布使用不同的配置。
iBatis
中指定映射文件的方式<sqlMap resource=... />
<sqlMap resource=... />
<sqlMap resource=... />
MyBatis
中指定映射文件的方式<mappers>
<mapper resource=... />
<mapper resource=... />
mappers>
上面的这些调整,主要出发点其实并不是使得MyBatis
功能更为强大,而是使配置更为合理,让开发者更容易阅读和理解。
到目前为止,除了XML
形式的全局配置,其实这也不是唯一选择,MyBatis
还提供了通过代码来进行配置的方式:
在MyBatis
中使用代码进行配置
DataSource ds = …… // 获取一个 DataSource
TransactionFactory txFactory = new JdbcTransactionFactory();
Environment env = new Environment("demo", txFactory, ds);
Configuration cfg = new Configuration(env);
cfg.addMapper(UserInfoMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(cfg);
结合前面的配置文件,很容易理解这段代码的意思,故不再赘述。不过,需要注意的是Configuration
的addMapper()
方法,该方法的参数通常是一个接口,可以在接口里面定义若干方法,在方法上使用注解来指定映射的SQL
语句。一个典型的接口定义以及对应的数据访问方法如下:
将映射的SQL
语句与接口中的方法绑定
// 映射 SQL 绑定接口
public interface UserInfoMapper
{
@Select("select * from userinfo where userid = #{userid}")
public UserInfo getUserInfo(int userid);
}
// 接口绑定对应的数据访问方法
try
{
//UserInfo userinfo = (UserInfo) sqlSession.selectOne ("mybatis.demo.UserInfoMapper.selectUser", 2);
UserInfoMapper userinfoMapper = sqlSession.getMapper(UserInfoMapper.class);
UserInfo userinfo = userinfoMapper.getUserInfo(1);
System.out.println(userinfo);
} finally
{
sqlSession.close();
}
MyBatis
映射文件的改变
MyBatis
针对映射文件进行格式调整的地方很多,但大部分仅仅只是名称上的变化,现代的 IDE
都支持联想功能,可以很方便的获取到当前位置可以有哪些元素、哪些属性等。所以这基本不会给开发者造成什么麻烦。
针对映射文件,首先是一系列的属性名称的改变,这些仅仅是名称的改变,用法和含义并没有发生变化:
DTD
约束发生变化,根元素也由iBatis
原来的
调整为MyBatis
的
等元素的iBatis
的parameterClass
属性改为了MyBatis
的 parameterType
属性。
等元素的iBatis
的resultClasss
属性改为了MyBatis
的 resultType
属性。
等元素的 iBatis
的class
属性改为了MyBatis
的type
属性。
元素的iBatis
的columnIndex
属性被移除了。iBatis
的#value#
改为了MyBatis
的#{value}
等元素的jdbcType
属性取值中,原来的iBatis
的 ORACLECURSOR
取值改为了现在的 MyBatis
的CURSOR
,iBatis
的NUMBER
取值改为了 MyBatis
的NUMERIC
。iBatis
配置文件的根元素是
,而MyBatis
使用的是
iBatis
中调用存储过程的方式iBatis/MyBatis
对存储过程的支持一直是值得称道的。之前通过使用
元素进行存储过程的定义,示例如下:<procedure id="getValues" parameterMap="getValuesPM">
{ ? = call pkgExample.getValues(p_id => ?) }
procedure>
MyBatis
中调用存储过程的方式MyBatis
中,
元素已经被移除,通过
、
和
进行定义:<select id="getValues" parameterMap="getValuesPM" statementType="CALLABLE">
{ ? = call pkgExample.getValues(p_id => ?)}
select>
如上所示,通过statementType
属性将该语句标识为存储过程而非普通SQL
语句。
代码层面的改变
通过前面的示例可以看出,MyBatis
在编码中的最大的改变就是将一个最常用的 API
由 SqlMapClient
改为了SqlSessionFactory
。另外,类型处理器接口也由原来的 TypeHandlerCallback
改为了 TypeHandler
。最后 DataSourceFactory
也进行了调整,移动到 org.apache.ibatis.datasource
包下,其中的方法也作了微调。总之,代码层面公开的部分改动较少,不会给开发者造成较大的移植成本。
Iterate
:这属性遍历整个集合,并为List
集合中的元素重复元素体的内容。
Iterate
的属性:
prepend
- 可被覆盖的SQL
语句组成部分,添加在语句的前面
(可选)property
- 类型为java.util.List
的用于遍历的元素(必选)open
- 整个遍历内容体开始的字符串,用于定义括号(可选)close
-整个遍历内容体结束的字符串,用于定义括号(可选)conjunction
- 每次遍历内容之间的字符串,用于定义AND
或OR
(可选)遍历类型为java.util.List(或数组)的元素。
例子:
<iterate prepend=”AND” property=”userNameList” open=”(” close=”)” conjunction=”OR”>
username=#userNameList[]#
iterate>
注意
:使用
时,在List
元素名后面包括方括号[]
非常重要,方括号[]
将对象标记为List
, 以防解析器简单地将List
输出成String
。
在生成该条sql
语句时,
标签中的内容是循环生成的,就拿上面的例子来说,生成的sql
是:(username=xxx1 or username=xxx2 or username=xxx 3)
而不是(username=xxx1 or xxx2 or xxx3)
再举个例子,如下:
id in
<iterate prepend="" property="ids" open="(" close=")" conjunction="," >
#ids[]#
iterate>
其生成的sql
语句是:id in (xx1,xx2,xx3,.....)
,括号中的(包括括号)是
标签生成的。
标签虽然是遍历整个集合的,但他也不象我们在java
中用到的for
一样,一次一次的循环生成标签中的内容,而是一次性利用list或数组生成整个可运行的sql语句。 就好比它生成的sql
不可能象这样:id in (xx1) id in (xx2) ,id in (xx3),.....
属性关键字 | 含义 |
---|---|
|
如果参数相等于值则查询条件有效 |
|
如果参数不等于值则查询条件有效 |
|
如果参数大于值则查询条件有效 |
|
如果参数等于值则查询条件有效 |
|
如果参数小于值则查询条件有效。如下所示: ADOLESCENT = ‘TRUE’ |
|
如果参数有使用则查询条件有效 |
|
如果参数没有使用则查询条件有效 |
|
如果参数为NULL则查询条件有效 |
|
如果参数不为NULL则查询条件有效 |
|
如果参数为空则查询条件有效 |
|
如果参数不为空则查询条件有效。参数的数据类型为Collection、String 时参数不为NULL或“”。如下所示: FIRST_NAME=#firstName# |
|
如果参数类不为NULL则查询条件有效 |
|
如果参数对象不存在(空) 有效: EMPLOYEE_TYPE = ‘DEFAULT’ |
在动态标签中:
isequal
:作比较用相当于String
里的equal
方法,property
:指定比较属性的名称,compareValue
:表示要比较的参数,prepend
:表示追加比较条件