直接上示例代码,相关细节说明,详见代码注释
首先是存储数据库连接信息的jdbc.properties
driver = oracle.jdbc.OracleDriver
url = jdbc:oracle:thin:@127.0.0.1:1521:xuanyu
username = scott
password = xuanyu
其次是ibatis2的全局配置文件//src//SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
<!-- <properties/>应该放到<settings/>之前 -->
<properties resource="jdbc.properties"/>
<!--
cacheModelsEnabled:是否启用SqlMapClient上的缓存机制,建议设为"true"
enhancementEnabled:是否针对POJO启用字节码增强机制以提升getter/setter的调用效能
其可避免使用反射所带来的性能开销,同时提升延迟加载的性能,建议设为"true"
lazyLoadingEnabled:是否启用延迟加载机制,建议设为"true"
errorTracingEnabled:是否启用错误日志,开发期间可以设为"true"以便调试
maxRequests:最大并发请求数,即Statement并发数
maxTransactions:最大并发事务数
maxSessions:最大Session数,即最大允许的并发SqlMapClient数,它设定必须介于maxRequests和maxTransactions之间
useStatementNamespaces:是否使用Statement命名空间。这里的命名空间指的是User.xml映射文件中,<sqlMap/>节点的namespace属性
如User.xml中的<sqlMap namespace="User">,即指定了此sqlMap节点下定义的操作均从属于"User"命名空间
当其值为true时,Statement调用需追加命名空间,如sqlMapClient.insert("User.saveBySequence", user)
否则直接通过Statement名称调用即可,如sqlMapClient.insert("saveBySequence", user);
但请注意此时需要保证所有的映射文件中,Statement定义不能重名
-->
<settings cacheModelsEnabled="true"
enhancementEnabled="true"
lazyLoadingEnabled="true"
errorTracingEnabled="true"
maxRequests="32"
maxSessions="10"
maxTransactions="5"
useStatementNamespaces="false"/>
<!--
定义ibatis的事务管理器
目前提供了三种选择:JDBC、JTA、EXTERNAL
JDBC:通过传统JDBC Connection.commit/rollback实现事务支持
JTA:使用容器提供的JTA服务实现全局事务管理
EXTERNAL:外部事务管理。如在EJB中使用ibatis,通过EJB的部署配置即可实现自动的事务管理机制
此时ibatis将把所有事务委托给外部容器进行管理
此外:也可以通过Spring等轻量级容器实现事务的配置化管理
-->
<transactionManager type="JDBC">
<!--
设定ibatis运行期使用的DataSource属性,type属性用于指定dataSource的实现类型
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.JndiDataSourceFactory
-->
<dataSource type="SIMPLE">
<property name="JDBC.Driver" value="${driver}" />
<!-- 若是MSSQL,需要在url后追加SelectMethod=Cursor以获得JDBC事务的多Statement支持 -->
<property name="JDBC.ConnectionURL" value="${url}" />
<property name="JDBC.Username" value="${username}" />
<property name="JDBC.Password" value="${password}" />
<!-- 数据库连接池可维持的最大容量 -->
<property name="Pool.MaximumActiveConnections" value="10"/>
<!-- 数据库连接池中允许的挂起(idle)连接数 -->
<property name="Pool.MaximumIdleConnections" value="5"/>
<!-- ****************************************************************************************** -->
<!-- **********************************以下配置为SIMPLE模式独享********************************** -->
<!-- ****************************************************************************************** -->
<!-- 数据库连接池中,连接被某个任务所允许占用的最大时间,如果超过这个时间限定,连接将被强制收回。单位为毫秒 -->
<property name="Pool.MaximumCheckoutTime" value="120000"/>
<!-- 当线程试图从连接池中获取连接时,连接池中无可用连接可供使用,此时线程将进入等待状态,直到池中出现空闲连接 -->
<!-- 此参数设定了线程所允许等待的最长时间。单位为毫秒 -->
<property name="Pool.TimeToWait" value="500"/>
<!-- 数据库连接状态检测语句 -->
<!-- 某些数据库连接在某段时间持续处于空闲状态时会将其断开。而连接池管理器将通过此语句检测池中连接是否可用 -->
<!-- 检测语句应是最简化的无逻辑SQL。若语句执行成功,则认为此连接处于可用状态 -->
<property name="Pool.PingQuery" value="select 1 from t_user"/>
<!-- 是否允许检测连接状态 -->
<property name="Pool.PingEnabled" value="false"/>
<!-- 对持续连接时间超过设定值(毫秒)的连接进行检测 -->
<property name="Pool.PingConnectionsOlderThan" value="1"/>
<!-- 对空闲超过设定值(毫秒)的连接进行检测 -->
<property name="Pool.PingConnectionsNotUsedFor" value="1"/>
<!-- ****************************************************************************************** -->
<!-- ***********************************以下配置为DBCP模式独享*********************************** -->
<!-- ****************************************************************************************** -->
<!--
等同于SIMPLE模式下的Pool.TimeToWait
<property name="Pool.MaximumWait" value="500"/>
等同于SIMPLE模式下的Pool.PingQuery
<property name="Pool.ValidationQuery" value="select 1 from t_user"/>
当数据库连接被废弃时,是否打印日志
<property name="Pool.LogAbandoned" value="true"/>
数据库连接被废弃的最大超时时间
<property name="Pool.RemoveAbandonedTimeout" value="150"/>
当连接空闲时间超过RemoveAbandonedTimeout时,是否将其废弃
<property name="Pool.RemoveAbandoned" value="true"/>
-->
</dataSource>
</transactionManager>
<!--
JNDI由于大部分配置是在应用服务器中进行,因此ibatis中的配置相对简单
使用JDBC事务管理的JNDI DataSource配置
<transactionManager type="JDBC">
<dataSource type="JNDI">
<property name="DataSource" value="java:comp/env/jdbc/myDataSource"/>
</dataSource>
</transactionManager>
使用JTA事务管理的JNDI DataSource配置
<transactionManager type="JTA">
<property name="UserTransaction" value="java:/ctx/con/UserTransaction"/>
<dataSource type="JNDI">
<property name="DataSource" value="java:comp/env/jdbc/myDataSource"/>
</dataSource>
</transactionManager>
-->
<!-- 指定了映射文件的位置,配置中允许出现多个sqlMap节点,以指定工程中的所有映射文件 -->
<sqlMap resource="com/xuanyuv/model/User.xml" />
</sqlMapConfig>
下面是用到的实体类User.java
package com.xuanyuv.model;
import java.util.Date;
public class User {
private Integer id;
private String name;
private Date birth;
/* 三个属性的setter和getter略 */
public User(){}
public User(String name, Date birth){
this.name = name;
this.birth = birth;
}
public User(Integer id, String name, Date birth){
this.id = id;
this.name = name;
this.birth = birth;
}
@Override
public String toString() {
return "id=" + id + "/tname=" + name + "/tbirth=" + birth;
}
}
下面是与实体类位于同一包下的映射文件User.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="User">
<!-- 这样在本配置文件的其他部分,需要引用"com.xuanyuv.model.User"类时,只需以其别名替代即可 -->
<typeAlias alias="user" type="com.xuanyuv.model.User"/>
<!--
Statement配置
它包含了数个与SQLStatement相关的节点:statement、insert、delete、update、select、procedure
其中<statement>最为通用,它可以替代其余的所有节点。除<statement>之外的节点各自对应SQL中的同名操作
-->
<insert id="save" parameterClass="user">
insert into T_USER(id, name, birth) values(#id#, #name#, #birth#)
</insert>
<insert id="saveBySequence" parameterClass="user">
<selectKey resultClass="int" keyProperty="id">
select SEQUENCE_T_USER.nextVal as id from dual
</selectKey>
insert into T_USER(id, name, birth) values(#id#, #name#, #birth#)
</insert>
<update id="update" parameterClass="user">
update T_USER set name=#name#, birth=#birth# where id=#id#
</update>
<delete id="deleteById" parameterClass="Integer">
delete from T_USER where id = #id#
</delete>
<!--
以下是<select>标签的几个常用属性
parameterClass:用于指定执行该节点中的操作时,需要传入的参数的类型
比如parameterClass="user",其中"user"就是"com.xuanyuvv.model.Person"类的别名
别名是通过<typeAlias>指定的,如本文的<typeAlias alias="user" type=".....User"/>
resultClass:指定返回的结果类型。建议输入别名,避免每次重复书写冗长的类名
cacheModel:表明通过id为"findById"的Select statement获取的数据,使用"userCache"进行缓存
以后再用此Statement进行数据查询时,会直接从缓存中读取查询结果,而无需再去数据库查询
resultMap:结果映射,需结合resultMap节点对映射关系加以定义
parameterMap:参数映射,需结合parameterMap节点对映射关系加以定义
对于存储过程之外的statement而言,建议使用parameterClass作为参数配置方式
一方面避免了参数映射配置工作,另一方面其性能表现也更加出色
-->
<select id="findById" parameterClass="int" resultClass="user">
<![CDATA[
select * from T_USER where id = #id#
]]>
</select>
<select id="findAll" resultClass="user">
select * from T_USER
</select>
<select id="findByName11" parameterClass="java.lang.String" resultClass="user">
select * from T_USER where name like '$name$'
</select>
<select id="findByName22" parameterClass="java.lang.String" resultClass="user">
select * from T_USER where name like '%$name$%'
</select>
</sqlMap>
下面是配置日志打印的//src//log4j.properties
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.logger.com.ibatis=debug
log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=debug
log4j.logger.com.ibatis.common.jdbc.ScriptRunner=debug
log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=debug
log4j.logger.java.sql.Connection=debug
log4j.logger.java.sql.Statement=debug
log4j.logger.java.sql.PreparedStatement=debug,stdout
下面是JUnit4.x的单元测试类UserDaoIbatisTest.java
package com.xuanyuv.test;
import java.io.IOException;
import java.io.Reader;
import java.sql.Date;
import java.sql.SQLException;
import java.util.List;
import org.junit.BeforeClass;
import org.junit.Test;
import com.ibatis.common.resources.Resources;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapClientBuilder;
import com.xuanyuv.model.User;
/**
* 本程序用到了ibatis-2.3.4.726.jar和log4j-1.2.16.jar
* Created by 玄玉<https://www.xuanyuv.com/> on 2011/04/05 23:26.
*/
public class UserDaoIbatisTest {
//SqlMapClient是ibatis的核心组件,提供数据操作的基础平台
private static SqlMapClient sqlMapClient;
/**
* 读取ibatis配置文件,连接数据库,并创建SqlMapClient
*/
@BeforeClass
public static void readConfig() throws IOException {
Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml");
sqlMapClient = SqlMapClientBuilder.buildSqlMapClient(reader);
reader.close();
}
/**
* 添加用户,其ID是通过实体类自行设定的
*/
@Test
public void save() throws SQLException {
User user = new User(22, "张起灵", Date.valueOf("2011-03-26"));
sqlMapClient.insert("save", user);
}
/**
* 添加用户,其ID是由序列生成的
*/
@Test
public void saveBySequence() throws SQLException {
User user = new User("闷油瓶", Date.valueOf("1988-03-26"));
sqlMapClient.insert("saveBySequence", user);
System.out.println("id = " + user.getId());
}
/**
* 更新用户信息
* ibatis有一个自动化的事务提交机制,即根据当前的调用环境,自动判断操作是否需要自动提交
* 若未显式调用SqlMapClient.startTransaction(),则ibatis会默认当前的数据库操作为AutoCommit=true
* <p>
* 这里的所谓"自动判定",并不是指ibatis去检查当前是否已开启事务,从而判断当前数据库连接是否设定为自动提交
* 实际上,在执行update语句时,sqlMapClient会检查当前的Session是否已经关联了某个数据库连接
* 如果没有,则取一个数据库连接,将其AutoCommit属性设为true,然后执行update操作,执行完之后又释放这个连接
* 即,下面的两次update实际上先后获取了两个数据库连接,即,下面的两次update并不是基于同一个JDBC Connection
* 所以:对于多条SQL组合的一个JDBC事务操作而言,必须使用start、commit和endTransaction以实现整体事务的原子性
* </p>
*/
@Test
public void update() throws SQLException {
User user1 = new User(22, "陈文锦", Date.valueOf("1988-03-26"));
sqlMapClient.update("update", user1);
User user2 = new User(22, "霍玲", Date.valueOf("1988-03-12"));
sqlMapClient.update("update", user2);
}
/**
* 删除指定ID的用户资料
*/
@Test
public void deleteById() throws SQLException {
sqlMapClient.delete("deleteById", 22);
}
/**
* 查询指定ID的用户资料
*/
@Test
public void findById() throws SQLException {
User user = (User)sqlMapClient.queryForObject("findById", 2);
System.out.println(user);
}
/**
* 查询所有的用户信息
*/
@Test
public void findAll() throws SQLException {
List<User> list = sqlMapClient.queryForList("findAll");
for(User user : list){
System.out.println(user);
}
}
/**
* 根据提供的用户名,模糊查询用户信息
*/
@Test
public void findByName11() throws SQLException {
List<User> list = sqlMapClient.queryForList("findByName11", "%寻%");
for(User user : list){
System.out.println(user);
}
}
@Test
public void findByName22() throws SQLException {
List<User> list = sqlMapClient.queryForList("findByName22", "寻");
for(User user : list){
System.out.println(user);
}
}
}
最后是数据库脚本文件t_user.sql
-- Oracle 11g
-- Create table
create table t_user(
id number,
name varchar2(10),
birth date
);
-- Create sequence
create sequence sequence_t_user increment by 1 start with 1 nomaxvalue nocycle;
-- Add data
insert into t_user values(sequence_t_user.nextval, '玄寻玉', to_date('2011-3-22', 'yyyy-mm-dd'));
insert into t_user values(sequence_t_user.nextval, '李寻欢', to_date('2011-3-23', 'yyyy-mm-dd'));
insert into t_user values(sequence_t_user.nextval, '易天寻', to_date('2011-3-24', 'yyyy-mm-dd'));
insert into t_user values(sequence_t_user.nextval, '寻无迹', to_date('2011-3-25', 'yyyy-mm-dd'));