ibatis开发指南
2013年12月07日
⁄ 综合
⁄ 共 10079字 ⁄ 字号
小 中 大
- 相对Hibernate和Apache OJB 等“一站式”ORM解决方案而言,ibatis 是一种“半自动化”的ORM实现。
-
- 所谓“半自动”,可能理解上有点生涩。纵观目前主流的ORM,无论Hibernate 还是Apache OJB,都对数据库结构提供了较为完整的封装,提供了从POJO 到数据库表的全套映射机制。程序员往往只需定义好了POJO 到数据库表的映射关系,即可通过Hibernate或者OJB 提供的方法完成持久层操作。程序员甚至不需要对SQL 的熟练掌握,Hibernate/OJB 会根据制定的存储逻辑,自动生成对应的SQL 并调用JDBC 接口加以执行。
-
- 大多数情况下(特别是对新项目,新系统的开发而言),这样的机制无往不利,大有一统天下的势头。但是,在一些特定的环境下,这种一站式的解决方案却未必灵光。
-
- 在笔者的系统咨询工作过程中,常常遇到以下情况:
-
- 1. 系统的部分或全部数据来自现有数据库,处于安全考虑,只对开发团队提供几条Select SQL(或存储过程)以获取所需数据,具体的表结构不予公开。
-
- 2. 开发规范中要求,所有牵涉到业务逻辑部分的数据库操作,必须在数据库层由存储过程实现(就笔者工作所面向的金融行业而言,工商银行、中国银行、交通银行,都在开发规范中严格指定)
-
- 3. 系统数据处理量巨大,性能要求极为苛刻,这往往意味着我们必须通过经过高度优化的SQL语句(或存储过程)才能达到系统性能设计指标。
-
- 面对这样的需求,再次举起Hibernate 大刀,却发现刀锋不再锐利,甚至无法使用,奈何?恍惚之际,只好再摸出JDBC 准备拼死一搏……,说得未免有些凄凉,直接使用JDBC进行数据库操作实际上也是不错的选择,只是拖沓的数据库访问代码,乏味的字段读取操作令人厌烦。
-
- “半自动化”的ibatis,却刚好解决了这个问题。
-
- 这里的“半自动化”,是相对Hibernate等提供了全面的数据库封装机制的“全自动化”ORM 实现而言,“全自动”ORM 实现了POJO 和数据库表之间的映射,以及SQL 的自动生成和执行。而ibatis 的着力点,则在于POJO 与SQL之间的映射关系。也就是说,ibatis并不会为程序员在运行期自动生成SQL 执行。具体的SQL 需要程序员编写,然后通过映射配置文件,将SQL所需的参数,以及返回的结果字段映射到指定POJO。
-
- 使用ibatis 提供的ORM机制,对业务逻辑实现人员而言,面对的是纯粹的Java对象,这一层与通过Hibernate 实现ORM 而言基本一致,而对于具体的数据操作,Hibernate会自动生成SQL 语句,而ibatis 则要求开发者编写具体的SQL 语句。相对Hibernate等“全自动”ORM机制而言,ibatis 以SQL开发的工作量和数据库移植性上的让步,为系统设计提供了更大的自由空间。作为“全自动”ORM 实现的一种有益补充,ibatis 的出现显得别具意义。
-
- IBATIS Developer’s Guide Version 1.0
-
- September 2, 2004 So many open source projects. Why not Open your Documents?
-
- ibatis Quick Start
-
- 准备工作
-
- 1. 下载ibatis软件包(http:
-
- 2. 创建测试数据库,并在数据库中创建一个t_user 表,其中包含三个字段:
-
- Ø id(int)
-
- Ø name(varchar)
-
- Ø sex(int)。
-
- 3. 为了在开发过程更加直观,我们需要将ibatis日志打开以便观察ibatis运作的细节。
-
- ibatis 采用Apache common_logging,并结合Apache log4j 作为日志输出组件。在CLASSPATH 中新建log4j.properties配置文件,内容如下:
-
- log4j.rootLogger=DEBUG, stdout
-
- log4j.appender.stdout=org.apache.log4j.ConsoleAppender
-
- log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
-
- log4j.appender.stdout.layout.ConversionPattern=%c{1} - %m%n
-
- log4j.logger.java.sql.PreparedStatement=DEBUG
-
- 构建ibatis基础代码
-
- ibatis 基础代码包括:
-
- 1. ibatis 实例配置
-
- 一个典型的配置文件如下(具体配置项目的含义见后):
-
- <?xml version="1.0" encoding="UTF-8" ?>
-
- <!DOCTYPE sqlMapConfig
-
- PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN"
-
- "http://www.ibatis.com/dtd/sql-map-config-2.dtd">
-
- <sqlMapConfig>
-
- <settings
-
- cacheModelsEnabled="true"
-
- enhancementEnabled="true"
-
- lazyLoadingEnabled="true"
-
- errorTracingEnabled="true"
-
- maxRequests="32"
-
- maxSessions="10"
-
- maxTransactions="5"
-
- useStatementNamespaces="false"
-
- />
-
- <transactionManager type="JDBC">
-
- IBATIS Developer’s Guide Version 1.0
-
- September 2, 2004 So many open source projects. Why not Open your Documents?
-
- <dataSource type="SIMPLE">
-
- <property name="JDBC.Driver"
-
- value="com.p6spy.engine.spy.P6SpyDriver"/>
-
- <property name="JDBC.ConnectionURL"
-
- value="jdbc:mysql://localhost/sample"/>
-
- <property name="JDBC.Username" value="user"/>
-
- <property name="JDBC.Password" value="mypass"/>
-
- <property name="Pool.MaximumActiveConnections"
-
- value="10"/>
-
- <property name="Pool.MaximumIdleConnections" value="5"/>
-
- <property name="Pool.MaximumCheckoutTime"
-
- value="120000"/>
-
- <property name="Pool.TimeToWait" value="500"/>
-
- <property name="Pool.PingQuery" value="select 1 from
-
- ACCOUNT"/>
-
- <property name="Pool.PingEnabled" value="false"/>
-
- <property name="Pool.PingConnectionsOlderThan"
-
- value="1"/>
-
- <property name="Pool.PingConnectionsNotUsedFor"
-
- value="1"/>
-
- </dataSource>
-
- </transactionManager>
-
- <sqlMap resource="com/ibatis/sample/User.xml"/>
-
- </sqlMapConfig>
-
- 2. POJO(Plain Ordinary Java Object)
-
- 下面是我们用作示例的一个POJO:
-
- public class User implements Serializable {
-
- private Integer id;
-
- private String name;
-
- private Integer sex;
-
- private Set addresses = new HashSet();
-
-
-
- public User() {
-
- }
-
- public Integer getId() {
-
- return this.id;
-
- }
-
- public void setId(Integer id) {
-
- this.id = id;
-
- }
-
- public String getName() {
-
- return this.name;
-
- }
-
- public void setName(String name) {
-
- this.name = name;
-
- }
-
- public Integer getSex() {
-
- return this.sex;
-
- }
-
- public void setSex(Integer sex) {
-
- this.sex = sex;
-
- }
-
- }
-
- 3. 映射文件
-
- 与Hibernate 不同。因为需要人工编写SQL 代码,ibatis 的映射文件一般采用手动编写(通过Copy/Paste,手工编写映射文件也并没想象中的麻烦)。
-
- 针对上面POJO 的映射代码如下:
-
- <?xml version="1.0" encoding="UTF-8"?>
-
- <!DOCTYPE sqlMap
-
- PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN"
-
- "http://www.ibatis.com/dtd/sql-map-2.dtd">
-
- <sqlMap namespace="User">
-
- <typeAlias alias="user" type="com.ibatis.sample.User"/>
-
- <select id="getUser"
-
- parameterClass="java.lang.String"
-
- resultClass="user">
-
- <![CDATA[
-
- select
-
- name,
-
- sex
-
- from t_user
-
- IBATIS Developer’s Guide Version 1.0
-
- September 2, 2004 So many open source projects. Why not Open your Documents?
-
- where name = #name#
-
- ]]>
-
- </select>
-
- <update id="updateUser"
-
- parameterClass="user">
-
- <![CDATA[
-
- UPDATE t_user
-
- SET
-
- name=#name#,
-
- sex=#sex#
-
- WHERE id = #id#
-
- ]]>
-
- </update>
-
- <insert id="insertUser"
-
- parameterClass="user"
-
- >
-
- INSERT INTO t_user (
-
- name,
-
- sex)
-
- VALUES (
-
- #name#,
-
- #sex#
-
- )
-
- </insert>
-
- <delete id="deleteUser"
-
- parameterClass="java.lang.String">
-
- delete from t_user
-
- where id = #value#
-
- </delete>
-
- </sqlMap>
-
- 从上面的映射文件可以看出,通过<insert>、<delete>、<update>、<select>四个节点,我们分别定义了针对TUser 对象的增删改查操作。在这四个节点中,我们指定了对应的SQL 语句,以update节点为例:
-
- ……
-
- <update id="updateUser" ⑴
-
- parameterClass="user"> ⑵
-
- <![CDATA[ ⑶
-
- UPDATE t_user ⑷
-
- SET (
-
- IBATIS Developer’s Guide Version 1.0
-
- September 2, 2004 So many open source projects. Why not Open your Documents?
-
- name=#name#, ⑸
-
- sex=#sex# ⑹
-
- )
-
- WHERE id = #id# ⑺
-
- ]]>
-
- </update>
-
- ……
-
- ⑴ ID
-
- 指定了操作ID,之后我们可以在代码中通过指定操作id 来执行此节点所定
-
- 义的操作,如:
-
- sqlMap.update("updateUser",user);
-
- ID设定使得在一个配置文件中定义两个同名节点成为可能(两个update节
-
- 点,以不同id区分)
-
- ⑵ parameterClass
-
- 指定了操作所需的参数类型, 此例中update 操作以
-
- com.ibatis.sample.User 类型的对象作为参数,目标是将提供的User
-
- 实例更新到数据库。
-
- parameterClass="user"中,user为“com.ibatis.sample.User”
-
- 类的别名,别名可通过typeAlias节点指定,如示例配置文件中的:
-
- <typeAlias alias="user" type="com.ibatis.sample.User"/>
-
- ⑶ <![CDATA[……]]>
-
- 通过<![CDATA[……]]>节点,可以避免SQL 中与XML 规范相冲突的字符对
-
- XML映射文件的合法性造成影响。
-
- ⑷ 执行更新操作的SQL,这里的SQL 即实际数据库支持的SQL 语句,将由
-
- ibatis填入参数后交给数据库执行。
-
- ⑸ SQL中所需的用户名参数,“#name#”在运行期会由传入的user对象的name
-
- 属性填充。
-
- ⑹ SQL 中所需的用户性别参数“#sex#”,将在运行期由传入的user 对象的
-
- sex属性填充。
-
- ⑺ SQL中所需的条件参数“#id#”,将在运行期由传入的user对象的id属性
-
- 填充。
-
- 对于这个示例,ibatis在运行期会读取id 为“updateUser”的update节点的SQL定义,并调用指定的user对象的对应getter方法获取属性值,并用此属性值,对SQL中的参数进行填充后提交数据库执行。
-
- 此例对应的应用级代码如下,其中演示了ibatis SQLMap的基本使用方法:
-
- String resource ="com/ibatis/sample/SqlMapConfig.xml";
-
- Reader reader;
-
- IBATIS Developer’s Guide Version 1.0
-
- September 2, 2004 So many open source projects. Why not Open your Documents?
-
- reader = Resources.getResourceAsReader(resource);
-
- XmlSqlMapClientBuilder xmlBuilder =
-
- new XmlSqlMapClientBuilder();
-
- SqlMapClient sqlMap = xmlBuilder.buildSqlMap(reader);
-
-
-
- try{
-
- sqlMap.startTransaction();
-
- User user = new User();
-
- user.setId(new Integer(1));
-
- user.setName("Erica");
-
- user.setSex(new Integer(1));
-
- sqlMap.update("updateUser",user);
-
- sqlMap.commitTransaction();
-
- finally{
-
- sqlMap.endTransaction();
-
- }
-
- 其中,SqlMapClient是ibatis运作的核心,所有操作均通过SqlMapClient实例完成。
-
- 可以看出,对于应用层而言,程序员面对的是传统意义上的数据对象,而非JDBC中烦杂的ResultSet,这使得上层逻辑开发人员的工作量大大减轻,同时代码更加清晰简洁。数据库操作在映射文件中加以定义,从而将数据存储逻辑从上层逻辑代码中独立出来。而底层数据操作的SQL可配置化,使得我们可以控制最终的数据操作方式,通过SQL的优化获得最佳的数据库执行效能,这在依赖SQL自动生成的“全自动”ORM机制中是所难以实现的。
-
- ibatis配置
-
-
-
- 结合上面示例中的ibatis配置文件。下面是对配置文件中各节点的说明:
-
- <?xml version="1.0" encoding="UTF-8" ?>
-
- <!DOCTYPE sqlMapConfig
-
- PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN"
-
- "http://www.ibatis.com/dtd/sql-map-config-2.dtd">
-
- <sqlMapConfig>
-
- <settings ⑴
-
- cacheModelsEnabled="true"
-
- enhancementEnabled="true"
-
- lazyLoadingEnabled="true"
-
- errorTracingEnabled="true"
-
- maxRequests="32"
-
- maxSessions="10"
-
- maxTransactions="5"
-
- useStatementNamespaces="false"
-
- />
-
- <transactionManager type="JDBC"> ⑵
-
- <dataSource type="SIMPLE"> ⑶
-
- <property name="JDBC.Driver"
-
- value="com.p6spy.engine.spy.P6SpyDriver"/>
-
- <property name="JDBC.ConnectionURL"
-
- value="jdbc:mysql://localhost/sample"/>
-
- <property name="JDBC.Username" value="user"/>
-
- <property name="JDBC.Password" value="mypass"/>
-
- <property name="Pool.MaximumActiveConnections"
-
- value="10"/>
-
- <property name="Pool.MaximumIdleConnections" value="5"/>
-
- <property name="Pool.MaximumCheckoutTime"
-
- value="120000"/>
-
- <property name="Pool.TimeToWait" value="500"/>
-
- <property name="Pool.PingQuery" value="select 1 from
-
- ACCOUNT"/>
-
- <property name="Pool.PingEnabled" value="false"/>
-
- <property name="Pool.PingConnectionsOlderThan"
-
- value="1"/>
-
- <property name="Pool.PingConnectionsNotUsedFor"
-
- value="1"/>
-
- </dataSource>
-
- IBATIS Developer’s Guide Version 1.0
-
- September 2, 2004 So many open source projects. Why not Open your Documents?
-
- </transactionManager>
-
- <sqlMap resource="com/ibatis/sample/User.xml"/> ⑷
-
- <sqlMap resource="com/ibatis/sample/Address.xml"/>
-
- </sqlMapConfig>
-
- ⑴ Settings 节点
-
- 参数 描述
-
- cacheModelsEnabled 是否启用SqlMapClient上的缓存机制。
-
- 建议设为"true"
-
- enhancementEnabled 是否针对POJO启用字节码增强机制以提升getter/setter的调用效能,避免使用JavaReflect所带来的性能开销。同时,这也为Lazy Loading带来了极大的性能提升。
-
- 建议设为"true"errorTracingEnabled 是否启用错误日志,在开发期间建议设为"true"以方便调试lazyLoadingEnabled 是否启用延迟加载机制,建议设为"true"
-
- maxRequests 最大并发请求数(Statement并发数)
-
- maxTransactions 最大并发事务数
-
- maxSessions 最大Session 数。即当前最大允许的并发
-
- SqlMapClient数。
-
- maxSessions设定必须介于
-
- maxTransactions和maxRequests之间,即
-
- maxTransactions<maxSessions=<
-
- maxRequests
-
- useStatementNamespaces 是否使用Statement命名空间。
-
- 这里的命名空间指的是映射文件中,sqlMap节点
-
- 的namespace属性,如在上例中针对t_user
-
- 表的映射文件sqlMap节点:
-
- <sqlMap namespace="User">
-
- 这里,指定了此sqlMap节点下定义的操作均从
-
- 属于"User"命名空间。
-
- 在useStatementNamespaces="true"的情
-
- 况下,Statement调用需追加命名空间,如:
-
- sqlMap.update("User.updateUser",user);
-
- 否则直接通过Statement名称调用即可,如:
-
- sqlMap.update("updateUser",user);
-
- 但请注意此时需要保证所有映射文件中,
-
- Statement定义无重名。
-
- ⑵ transactionManager节点
-
- trans