在开发过程中最能帮助你的是什么?是框架,一个优秀的框架可以极大的提高你的效率。struts给了我们什么?MVC的实现,国际化、灵活。还有很多。不过,在一个通常的WEB应该中,是不能缺少数据库的,而struts在这方面并没有给我们提供什么有效的帮助。通常情况下我们做这个的时候有几个选择。
最直接的当然是JDBC啊,自己写connect、statment和resultset等等的代码,结果是累死自己。
然后一种方法是EJB,EJB确实是一个好东西,可惜在很多场合用不上,起码它很烦,速度很慢
还有一种选择就是JDO及类似的东西。最著名是free的应该是castor,hibernate等。
现在我们又多了一种选择,就是ibatis Db Layer,它的主页是http://www.ibatis.com,为什么说它好,让我们来看看作者自己的说明吧,使用ibatis的理由
10、知道怎样操作10种以上的数据库
9 、可配置的caching(包括从属)
8、支持DataSource、local transaction managemen和global transaction
7、简单的XML配置文档
6、支持Map, Collection, List和简单类型包装(如Integer, String)
5、支持JavaBeans类(get/set 方法)
4、支持复杂的对象映射(如populating lists, complex object models)
3、对象模型从不完美(不需要修改)
2、数据模型从不完美(不需要修改)
1、你已经知道SQL,为什么还要学习其他东西
另外一点它是100% Open Source Freeware
下面我们就来看一看,做一个简单的ibatis需要哪一些工作。然后一步一步深入探索它的强大功能。在实践中来看它的好处在哪里。
在ibatis的网站上有一个它自己的petstore,在我个人看来是最简洁的petstore了,跟struts1.0结合。应该说是一个不错的教程。希望大家能够好好研究。当然,作为入门。我们先来做一个简单的程序。所采用的库嘛,就仍然是用petstore的了。数据库也是选择Oracle(为什么选择Oracle,很多朋友不理解,怎么不用mysql之类的呢,一个主要的原因是个人爱好,Oracle毕竟是商业数据库,有它的强大之处,另外在linux下它也是免费的,:)。废话少说,先用jpetstore3.1提供的ddl建立一个库吧。
然后在eclipse里建立一个ibatisDemo的工程。加入ibatis提供的库,建立相就的目录。看一下一个最简单的程序需要哪一些文件。我们选择一个简单表,即Category这个表的操作来演示功能
文件路径 |
功能说明 |
备注 |
config\properties\petstore.properties |
可变参数配置文件,所有根据环境不同的参数都放在这里 |
|
config\properties\simple\dao.xml |
dao配置文件,主要存放dao对象和数据池设置 |
|
config\properties\simple\sql-map-config-storedb.xml |
真正的核心配置文件 |
|
config\sqlmap\Category.xml |
存放Category的数据操作的SQL |
|
com.ewuxi.champion.exception.DaoException.java |
自定义的Exception类,不用说了吧 |
|
com.ewuxi.champion.Service.java |
一个服务类,用于初始化 |
|
com.ewuxi.champion.persistence.dao.DaoCommon |
Dao层的统一操作类,提供一些公共函数 |
|
com.ewuxi.champion.persistence.dao.CategoryDb |
Category的操作类 |
|
com.ewuxi.champion.persistence.vo.Category |
valueObject 值对象 |
|
com.ewuxi.champion.persistence.dao.CategoryDbTest |
单元测试类 |
|
下面一个一个文件详细说明
petstore.properties
##################################################################
SIMPLE CONFIGURATION SECTION
##################################################################
## SimpleDataSource properties
## Use only if useSimpleConfiguration=true
SimpleDriver=oracle.jdbc.OracleDriver
SimpleUrl=jdbc:oracle:thin:@10.0.0.5:1521:champion
SimpleUsername=pet
SimplePassword=pet
|
这个不用解释,就是数据库的连接串,如果你在自己的机器上运行,当然这些都是需要改的。
dao.xml
<?xml version="1.0"
encoding="UTF-8"?>
<!DOCTYPE dao-config
PUBLIC "-//iBATIS.com//DTD DAO Configuration
1.0//EN"
"http://www.ibatis.com/dtd/dao.dtd">
<dao-config>
<context name="StoreDaoManager"
default="true">
<!-- Configure the transaction pool. -->
<transaction-pool implementation="com.ibatis.db.dao.jdbc.SqlMapDaoTransactionPool">
<property name="sql-map-config-file"
value="properties/simple/sql-map-config-storedb.xml"/>
</transaction-pool>
</context>
</dao-config>
|
上面这一段也是很简单的,连一个dao也没有配置,也就是说,用的是默认的Dao。其中<context
name="StoreDaoManager" default="true">表示它是默认的数据库配置(它可以根据名字不同同时连接几个数据库的)。
sql-map-config-storedb.xml
<?xml version="1.0"
encoding="UTF-8"?>
<!DOCTYPE sql-map-config
PUBLIC "-//iBATIS.com//DTD SQL Map Config 1.0//EN"
"http://www.ibatis.com/dtd/sql-map-config.dtd">
<sql-map-config>
<properties resource="properties/petstore.properties"
/>
<settings
maxExecute="0"
maxExecutePerConnection="0"
maxTransactions="0"
cacheModelsEnabled="true"
statementCacheSize="175"
useBeansMetaClasses="false"
useGlobalTransactions="false" />
<datasource name="jpestoreSimple"
factory-class="com.ibatis.db.sqlmap.datasource.DbcpDataSourceFactory"
default="true" >
<property name="JDBC.Driver" value="${SimpleDriver}"/>
<property name="JDBC.ConnectionURL"
value="${SimpleUrl}"/>
<property name="JDBC.Username" value="${SimpleUsername}"/>
<property name="JDBC.Password" value="${SimplePassword}"/>
<property name="Pool.MaximumActiveConnections"
value="15"/>
<property name="Pool.MaximumIdleConnections"
value="15"/>
<property name="Pool.MaximumWait"
value="1000"/>
</datasource>
<sql-map resource="sqlmap/Category.xml"
/>
</sql-map-config>
|
这里真正实现了数据库连接,我们使用的是dbcp的连接池。JDBC的配置大家都很熟了。${SimpleDriver}就是指的前面petstore.properties中的SimpleDriver的内容。
而<sql-map resource="sqlmap/Category.xml" />则表示包含Category.xml这个文件。
Category.xml
<?xml version="1.0"
encoding="UTF-8"?>
<!DOCTYPE sql-map
PUBLIC "-//iBATIS.com//DTD SQL Map 1.0//EN"
"http://www.ibatis.com/dtd/sql-map.dtd">
<sql-map name="Category">
<result-map name="result" class="com.ewuxi.champion.persistence.vo.Category">
<property name="categoryId" column="CATID"
columnIndex="1" />
<property name="name" column="NAME"
columnIndex="2"/>
<property name="description" column="DESCN"
columnIndex="3"/>
</result-map>
<mapped-statement name="findByPrimaryKeyCategoryDao"
result-map="result">
select CATID, NAME, DESCN from CATEGORY where CATID = #categoryId#
</mapped-statement>
<dynamic-mapped-statement name="findCategoryDao"
result-map="result">
select CATID, NAME, DESCN from CATEGORY
<dynamic prepend="where">
<isNotNull prepend="and" property="categoryId"
>
CATID = #categoryId#
</isNotNull>
<isNotNull prepend="and"
property="name" >
NAME = #name#
</isNotNull>
<isNotNull prepend="and"
property="description">
DESCN = #description#
</isNotNull>
</dynamic>
</dynamic-mapped-statement>
<mapped-statement name="findCategoryDaoCount"
result-class="java.lang.Integer">
select count(1) as value from CATEGORY
</mapped-statement>
<!-- =============================================
mapped-statement
============================================= -->
<dynamic-mapped-statement name="updateByPrimaryKeyCategoryDao">
update CATEGORY
<dynamic prepend="set">
<isNotNull prepend=","
property="name" >
NAME = #name#
</isNotNull>
<isNotNull prepend=","
property="description">
DESCN = #description#
</isNotNull>
</dynamic>
where
CATID =#categoryId#
</dynamic-mapped-statement>
<!-- =============================================
mapped-statement
============================================= -->
<mapped-statement name="deleteByPrimaryKeyCategoryDao">
delete from CATEGORY
where CATID =#categoryId#
</mapped-statement>
<!-- =============================================
OPTIONAL EXPLICIT PARAMETER MAP
============================================= -->
<parameter-map name="insert-params">
<property name="categoryId"/>
<property name="name" type="VARCHAR"/>
<property name="description" type="VARCHAR"/>
</parameter-map>
<!-- =============================================
MAPPED STATEMENTS - w/Explicit Parameter Map
============================================= -->
<mapped-statement name="insertCategoryDao"
parameter-map="insert-params" >
insert into CATEGORY (
CATID,NAME,DESCN)
values (
?,?,?
)
</mapped-statement>
</sql-map>
|
上述文件就是真正的SQL所存在的地方。
<result-map name="result" class="com.ewuxi.champion.persistence.vo.Category">
<property name="categoryId" column="CATID"
columnIndex="1" />
<property name="name" column="NAME"
columnIndex="2"/>
<property name="description" column="DESCN"
columnIndex="3"/>
</result-map>
这一段的内容表示返回的对象是com.ewuxi.champion.persistence.vo.Category,也就是我们值对象。当执行查询的时候,dblay会封装出一个Category对象或者一个Category的list集合。其中数据列CATID就对象javabean的categoryId值。name是自定义的
|
<mapped-statement name="findByPrimaryKeyCategoryDao"
result-map="result">
select CATID, NAME, DESCN from CATEGORY where CATID = #categoryId#
</mapped-statement>
此处result-map="result"表示返回结果以后,就会参照前面的result来返回对象。select
CATID, NAME, DESCN from CATEGORY where CATID = #categoryId#标准的SQL,只不过这一点CATID
= #categoryId#有些不同,#categoryId#表示传递一个Category对象时,Dblay会自动取得categoryId的值来执行SQL
|
再来看一个
<dynamic-mapped-statement name="updateByPrimaryKeyCategoryDao">
update CATEGORY
<dynamic prepend="set">
<isNotNull prepend=","
property="name" >
NAME = #name#
</isNotNull>
<isNotNull prepend=","
property="description">
DESCN = #description#
</isNotNull>
</dynamic>
where
CATID =#categoryId#
</dynamic-mapped-statement>
这个地方就体现了dblayer的强大之处,动态SQL。平常我们经常碰到的情况是根据不同的情况,执行的SQL有一点点不一样。写在程序里,要写不少的if
then之类的,在这里,dbLayer给你一下解决了。比如在这里,我们三个值都是String对象,所以通过isNotNull就可以实现几种不同的update了,比如,如果我只想修改DESCN这个字段,只要传过去的Category对象只有categoryId和description有值,就会生成update
CATEGORY set DESCN = #description# where CATID =#categoryId#。同样如果传递的对象只有categoryId和name有值,就会生成update
CATEGORY set NAME = #name# where CATID =#categoryId#。是否很强大?:)
|
前面这两种,参数的传递方式是内置参数,也就是CATID
=#categoryId#这种,大家可能不太习惯,那就看一看标准的写法吧。
<!-- =============================================
OPTIONAL EXPLICIT PARAMETER MAP
============================================= -->
<parameter-map name="insert-params">
<property name="categoryId"/>
<property name="name" type="VARCHAR"/>
<property name="description" type="VARCHAR"/>
</parameter-map>
<!-- =============================================
MAPPED STATEMENTS - w/Explicit Parameter Map
============================================= -->
<mapped-statement name="insertCategoryDao"
parameter-map="insert-params" >
insert into CATEGORY (
CATID,NAME,DESCN)
values (
?,?,?
)
</mapped-statement>
</sql-map>
|
这里面的insert语句想来大家都很熟了吧?这个时候怎么取得参数呢?关键在于这里parameter-map="insert-params",表示会读取<parameter-map
name="insert-params">的设置,而这个设置也不用多解释了吧,就是按顺序,三个?分别对应三个值。还能指明他们的数据类型。
下面来看看Service.java
package com.ewuxi.champion;
import java.io.Reader;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.ibatis.common.resources.Resources;
import com.ibatis.db.dao.DaoManager;
/**
* @author champion
*
* To change the template for this generated type comment go
to
* Window - Preferences - Java - Code Generation - Code and
Comments
*/
public class Service {
static Log log = LogFactory.getLog(Service.class);
public static void initSet() {
try {
String resource = null;
resource = "properties/simple/dao.xml";
log.info("Using SIMPLE configuration. (" +
resource + ")");
Reader reader = Resources.getResourceAsReader(resource);
DaoManager.configure(reader);
} catch (Exception e) {
throw new RuntimeException(
"Could not initialize BaseLogic. Cause: " + e);
}
}
}
|
一个静态方法,从resource文件中读出配置,最后用DaoManager.configure(reader);完成配置。
DaoCommon
public static Dao getDefautDao(){
return DaoManager.getInstance().getDao("");
}
public static SqlMap getSqlMap(Dao c) throws DaoException {
try {
DaoManager daoManager = DaoManager.getInstance(c);
if (daoManager == null) {
daoManager = DaoManager.getInstance();
}
SqlMapDaoTransaction trans = (SqlMapDaoTransaction)
daoManager.getLocalTransaction();
SqlMap sqlMap = trans.getSqlMap();
return sqlMap;
} catch (Exception e) {
throw new DaoException(e);
}
}
public static SqlMap getSqlMap(String c) throws
DaoException {
try {
DaoManager daoManager = DaoManager.getInstance(c);
SqlMapDaoTransaction trans = (SqlMapDaoTransaction)
daoManager.getLocalTransaction();
SqlMap sqlMap = trans.getSqlMap();
return sqlMap;
} catch (Exception e) {
throw new DaoException(e);
}
}
|
三个主要的函数,第一个是得到默认的DAO对象,后两个是根据一个dao对象或者一个参数(也就是前面<context
name="StoreDaoManager" >中是name值)。取得SqlMap对象,这个对象是主要的数据操作接口。
/**
* @throws Exception
* 开始事务,所在session层必须使用它
*/
public static void startTransaction() throws Exception {
if (!DaoCommon.inTransaction()) {
DaoManager.getInstance().startTransaction();
}
}
public static boolean inTransaction() throws Exception {
try {
DaoManager.getInstance().getLocalTransaction();
return true;
} catch (Exception e) {
return false;
}
}
/**
* @throws Exception
* 放弃事务
*/
public static void rollBack() {
try {
DaoManager.getInstance().rollbackTransaction();
} catch (Exception e) {
LogFactory.getLog(DaoCommon.class).error(e, e);
}
}
/**
* @throws Exception
* 提交事务
*/
public static void commit() throws Exception {
DaoManager.getInstance().commitTransaction();
}
|
下面的一些函数是对事务的一些封装。想必也很容易理解。
然后让我们来看CategoryDb的内容
/*
* Created on 2003-10-11
*
* To change the template for this generated file go to
* Window - Preferences - Java - Code Generation - Code and
Comments
*/
package com.ewuxi.champion.persistence.dao;
import com.ewuxi.champion.exception.DaoException;
import com.ewuxi.champion.persistence.vo.Category;
import com.ibatis.db.sqlmap.SqlMap;
/**
* @author champion
*
*category数据库操作对象
*/
public class CategoryDb {
/**
* @param vo
* @throws DaoException
* 插入一条记录
*/
public void insert(Category vo) throws DaoException{
try {
SqlMap sqlMap=DaoCommon.getSqlMap(DaoCommon.getDefautDao());
sqlMap.executeUpdate("insertCategoryDao",vo);
} catch (Exception e) {
throw new DaoException(e);
}
}
/**
* @param vo
* @throws DaoException
* 删除一条记录
*/
public void delete(Category vo) throws DaoException{
try {
SqlMap sqlMap=DaoCommon.getSqlMap(DaoCommon.getDefautDao());
sqlMap.executeUpdate("deleteByPrimaryKeyCategoryDao",vo);
} catch (Exception e) {
throw new DaoException(e);
}
}
/**
* @param vo
* @throws DaoException
* 修改一条记录
*/
public void update(Category vo) throws DaoException{
try {
SqlMap sqlMap=DaoCommon.getSqlMap(DaoCommon.getDefautDao());
sqlMap.executeUpdate("updateByPrimaryKeyCategoryDao",vo);
} catch (Exception e) {
throw new DaoException(e);
}
}
/**
* @param vo
* @return
* @throws DaoException
* 查找一条记录
*/
public Category findByPk(Category vo) throws DaoException{
try {
SqlMap sqlMap=DaoCommon.getSqlMap(DaoCommon.getDefautDao());
return (Category)
sqlMap.executeQueryForObject("findByPrimaryKeyCategoryDao",vo);
} catch (Exception e) {
throw new DaoException(e);
}
}
}
|
每一个函数都很类似的。关键就在这一句(Category)
sqlMap.executeQueryForList("findByPrimaryKeyCategoryDao",vo);。看到"findByPrimaryKeyCategoryDao",这个对应于前面Category.xml中的名字。而vo则是一个Category对象。
最后是CategoryDbTest类,这个是我们的单元测试程序
/*
* Created on 2003-10-11
*
* To change the template for this generated file go to
* Window - Preferences - Java - Code Generation - Code and
Comments
*/
package com.ewuxi.champion.persistence.dao;
import com.ewuxi.champion.exception.DaoException;
import com.ewuxi.champion.persistence.vo.Category;
import com.ibatis.db.sqlmap.SqlMap;
/**
* @author champion
*
*category数据库操作对象
*/
public class CategoryDb {
/**
* @param vo
* @throws DaoException
* 插入一条记录
*/
public void insert(Category vo) throws DaoException{
try {
SqlMap sqlMap=DaoCommon.getSqlMap(DaoCommon.getDefautDao());
sqlMap.executeUpdate("insertCategoryDao",vo);
} catch (Exception e) {
throw new DaoException(e);
}
}
/**
* @param vo
* @throws DaoException
* 删除一条记录
*/
public void delete(Category vo) throws DaoException{
try {
SqlMap sqlMap=DaoCommon.getSqlMap(DaoCommon.getDefautDao());
sqlMap.executeUpdate("deleteByPrimaryKeyCategoryDao",vo);
} catch (Exception e) {
throw new DaoException(e);
}
}
/**
* @param vo
* @throws DaoException
* 修改一条记录
*/
public void update(Category vo) throws DaoException{
try {
SqlMap sqlMap=DaoCommon.getSqlMap(DaoCommon.getDefautDao());
sqlMap.executeUpdate("updateByPrimaryKeyCategoryDao",vo);
} catch (Exception e) {
throw new DaoException(e);
}
}
/**
* @param vo
* @return
* @throws DaoException
* 查找一条记录
*/
public Category findByPk(Category vo) throws DaoException{
try {
SqlMap sqlMap=DaoCommon.getSqlMap(DaoCommon.getDefautDao());
return (Category)
sqlMap.executeQueryForObject("findByPrimaryKeyCategoryDao",vo);
} catch (Exception e) {
throw new DaoException(e);
}
}
}
|
运行结果,测试通过。原代码全部下载 |