一.ibatis简介
相对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 的出现显 得别具意义。
二.快速入门
1.想要使用ibatis,就需要先导入ibatis-2.3.0.677.jar包,当然少不了数据库驱动jar包classes12.jar。如下图:
2.在工程src目录下,建立ibatis.xml文件,这里以dept表为例,连接的是Oracle数据库
参考代码如下:
<?xml version="1.0"encoding="UTF-8"?>
<!DOCTYPE sqlMapConfig
PUBLIC "-//ibatis.apache.org//DTDSQL Map Config 2.0//EN"
"http://ibatis.apache.org/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="oracle.jdbc.driver.OracleDriver"/>
<property name="JDBC.ConnectionURL" value="jdbc:oracle:thin:@localhost:1521:orcl"/>
<property name="JDBC.Username"value="scott"/>
<property name="JDBC.Password"value="tiger"/>
<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 dept"/>
<property name="Pool.PingEnabled"value="true"/>
<property name="Pool.PingConnectionsOlderThan"value="1"/>
<property name="Pool.PingconnectionsNotUsedFor"value="1"/>
</dataSource>
</transactionManager>
<sqlMap resource="com/siqi/po/dept.ibatis.xml"/>
</sqlMapConfig>
注意:对于ibatis.xml文件中,各项配置标签的解释,请参考同目录下的配置注释.txt,里面有详细解释。
3.我们在com.siqi.po包下建立Dept类和dept.ibatis.xml配置文件,
如下图:
参考代码如下:
publicclass Dept {
privateintdeptno;
private String dname;
private String loc;
publicint getDeptno() {
returndeptno;
}
publicvoid setDeptno(int deptno) {
this.deptno = deptno;
}
public String getDname() {
returndname;
}
publicvoid setDname(String dname) {
this.dname = dname;
}
public String getLoc() {
returnloc;
}
publicvoid setLoc(String loc) {
this.loc = loc;
}
}
dept.ibatis.xml文件,参考代码如下:
<?xml version="1.0"encoding="UTF-8"?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTDSQL Map 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="Dept">
<typeAlias alias="dept" type="com.siqi.po.Dept"/>
<select id="getDeptById"
parameterClass="dept"
resultClass="dept">
<![CDATA[
select dname,loc from dept where deptno=#deptno#
]]>
</select>
<select id="getDept"
parameterClass="dept"
resultClass="dept"
>
<![CDATA[
select deptno,dname,loc from dept
]]>
</select>
<update id="updateDept"
parameterClass="dept">
<![CDATA[
update dept set dname=#dname#,loc=#loc# wheredeptno=#deptno#
]]>
</update>
<insert id="insertDept"
parameterClass="dept">
<![CDATA[
insert into dept values(#deptno#,#dname#,#loc#)
]]>
</insert>
<delete id="deleteDept"
parameterClass="dept">
<![CDATA[
delete from dept where deptno=#deptno#
]]>
</delete>
</sqlMap>
注意:
id:
指定了操作ID,之后我们可以在代码中通过指定操作id来执行节点所定义的操作,可以定义两个同名节点,但id必须不同。它就相当于我们以前的方法名。
举例:
sqlMap.delete( “deleteDept”,dept);
parameterClass:
指定了操作所需的参数类型,上面配置文件中 真正的参数类型应该为:com.siqi.po.Dept
因为,我们在前面设置了<typeAlias alias="dept" type="com.siqi.po.Dept"/>
所以,dept 就为com.siqi.po.Dept的别名。所以在参数类型位置可以直接写dept
<![CDATA[…]>:
通过此节点,可以避免SQL中与XML规范相冲突的字符对XML映射文件的合法性造成影响。
可执行操作的SQL语句:
这里的SQL语句即实际数据库支持的SQL语句,将由iBatis填入参数后交给数据库执行。
SQL中所需要的参数:
参数都以 “ #...# ”标注。
三.测试
下面我们写一个测试类,来简单测试下,经过上述配置,ibatis能否使用
publicclass Test {
publicstaticvoid main(String[] args) {
Reader reader=null;
try {
reader=Resources.getResourceAsReader("ibatis.xml");
} catch (IOException e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
SqlMapClientsqlMap=SqlMapClientBuilder.buildSqlMapClient(reader);
try {
sqlMap.startTransaction();
Dept d=new Dept();
d.setDeptno(80);
d.setDname("hello");
d.setLoc("world");
sqlMap.insert("insertDept",d);增加方法
//sqlMap.delete("deleteDept",d);删除方法
//sqlMap.update("updateDept",d); 修改方法
List<Dept> list=s.queryForList("getDept",d); //查询方法, //查询方法比较特殊。
for(Dept dept:list){
System.out.println(dept.getDeptno()+”,”+dept.getDname()+”,”+dept.getLoc());
}
s.commitTransaction();
s.endTransaction();
} catch (Exception e) {
// TODO Auto-generatedcatch block
e.printStackTrace();
}
}
}
经过上面测试,我们成功啦~!
增加一条数据:
我们再修改编号为88的loc为“jinan“。
代码如下:
效果如下:
我们再来删除部门编号为88的部门
代码如下:
四.拓展
(1)分页查询
queryForList() 方法,有传入四个参数的,再利用自定义标签就可以实现分页查询
举例:
queryForList(“getDept”,d,(当前页-1)*每页大小,每页大小)
代码如下,定义currentPage:当前页,pageSize:每页大小,totalCount为记录总条数.
实现的效果为:
(2)模糊查询
SQL语句为:select* from dept where dname like '%'||#dname#||'%'
配置文件中代码如下:
调用方法时,
这样就可以查出所有部门名称中带“A“的部门
Ps:囧,貌似都带A ⊙﹏⊙b汗
(3)一对多查询.这里我们以dept和emp表为例
1.首先在po里加上Emp类
2.在dept.ibatis.xml中增加以下配置
执行步骤如上,师弟,师妹们一定要注意配置id和resultMap时,起的名字单词要对应一致
别忘记在Dept中加上 emp的集合。而且由于版本问题, iBatis2.0 仅对java.util.List或java.util.Collection类型进行支持。
3.进行测试
这里我们以部门编号为30的部门为例
上述代码无BUG!