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

来源:互联网 发布:模拟电路的软件 编辑:程序博客网 时间:2024/05/22 12:38

可能大家对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));

第二步:生成iBatis持久层配置

使用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);}@Overridepublic int insert(DePcKeywords keyword) {return dao.insertSelective(keyword);}@Overridepublic List<DePcKeywords> query(DePcKeywordsExample example) {return dao.selectByExample(example);}@Overridepublic int update(DePcKeywords keyword) {return dao.updateByPrimaryKeySelective(keyword);}}

第六步:调用Service实现

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

private static final DePcKeywordsService dePcKeywordsService = new DePcKeywordsServiceImpl();

 

其实,有了上面这些作为基础,开发人员在开发过程甚至感觉不到是在调用数据库,而只是在调用一组与自己实现业务逻辑相关的服务。而且,开发人员可以在完成业务逻辑代码的过程中,处理好异常,关注自己代码的性能和健壮性。

原创粉丝点击