IBATIS Developer’s Guide Version 1.0
OpenDoc Series’
iBATIS 2.0开发指南
V1.0
作者:夏昕xiaxin(at)https://www.wendangku.net/doc/9918526612.html, So many open source projects. Why not Open your Doc uments? J
文档说明
参与人员:
作者联络
夏昕xiaxin(at)https://www.wendangku.net/doc/9918526612.html,
(at) 为email @ 符号
发布记录
版本日期作者说明
0.0 2004.8.1 夏昕第一版
1.0 2004.9.1 夏昕补充ibatis in Spring部分
OpenDoc版权说明
本文档版权归原作者所有。
在免费、且无任何附加条件的前提下,可在网络媒体中自由传播。
如需部分或者全文引用,请事先征求作者意见。
如果本文对您有些许帮助,表达谢意的最好方式,是将您发现的问题和文档改进意见及时反馈给作者。当然,倘若有时间和能力,能为技术群体无偿贡献自己的所学为最好的回馈。
另外,笔者近来试图就日本、印度的软件开发模式进行一些调研。如果诸位可以赠阅日本、印度软件研发过程中的需求、设计文档以供研究,感激不尽!
ibatis Quick Start (5)
准备工作 (5)
构建ibatis基础代码 (5)
ibatis配置 (11)
ibatis基础语义 (16)
XmlSqlMapClientBuilder (16)
SqlMapClient (16)
SqlMapClient基本操作示例 (16)
OR映射 (19)
ibatis高级特性 (26)
数据关联 (26)
一对多关联 (26)
一对一关联 (28)
延迟加载 (30)
动态映射 (31)
事务管理 (35)
基于JDBC的事务管理机制 (35)
基于JTA的事务管理机制 (36)
外部事务管理 (38)
Cache (39)
MEMORY类型Cache与WeakReference (40)
LRU型Cache (42)
FIFO型Cache (43)
OSCache (43)
相对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 Quick Start
准备工作
1.下载ibatis软件包(https://www.wendangku.net/doc/9918526612.html,)。
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
https://www.wendangku.net/doc/9918526612.html,yout=org.apache.log4j.PatternLayout https://www.wendangku.net/doc/9918526612.html,yout.ConversionPattern=%c{1}-%m%n
log4j.logger.java.sql.PreparedStatement=DEBUG
构建ibatis基础代码
ibatis基础代码包括:
1.ibatis实例配置
value="com.p6spy.engine.spy.P6SpyDriver"/> value="jdbc:mysql://localhost/sample"/> value="10"/> value="120000"/> value="1"/> value="1"/> 2.POJO(P lain Ordinary J ava O bject) 下面是我们用作示例的一个POJO: } public void setId(Integer id) { this.id = id; } public String getName() { return https://www.wendangku.net/doc/9918526612.html,; } public void setName(String name) { https://www.wendangku.net/doc/9918526612.html, = name; } public Integer getSex() { return this.sex; } public void setSex(Integer sex) { this.sex = sex; } } 3.映射文件 与Hibernate不同。因为需要人工编写SQL代码,ibatis的映射文件一般采用手动编写(通过Copy/Paste,手工编写映射文件也并没想象中的麻烦)。 针对上面POJO的映射代码如下: where name = #name# ]]> parameterClass="user"> UPDATE t_user SET name=#name#, sex=#sex# WHERE id = #id# ]]> parameterClass="user" > INSERT INTO t_user ( name, sex) VALUES ( #name#, #sex# ) parameterClass="https://www.wendangku.net/doc/9918526612.html,ng.String"> delete from t_user where id = #value# 从上面的映射文件可以看出,通过 …… parameterClass="user"> ⑵ UPDATE t_user ⑷ SET ( name=#name#, ⑸ sex=#sex# ⑹ ) WHERE id = #id# ⑺ ]]> …… ⑴ID 指定了操作ID,之后我们可以在代码中通过指定操作id来执行此节点所定义的操作,如: sqlMap.update("updateUser",user); ID设定使得在一个配置文件中定义两个同名节点成为可能(两个update节点,以不同id区分) ⑵parameterClass 指定了操作所需的参数类型,此例中update操作以https://www.wendangku.net/doc/9918526612.html,er类型的对象作为参数,目标是将提供的User 实例更新到数据库。 parameterClass="user"中,user为“https://www.wendangku.net/doc/9918526612.html,er” 类的别名,别名可通过typeAlias节点指定,如示例配置文件中的: ⑶ 通过节点,可以避免SQL中与XML规范相冲突的字符对XML映射文件的合法性造成影响。 ⑷执行更新操作的SQL,这里的SQL即实际数据库支持的SQL语句,将由 ibatis填入参数后交给数据库执行。 “#name#”在运行期会由传入的user对象的name ⑸SQL中所需的用户名参数, 属性填充。 ⑹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; reader = Resources.getResourceAsReader(resource); XmlSqlMapClientBuilder xmlBuilder = new XmlSqlMapClientBuilder(); SqlMapClient sqlMap = xmlBuilder.buildSqlMap(reader); //sqlMap系统初始化完毕,开始执行update操作 try{ sqlMap.startTransaction(); User user = new User(); user.setId(new Integer(1)); user.setName("Erica"); user.setSex(new Integer(1)); sqlMap.update("updateUser",user); https://www.wendangku.net/doc/9918526612.html,mitTransaction(); finally{ sqlMap.endTransaction(); } 其中,SqlMapClient是ibatis运作的核心,所有操作均通过SqlMapClient 实例完成。 可以看出,对于应用层而言,程序员面对的是传统意义上的数据对象,而非JDBC 中烦杂的ResultSet,这使得上层逻辑开发人员的工作量大大减轻,同时代码更加清晰简洁。 数据库操作在映射文件中加以定义,从而将数据存储逻辑从上层逻辑代码中独立出来。 而底层数据操作的SQL可配置化,使得我们可以控制最终的数据操作方式,通过SQL的优化获得最佳的数据库执行效能,这在依赖SQL自动生成的“全自动”ORM 机制中是所难以实现的。 ibatis配置 结合上面示例中的ibatis配置文件。下面是对配置文件中各节点的说明: ⑴Settings 节点 参数描述 cacheModelsEnabled是否启用SqlMapClient上的缓存机制。 建议设为"true" enhancementEnabled是否针对POJO启用字节码增强机制以提升 getter/setter的调用效能,避免使用Java Reflect所带来的性能开销。 同时,这也为Lazy Loading带来了极大的性能 提升。 建议设为"true" errorTracingEnabled 是否启用错误日志,在开发期间建议设为"true" 以方便调试 lazyLoadingEnabled是否启用延迟加载机制,建议设为"true" maxRequests 最大并发请求数(Statement并发数) maxTransactions 最大并发事务数 maxSessions 最大Session数。即当前最大允许的并发 SqlMapClient数。 maxSessions设定必须介于 maxTransactions和maxRequests之间,即 maxTransactions maxRequests useStatementNamespaces 是否使用Statement命名空间。 这里的命名空间指的是映射文件中,sqlMap节点 的namespace属性,如在上例中针对t_user 表的映射文件sqlMap节点: 这里,指定了此sqlMap节点下定义的操作均从 属于"User"命名空间。 在useStatementNamespaces="true"的情 况下,Statement调用需追加命名空间,如: sqlMap.update("User.updateUser",use r); 否则直接通过Statement名称调用即可,如: sqlMap.update("updateUser",user); 但请注意此时需要保证所有映射文件中, Statement定义无重名。 ⑵transactionManager节点 transactionManager节点定义了ibatis的事务管理器,目前提供了以下几种选择: ?JDBC 通过传统JDBC https://www.wendangku.net/doc/9918526612.html,mit/rollback实现事务支持。 ?JTA 使用容器提供的JTA服务实现全局事务管理。 ?EXTERNAL 外部事务管理,如在EJB中使用ibatis,通过EJB的部署配置即可实现自 动的事务管理机制。此时ibatis将把所有事务委托给外部容器进行管理。 此外,通过Spring等轻量级容器实现事务的配置化管理也是一个不错的选 择。关于结合容器实现事务管理,参见“高级特性”中的描述。 ⑶dataSource节点 dataSource从属于transactionManager节点,用于设定ibatis运行期使用的DataSource属性。 type属性: dataSource节点的type属性指定了dataSource的实现类型。 可选项目: ?SIMPLE: SIMPLE是ibatis内置的dataSource实现,其中实现了一个简单的数据库连接池机制,对应ibatis实现类为 com.ibatis.sqlmap.engine.datasource.SimpleDataSourceFactory。 ?DBCP: 基于Apache DBCP连接池组件实现的DataSource封装,当无容器提供DataSource服务时,建议使用该选项,对应ibatis实现类为 com.ibatis.sqlmap.engine.datasource.DbcpDataSourceFactory。 ?JNDI: 使用J2EE容器提供的DataSource实现,DataSource将通过指定的JNDI Name从容器中获取。对应ibatis实现类为 com.ibatis.sqlmap.engine.datasource.JndiDataSourceFacto ry。 dataSource的子节点说明(SIMPLE&DBCP): 参数描述 JDBC.Driver JDBC 驱动。 如:org.gjt.mm.mysql.Driver JDBC.ConnectionURL 数据库URL。 如:jdbc:mysql://localhost/sample 如果用的是SQLServer JDBC Driver,需要 在url后追加SelectMethod=Cursor以获得 JDBC事务的多Statement支持。 https://www.wendangku.net/doc/9918526612.html,ername 数据库用户名 JDBC.Password 数据库用户密码 Pool.MaximumActiveConn ections 数据库连接池可维持的最大容量。 Pool.MaximumIdleConnec tions 数据库连接池中允许的挂起(idle)连接数。 以上子节点适用于SIMPLE和DBCP模式,分别针对SIMPLE和DBCP模式的DataSource私有配置节点如下: SIMPLE: 参数描述 Pool.MaximumCheckoutTi me 数据库联接池中,连接被某个任务所允许占用的最大时间,如果超过这个时间限定,连接将被强制收回。(毫秒) Pool.TimeToWait 当线程试图从连接池中获取连接时,连接池中无 可用连接可供使用,此时线程将进入等待状态, 直到池中出现空闲连接。此参数设定了线程所允 许等待的最长时间。(毫秒) Pool.PingQuery 数据库连接状态检测语句。 某些数据库在连接在某段时间持续处于空闲状态 时会将其断开。而连接池管理器将通过此语句检 测池中连接是否可用。 检测语句应该是一个最简化的无逻辑SQL。 如“select 1 from t_user”,如果执行此语句 成功,连接池管理器将认为此连接处于可用状态。Pool.PingEnabled 是否允许检测连接状态。 Pool.PingConnectionsOl derThan 对持续连接时间超过设定值(毫秒)的连接进行检测。 Pool.PingConnectionsNo 对空闲超过设定值(毫秒)的连接进行检测。 tUsedFor DBCP: 参数描述 Pool.MaximumWait 当线程试图从连接池中获取连接时,连接池中无 可用连接可供使用,此时线程将进入等待状态, 直到池中出现空闲连接。此参数设定了线程所允 许等待的最长时间。(毫秒) Pool.ValidationQuery 数据库连接状态检测语句。 某些数据库在连接在某段时间持续处于空闲状态 时会将其断开。而连接池管理器将通过此语句检 测池中连接是否可用。 检测语句应该是一个最简化的无逻辑SQL。 如“select 1 from t_user”,如果执行此语句 成功,连接池管理器将认为此连接处于可用状态。 Pool.LogAbandoned 当数据库连接被废弃时,是否打印日志。 Pool.RemoveAbandonedTi 数据库连接被废弃的最大超时时间 meout Pool.RemoveAbandoned 当连接空闲时间超过 RemoveAbandonedTimeout时,是否将其废 弃。 JNDI由于大部分配置是在应用服务器中进行,因此ibatis中的配置相对简单,下面是分别使用JDBC和JTA事务管理的JDNI配置: 使用JDBC事务管理的JNDI DataSource配置 value="java:comp/env/jdbc/myDataSource"/> value="java:/ctx/con/UserTransaction"/> value="java:comp/env/jdbc/myDataSource"/> ⑷ sqlMap节点 sqlMap节点指定了映射文件的位置,配置中可出现多个sqlMap节点,以指定项目内所包含的所有映射文件。 ibatis基础语义 XmlSqlMapClientBuilder XmlSqlMapClientBuilder是ibatis 2.0之后版本新引入的组件,用以替代1.x 版本中的XmlSqlMapBuilder。其作用是根据配置文件创建SqlMapClient实例。 SqlMapClient SqlMapClient是ibatis的核心组件,提供数据操作的基础平台。SqlMapClient 可通过XmlSqlMapClientBuilder创建: String resource ="com/ibatis/sample/SqlMapConfig.xml"; Reader reader; reader = Resources.getResourceAsReader(resource); XmlSqlMapClientBuilder xmlBuilder = new XmlSqlMapClientBuilder(); SqlMapClient sqlMap = xmlBuilder.buildSqlMap(reader); "com/ibatis/sample/SqlMapConfig.xml"指明了配置文件在CLASSPATH 中的相对路径。XmlSqlMapClientBuilder通过接受一个Reader类型的配置文件句柄,根据配置参数,创建SqlMapClient实例。 SqlMapClient提供了众多数据操作方法,下面是一些常用方法的示例,具体说明文档请参见ibatis java doc,或者ibatis官方开发手册。 SqlMapClient基本操作示例 以下示例摘自ibatis官方开发手册,笔者对其进行了重新排版以获得更好的阅读效果。例1: 数据写入操作(insert, update, delete): sqlMap.startTransaction(); Product product = new Product(); product.setId (1); product.setDescription (“Shih Tzu”); int rows = sqlMap.insert (“insertProduct”, product); https://www.wendangku.net/doc/9918526612.html,mitTransaction(); 例2: 数据查询(select) sqlMap.startTransaction(); Integer key = new Integer (1); Product product = (Product)sqlMap.queryForObject (“getProduct”, key); https://www.wendangku.net/doc/9918526612.html,mitTransaction(); 例3: 在指定对象中存放查询结果(select) sqlMap.startTransaction(); Customer customer = new Customer(); sqlMap.queryForObject(“getCust”, parameterObject, customer); sqlMap.queryForObject(“getAddr”, parameterObject, customer); https://www.wendangku.net/doc/9918526612.html,mitTransaction(); 例4: 执行批量查询(select) sqlMap.startTransaction(); List list = sqlMap.queryForList (“getProductList”, null); https://www.wendangku.net/doc/9918526612.html,mitTransaction(); 例5: 关于AutoCommit //没有预先执行startTransaction时,默认为auto_commit模式 int rows = sqlMap.insert (“insertProduct”, product); 例6:查询指定范围内的数据 sqlMap.startTransaction(); List list = sqlMap.queryForList (“getProductList”, null, 0, 40); https://www.wendangku.net/doc/9918526612.html,mitTransaction(); 例7: 结合RowHandler进行查询(select) public class MyRowHandler implements RowHandler { public void handleRow (Object object, List list) throws SQLException { Product product = (Product) object; product.setQuantity (10000); sqlMap.update (“updateProduct”, product); } } sqlMap.startTransaction(); RowHandler rowHandler = new MyRowHandler(); List list = sqlMap.queryForList (“getProductList”, null, rowHandler); https://www.wendangku.net/doc/9918526612.html,mitTransaction(); 例8: 分页查询(select) PaginatedList list = sqlMap.queryForPaginatedList (“getProductList”, null, 10); list.nextPage(); list.previousPage(); 例9: 基于Map的批量查询(select) sqlMap.startTransaction(); Map map = sqlMap.queryForMap (“getProductList”, null, “productCode”); https://www.wendangku.net/doc/9918526612.html,mitTransaction(); Product p = (Product) map.get(“EST-93”); OR映射 相对Hibernate等ORM实现而言,ibatis的映射配置更为简洁直接,下面是一个典型的配置文件。 可以看到,映射文件主要分为两个部分:模块配置和Statement配置。 模块配置包括: ?typeAlias节点: 定义了本映射文件中的别名,以避免过长变量值的反复书写,此例中通过typeAlias节点为类"https://www.wendangku.net/doc/9918526612.html,er"定义了一个别名"user",这样在本配置文件的其他部分,需要引用"https://www.wendangku.net/doc/9918526612.html,er"类时,只需以其别名替代即可。 ?cacheModel节点 定义了本映射文件中使用的Cache机制: 这里申明了一个名为"userCache"的cacheModel,之后可以在Statement申明中对其进行引用: