您的位置:首页 > 其它

使用iBatis作为持久层实现快速开发

2012-12-02 16:52 323 查看
可能大家对iBatis的开发使用已经能够耳熟能详了,但是我们这里并非是对一个新的持久层方案做推广式的介绍,我想说的是,使用任何一个持久层解决方案,都应该能很好地将屏蔽物理数据库的复杂性,iBatis也一样。然而,对于开发人员来说,甚至是经历比较资深的程序员,在选择持久层方案与JDBC直连的时候,往往都会觉得iBatis配置比较复杂,而直接使用JDBC可以非常游刃有余地写出复杂容易理解的SQL语句,实现比较复杂的业务,但是他们也会“偶尔”因为管理资源不当,导致内存泄露,比如忘记关闭连接,对于交易系统来说,严重的话很可能直接造成整个系统宕机。对于一些初级的程序员(接触数据库应用开发时间不长),发生这样错误的可能性就更大了,而且由于代码经过多人之手多次重构,想要排查错误可能要从一堆乱糟糟的代码之中,一点点地找到问题所在,代价实在很大。

所以,我在开发中一般鼓励程序员使用持久层框架来做数据访问层(DAL),甚至,一个业务系统前期上线并没有考虑到后期可能要统计出报表,在后期的升级开发过程中,我也建议使用持久层框架,像iBatis就可以很好地支持任意复杂SQL语句,你可以将你写的SQL语句直接通过CDATA来避免iBatis解析器去解析,而直接在运行时执行你所实现的SQL语句查询,如下所示:

<sqlMap namespace="marketing_data_stat">
<resultMap id="stat_ssl_result_map" class="org.shirdrn.wm.de.db.model.MarketingDataStat">
<result column="domain" property="domain" jdbcType="VARCHAR" />
<result column="count" property="count" jdbcType="INT" />
</resultMap>
<select id="stat_count_ssl" parameterClass="java.util.HashMap"
resultClass="org.shirdrn.wm.de.db.model.MarketingDataStat">
<![CDATA[
select primary_domain as domain, count(cert_issuer_brand) as count
from marketing_data
where
(cert_validation='DV' or cert_validation='EV' or cert_validation='OV') and
cert_validity_notBefore<=DATE(NOW()) and
cert_validity_notAfter >=DATE(NOW()) and
cert_issuer_brand!='' and
created_at<DATE(NOW()) and
cert_url_isNameMatch='Y'
and live=1
group by primary_domain
order by count desc
]]>
<dynamic prepend="limit">
#limit:INT#
</dynamic>
</select>
</sqlMap>

一般来说,在系统上线之前,程序的逻辑错误导致的BUG基本都能够发现并修正,而对于一些比较隐藏的错误,例如内存泄露等,可能只要在系统使用一段时间以后才会出现,对这样的错误的排查可能也要花费时间和力气。而是用一些开源的解决方案的好处是,将一些复杂、容易出错的地方都在框架层解决掉,即使是系统上线后因为框架的问题,排查错误基本定位在框架这一层,而不需要排查每一个程序员开发的代码,而只需要通过开源社区对问题的跟踪和解决来完善我们的系统。使用框架的另一个好处是,能够是开发人员集中精力做好业务逻辑代码的处理。可能,在使用持久层框架的过程中,花费的时间多一点(针对那些并非很熟悉框架配置的人员),但是最终我们能够从这里受益的,甚至节约了更多的code
review的时间和精力。

下面,总结介绍一个使用iBatis作为持久层方案实现系统的数据库访问层以及服务层的代码框架:

第一步:实例数据库设计

我比较习惯使用Eclipse的ERMaster插件来对数据进行建模,它可以直接从ER图生成建表DDL,如图所示:



生成建表SQL语句如下:

CREATE TABLE DE_PC_KEYWORDS
(
ID INT NOT NULL UNIQUE AUTO_INCREMENT,
DOMAIN VARCHAR(255) NOT NULL,
KEYWORD VARCHAR(255) NOT NULL,
TYPE TINYINT NOT NULL,
STATUS TINYINT NOT NULL,
CREATED_AT DATE NOT NULL,
UPDATED_AT TIMESTAMP NOT NULL,
PRIMARY KEY (ID)
);

第二步:[b]生成iBatis持久层配置[/b]

使用Eclipse,需要安装一个iBator插件,然后配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ibatorConfiguration PUBLIC
"-//Apache Software Foundation//DTD Apache iBATIS Ibator Configuration 1.0//EN"
"http://ibatis.apache.org/dtd/ibator-config_1_0.dtd">
<ibatorConfiguration>
<classPathEntry location="/home/shirdrn/programs/eclipse-java-juno/workspace/ad_kw_platform/lib/mysql-connector-java-5.1.7-bin.jar" />
<ibatorContext id="ad_kw_platform" targetRuntime="Ibatis2Java5">
<property name="autoDelimitKeywords" value="true" />
<ibatorPlugin type="org.apache.ibatis.ibator.plugins.EqualsHashCodePlugin" />
<jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://172.0.8.249:5606/ad_kw_db?useUnicode=true" userId="root" password=$%@GFDsf00_o0pw />
<javaModelGenerator targetPackage="org.shirdrn.wm.de.db.model" targetProject="ad_kw_platform">
<property name="enableSubPackages" value="true" />
</javaModelGenerator>
<sqlMapGenerator targetPackage="org.shirdrn.wm.de.db.model.sqlmap" targetProject="ad_kw_platform">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<daoGenerator targetPackage="org.shirdrn.wm.de.db.dao" targetProject="ad_kw_platform" type="IBATIS" implementationPackage="org.shirdrn.wm.de.db.dao.impl">
<property name="enableSubPackages" value="true" />
</daoGenerator>

<table tableName="de_pc_keywords" modelType="flat">
<generatedKey column="id" sqlStatement="MySql" identity="true" type="post" />
</table>

</ibatorContext>
</ibatorConfiguration>

通过插件,就可以生成表de_pc_keywords对应DAO、DAO实现及其Map配置。

第三步:配置iBatis全局配置

全局iBatis配置可以配置数据库连接池、账号、表映射配置等等,如下所示:

<?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 resource="database.properties" />
<settings cacheModelsEnabled="true" enhancementEnabled="true" lazyLoadingEnabled="true" maxRequests="128" maxSessions="64" maxTransactions="16" useStatementNamespaces="true" />
<transactionManager type="JDBC">
<dataSource type="DBCP">
<property name="JDBC.Driver" value="${db.common.driver}" />
<property name="JDBC.ConnectionURL" value="${db.de.url}" />
<property name="JDBC.Username" value="${db.de.username}" />
<property name="JDBC.Password" value="${db.de.password}" />
<property name="initialSize" value="1" />
<property name="maxActive" value="50" />
<property name="maxIdle" value="10" />
<property name="minIdle" value="5" />
<property name="maxWait" value="60000" />
<!-- Use of the validation query can be problematic. If you have difficulty, try without it. -->
<property name="validationQuery" value="select null from dual" />
<property name="poolPreparedStatements" value="true" />
<property name="logAbandoned" value="false" />
<property name="removeAbandoned" value="true" />
<property name="removeAbandonedTimeout" value="300" />
<property name="defaultAutoCommit" value="false" />
<property name="defaultTransactionIsolation" value="NONE" />

</dataSource>
</transactionManager>

<!-- List the SQL Map XML files. They can be loaded from the classpath, as they are here (com.marketing.data...) -->
<sqlMap resource="org/shirdrn/wm/de/db/model/sqlmap/de_pc_keywords_SqlMap.xml" />
</sqlMapConfig>

上面使用了DBCP连接池,而且数据库的配置信息从属性文件“database.properties”文件中读取。

如果你的应用需要多张表,可以配置多个sqlMap元素。

第四步:配置DAO

配置内容,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE daoConfig
PUBLIC "-//ibatis.apache.org//DTD DAO Configuration 2.0//EN"
"http://ibatis.apache.org/dtd/dao-2.dtd">
<daoConfig>
<context>
<transactionManager type="SQLMAP">
<property name="SqlMapConfigResource" value="org/shirdrn/wm/de/db/sqlmap/db_SqlMap.xml" />
</transactionManager>
<dao interface="org.shirdrn.wm.de.db.dao.DePcKeywordsDAO" implementation="org.shirdrn.wm.de.db.dao.impl.DePcKeywordsDAOImpl" />
</context>
</daoConfig>

第五步:加载iBatis配置并开发Service框架

我们能够获取到DAO实例,就能通过它来访问数据库,实现代码如下所示:

package org.shirdrn.wm.de.db;

import java.io.IOException;
import java.io.Reader;
import java.util.Properties;

import com.ibatis.common.resources.Resources;
import com.ibatis.dao.client.DaoManager;
import com.ibatis.dao.client.DaoManagerBuilder;

public class DeDaoConfig {

private static final String resourceStaging = "dao/dao.xml";
private static DaoManager daoManager = null;

static {
try {
daoManager = newDaoManager(null);
} catch (Exception e) {
e.printStackTrace();
Runtime.getRuntime().exit(-1);
}
}

public static DaoManager getDaoManager() {
return daoManager;
}

private static DaoManager newDaoManager(Properties props) throws IOException {
Reader reader = Resources.getResourceAsReader(resourceStaging);
return DaoManagerBuilder.buildDaoManager(reader, props);
}

}

在开发过程中,为了开发人员集中精力实现复杂的业务逻辑,我们可以在DAO层基础上实现通用的Service层,这样开发人员只需要在Service层中增加自己需要的对数据库的操作,即可在实现业务逻辑时方便地调用。实现的CommonService泛型类如下:

package org.shirdrn.wm.de.common.service;

import org.shirdrn.wm.de.db.DeDaoConfig;

/**
* Common abstract service for injecting DAO to services
* which implement this class.
*
* @author Shirdrn
*
* @param <T> DAO {@link Class}
*/
public abstract class DeCommonService<T> {

protected T dao;

public DeCommonService() {
super();
}

@SuppressWarnings("unchecked")
protected DeCommonService(Class<T> clazz) {
super();
this.dao = (T) DeDaoConfig.getDaoManager().getDao(clazz);
}

}

使用任何一个表对应的DAO实现,都可以通过上面泛型在实际的Service类中注入。下面,看看我们实现的Service接口和实现类:

package org.shirdrn.wm.de.service;

import java.util.List;

import org.shirdrn.wm.de.db.model.DePcKeywords;
import org.shirdrn.wm.de.db.model.DePcKeywordsExample;

public interface DePcKeywordsService {

public int insert(DePcKeywords keyword);
public List<DePcKeywords> query(DePcKeywordsExample example);
public int update(DePcKeywords keyword);
}

在实现类中,可以继承自我们上面实现的CommonService泛型类,如下所示:

package org.shirdrn.wm.de.service.impl;

import java.util.List;

import org.shirdrn.wm.de.common.service.DeCommonService;
import org.shirdrn.wm.de.db.dao.DePcKeywordsDAO;
import org.shirdrn.wm.de.db.model.DePcKeywords;
import org.shirdrn.wm.de.db.model.DePcKeywordsExample;
import org.shirdrn.wm.de.service.DePcKeywordsService;

public class DePcKeywordsServiceImpl extends DeCommonService<DePcKeywordsDAO> implements DePcKeywordsService {

public DePcKeywordsServiceImpl() {
super(DePcKeywordsDAO.class);
}

@Override
public int insert(DePcKeywords keyword) {
return dao.insertSelective(keyword);
}

@Override
public List<DePcKeywords> query(DePcKeywordsExample example) {
return dao.selectByExample(example);
}

@Override
public int update(DePcKeywords keyword) {
return dao.updateByPrimaryKeySelective(keyword);
}

}

第六步:调用Service实现

其实,开发人员只需要在实际需要调用Service的地方,new一个Service实现类的实例即可,而且无需关心DAO层,如下所示:

private static final DePcKeywordsService dePcKeywordsService = new DePcKeywordsServiceImpl();


其实,有了上面这些作为基础,开发人员在开发过程甚至感觉不到是在调用数据库,而只是在调用一组与自己实现业务逻辑相关的服务。而且,开发人员可以在完成业务逻辑代码的过程中,处理好异常,关注自己代码的性能和健壮性。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐