iBATIS系统学习笔记三

本文深入探讨了iBATIS的高速缓存机制,包括内存缓存、缓存策略以及缓存模型的配置。讲解了如何利用缓存提高性能,如MEMORY、LRU、FIFO和OSCACHE等不同类型的缓存模型。此外,文章还介绍了iBATIS数据访问对象(DAO)的配置和实现,包括事务管理、自定义DAO层和与其他数据源的交互。
摘要由CSDN通过智能技术生成

目录:
学习笔记零 - 起源
学习笔记一 - 概念与入门
学习笔记二 - 基础配置
学习笔记四 - 技巧与实践


高级特性:使用高速缓存提高性能,iBATIS数据访问对象,DAO使用进阶,扩展iBATIS


使用高速缓存提高性能

内容
- 高速缓存理念
- 高速缓存配置
- 高速缓存策略

1 一个简单的iBATIS高速缓存示例

iBATIS的高速缓存机制完全基于配置的。

<cacheModel id="categoryCache" type="MEMORY">
    <flushOnExecute statement="insert"/>
    <flushOnExecute statement="update"/>
    <flushOnExecute statement="delete"/>
    <property name="reference-type" value="WEAK"/>
</cacheModel>

<select id="getCategory" parameterClass="Category" resultClass="Category" cacheModel="categoryCache">
    SELECT *
    FROM Category
    WHERE categoryId=#categoryId#
</select>

MEMORY:高速缓存类型,直接将查询结果存储在内存中。
flushOnExecute:当某个特定高速缓存被访问时,其存储结果将被清除。

2 iBATIS高速缓存的理念

高速缓存通常用来存储那些长效的、不会发生改变的数据。高速缓存也可以用于那些可修改的对象。
此处的难点是,需要保证请求的数据是否存在于当前高速缓存中,如果不存在,需要自己将它保存到高速缓存中。需要保证高速缓存中的数据是否已经过时。
iBATIS的思想是建立SQL语句到对象间的映射,而不是数据库表到对象间的映射。

3 高速缓存模型

高速缓存模型时一种高速缓存配置。高速缓存通过标签<cacheModel>定义,包含以下属性:
- id : 必须,唯一ID用来调用。
- Type:必须,高速缓存类型,MEMORY、LRU、FIFO和OSCACHE。
- readOnly:可选。
- serialize:可选,表示是否进行深复制。

3.1 type属性

iBATIS提供了4个默认的高速缓存实现
- MEMORY : 将缓存数据保存在内存中,直到垃圾收集器将它移除。
- FIFO: 这个模型中,高速缓存的数据量是固定的,使用先进先出(first in first out)算法来移除高速缓存中的数据。
- LRU:这个模型的数据量也是固定的,使用最近最少使用算法来移除高速缓存中的数据(least recently used)。
- OSCACHE:这个模型使用OpenSymphony高速缓存

3.2 readOnly属性

该属性仅仅是一个为高速缓存模型提供指令的指示器,用于告诉高速缓存模型应该如何检索和保存已高速缓存对象。当设置为true时,那高速缓存模型就会返回某个对象引用。

3.3 serialize属性

serialize属性用于指示高速缓存对象该如何返回,当属性为true时,高速缓存所请求的每个对象都将作为一个深副本返回。

3.4 联合使用readOnly属性和serialize属性

readOnlyserialize结果原因
TrueFalse可以快速地检索出已高速缓存的对象。返回一个共享的实例。
FalseTrue返回一个缓存对象的深副本
FalseFalse警告高速缓存仅仅同调用线程会话的生命周期有关,且不能被其他线程调用。
TrueTure同False,True组合一样。

4 如何使用高速缓存模型中的标签

4.1 高速缓存的清除

高速缓存具有两个标签 <flushOnExecute> 和<flushInterval>标签

<flushOnExecute>标签
定义查询已映射语句,其执行将引起高速缓存的清除。

<sqlMap namespace="Category"><cacheModel id="categoryCache" type="MEMORY"><flushOnExecute statement="Category.insert"/></cacheModel><select id="getCategory" parameterClass="Category" resultClass="Category" cacheModel="categoryCache">
        SELECT *
        FROM Category
        WHERE parentCategoryId=#categoryId#
    </select><insert id="insert" parameterClass="Category" >
        INSERT INTO Category
        (title,description,sequence)
        VALUES
        (#title#,#description#,#sequence#)
    </insert></sqlMap>

<flushInterval>标签
定义一个时间间隔,高速缓存将以此间隔定期清除
属性:hours,minutes,seconds,millseconds,都是可选项。

示例:

<sqlMap namespace="Category"><cacheModel id="categoryCache" type="MEMORY"><flushInterval hours= "12" /></cacheModel><select id="getCategory" parameterClass="Category" resultClass="Category" cacheModel="categoryCache">
    SELECT *
    FROM Category
    WHERE parentCategoryId=#categoryId#
    </select></sqlMap>

4.2 设置高速缓存模型实现的特性

高速缓存模型是iBATIS框架中的组件,他允许用户自己定制,因此必须有一种方式能够为这些组件提供任意的值。<property>标签就是用来完成此任务的。
<property>标签的属性:Name,value

5 高速缓存模型的类型

高速缓存模型具备MEMORY,LRU,FIFO,OSCACHE

5.1 MEMORY

MEMORY高速远程基于引用,具备WEAK,SOFT,STRONG三种引用类型。
例子:

<cacheModel id="categoryCache" type="MEMORY">
    <flushInterval hours="24"/>
    <flushOnExecute statement="insert"/>
    <flushOnExecute statement="update"/>
    <flushOnExecute statement="delete"/>
    <property name="reference-type" value="WEAK"/>
</cacheModel>

5.2 LRU

LRU类型的高速缓存使用最近最少使用策略来管理高速缓存。
LRU的propery标签唯一能指定的是size,用来指定高速缓存中的对象的最大数目。

<cacheModel id="categoryCache" type="LRU">
    <flushInterval hours="24"/>
    <flushOnExecute statement="insert"/>
    <flushOnExecute statement="update"/>
    <flushOnExecute statement="delete"/>
    <property name="size" value="200"/>
</cacheModel>

LRU高速缓存对于那些数据的不同子集(subsets of data)都用在某段时间内的应用程序非常有用

5.3 FIFO

FIFO高速缓存模型采用先进先出的管理策略。唯一需要指定的property也是size。

<cacheModel id="categoryCache" type="FIFO">
    <flushInterval hours="24"/>
    <flushOnExecute statement="insert"/>
    <flushOnExecute statement="update"/>
    <flushOnExecute statement="delete"/>
    <property name="size" value="1000"/>
</cacheModel>

5.4 OSCACHE

该高速缓存模型采用了OpenSymphony公司的产品OSCache2.0,需要JAR文件依赖以及配置文件。

5.5 自己的高速缓存模型

需要了解两点,1,高速缓存模型实际上都是CacheController接口的实现。
2,the names are type aliases that map to the fully qualified names of those implementations.

6 确定高速缓存策略

当需要缓存只读的长效数据时,我们可以使用LRU策略。比如购物车的类别。

如果需要缓存的对象易变,就要好好考虑是否使用高速缓存技术,如果需要使用,可以用MEMORY模式,并且设置reference-type为WEAK

旧的静态数据,比如统计数据,可以用FIFO模式

iBATIS数据访问对象

内容
- DAO基本原理
- 配置
- SQLMapDAO示例

DAO模式用于隐藏API格子的独特实现。

1 iBATIS DAO的简单示例

iBATIS Dao:
![Alt text](./iBATIS DAO.png)

1.1 Dao.xml 配置文件

要配置DaoManager,必须从一个XML配置文件开始,该文件通常被命名为dao.xml

dao.xml:

<?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 id="example">
        <transactionManager type="SQLMAP">
            <property name="SqlMapConfigResource" value="examples/SqlMapConfig.xml"/>
        </transactionManager>
        <dao interface="examples.dao.AccountDao" implementation="examples.dao.impl.AccountDao"/>
    </context>
</daoConfig>

1.2 创建DaoManager

public class DaoService {
    private static DaoManager daoManager;
    public static synchronized DaoManager getDaoManager(){
        String daoXmlResource = "dao.xml";
        Reader reader;
        if (null == daoManager){
            try {
                reader =
                Resources.getResourceAsReader(daoXmlResource);
                daoManager = DaoManagerBuilder.buildDaoManager(reader);
                return daoManager;
            } catch (IOException e) {
        throw new RuntimeException( "Unable to create DAO manager.", e);
            }
        } else {
            return daoManager;
        }
    }
    public static Dao getDao(Class interfaceClass){
        return getDaoManager().getDao(interfaceClass);
    }
}

要获取Dao可以直接调用以下代码:

AccountDao accountDao = (AccountDao) DaoService.getDao(AccountDao.class);

2 配置DAO

使用iBATIS DAO框架时,唯一需要的配置文件就是dao.xml文件。

2.1 <properties> 元素

他用来指定一个特性文件,该文件所列出的特性可以通过${name}语法使用在DAO层配置文件中

2.2 < context > 元素

一个DAO的上下文是指一组相关的DAO配置信息和一组DAO的实现。通过配置context可以很容易的将多个数据库访问配置集中。

2.3 < transactionManager> 元素

事务管理器,iBATIS DAO框架提供了7种不同的事务管理器:EXTERNAL,HIBERNATE,JDBC,JTA,OJB,SQLMAP,TOPLINK。
- EXTERNAL事务管理器:最易配置最难使用,需要自行对应用程序的所有事务负责
- HIBERNATE:易配置,任何名字以class开头的特性读背认为是应该被Hibernate管理的类,并将其添加到用于构建会话工厂的配置对象中。map.开头的特性被认为是映射文件
- JDBC:最难配置,DataSource特性是必须的,取值有SIMPLE,DBCP或JNDI。SIMPLE - 是iBATISD SimpleDataSource接口的实现。DBCP - Jakarta CDBCP。JNDI数据源 - 最简单的数据源。
- JTA事务管理器:允许为多个数据库提供分布式事务管理。
- OJB:O/RM工具
- SQLMAP:最常用的事务管理器。
- TOPLINK:另一个O/RM工具
- 自己的或其他的事务管理器:通过type属性指定类名

2.4 DAO元素

DAO仅有2个属性,interface和implementation。

<dao interface="com.mycompany.system.dao.AccountDao" implementation= "com.mycompany.system.dao.impl.AccountDaoImpl"/>

3 配置技巧

3.1 多个服务器

样例:

<?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>
    <properties resource="server.properties"/>
    <context>
        <transactionManager type="JDBC">
            <property name="DataSource" value="SIMPLE"/>
            <property name="JDBC.Driver" value="${jdbcDriver}" />
            <property name="JDBC.ConnectionURL" value="${jdbcUrl}" />
            <property name="JDBC.Username" value="${jdbcUser}" />
            <property name="JDBC.Password" value="${jdbcPassword}" />
            <property name="JDBC.DefaultAutoCommit" value="${jdbcAutoCommit}" />
        </transactionManager>
        <dao interface="..." implementation="..."/>
    </context>
</daoConfig>

所有的属性值保存在server.properties里。

3.2 多种数据库方言

和上例方法一样,可以

<dao interface="com.company.system.dao.AccountDao" implementation="${impl}.AccountDaoImpl"/>

3.3 运行时配置更改

public static DaoManager getDaoManager(Properties props){
    String daoXml = "/org/apache/mapper2/examples/Dao.xml";
    Reader reader;
    DaoManager localDaoManager;
    try {
        reader = Resources.getResourceAsReader(daoXml);
        localDaoManager =
        DaoManagerBuilder.buildDaoManager(reader, props);
    } catch (IOException e) {
    throw new RuntimeException("Unable to create DAO manager.", e);
    }
    return localDaoManager;
}

4 基于SQL Map的DAO实现示例

DAO就是关于如何在接口后面隐藏实际的数据访问实现。
构造接口:

public interface AccountDao {
    public void insert(Account account);
    public void update(Account account);
    public int delete(Account account);
    public int delete(Integer accountId);
    public List<Account> getAccountListByExample(Account account);
    public List<Map<String, Object>> getMapListByExample(Account account);
    public List<IdDescription> getIdDescriptionListByExample(Account account);
    public Account getById(Integer accountId);
    public Account getById(Account account);
}

4.1 配置iBATIS 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 id="sqlmap">
        <transactionManager type="SQLMAP">
            <property name="SqlMapConfigResource" value="SqlMapConfig.xml"/>
        </transactionManager>
        <dao interface="com.mycompany.system.dao.AccountDao"
implementation= "com.mycompany.system.dao.sqlmap.AccountDaoImpl"/>
</context>
</daoConfig>

4.2 创建DaoManager实例

private DaoManager getDaoManager() {
    DaoManager tempDaoManager = null;
    Reader reader;
    try {
        reader = Resources.getResourceAsReader("Dao.xml");
        tempDaoManager = DaoManagerBuilder.buildDaoManager(reader);
    } catch (Exception e) {
        e.printStackTrace();
        fail("Cannot load dao.xml file.");
    }
    return tempDaoManager;
}

4.3 定义事务管理器

<?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="SqlMapConfig.properties" />
    <settings
        errorTracingEnabled="true"
        cacheModelsEnabled="true"
        enhancementEnabled="true"
        lazyLoadingEnabled="true"
        maxRequests="32"
        maxSessions="10"
        maxTransactions="5"
        useStatementNamespaces="true"
    />
    <transactionManager type="JDBC" >
        <dataSource type="SIMPLE">
            <property name="JDBC.Driver" value="${driver}"/>
            <property name="JDBC.ConnectionURL" value="${connectionUrl}"/>
            <property name="JDBC.Username" value="${username}"/>
            <property name="JDBC.Password" value="${password}"/>
        </dataSource>
    </transactionManager>
    <sqlMap resource= "com/mycompany/system/dao/sqlmap/Account.xml" />
</sqlMapConfig>

4.4 加载映射

<?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="Account">
    <typeAlias alias="Account" type="${BeanPackage}.Account" />
    <typeAlias alias="IdDescription" type="${BeanPackage}.IdDescription" />
    <insert id="insert" parameterClass="Account">
        <selectKey keyProperty="accountId" resultClass="int">
            SELECT nextVal('account_accountid_seq')
        </selectKey>
            INSERT INTO Account (
                accountId,
                username,
                password,
                firstName,
                lastName,
                address1,
                address2,
                city,
                state,
                postalCode,
                country
            ) VALUES(
                #accountId#,
                #username:varchar#,
                #password:varchar#,
                #firstName:varchar#,
                #lastName:varchar#,
                #address1:varchar#,
                #address2:varchar#,
                #city:varchar#,
                #state:varchar#,
                #postalCode:varchar#,
                #country:varchar#
            )
    </insert>
    <!-- delete & update-->

    <sql id="allFields">
        accountId as "accountId",
        username,
        password,
        firstName as "firstName",
        lastName as "lastName",
        address1,
        address2,
        city,
        state,
        postalCode as "postalCode",
        country
    </sql>

    <sql id="whereByExample">
        <dynamic prepend=" where ">
            <isNotEmpty property="city">
                city like #city#
            </isNotEmpty>
            <isNotNull property="accountId" prepend=" and ">
                accountId = #accountId#
            </isNotNull>
        </dynamic>
    </sql>

    <sql id="getByExample">
        select
        <include refid="allFields" />
            from Account
        <include refid="whereByExample" />
    </sql>

    <select id="getAccountListByExample"
        resultClass="Account">
        <include refid="getByExample" />
    </select>

    <select id="getMapListByExample" resultClass="hashmap">
        <include refid="getByExample" />
    </select>
</sqlMap>

4.5 DAO实现编码

public class AccountDaoImpl extends SqlMapDaoTemplate implements AccountDao {
    public AccountDaoImpl(DaoManager daoManager) {
        super(daoManager);
    }

    public Integer insert(Account account) {
        return (Integer) insert("Account.insert", account);
    }

    public int update(Account account) {
        return update("Account.update", account);
    }

    public int delete(Account account) {
        return delete(account.getAccountId());
    }

    public int delete(Integer accountId) {
        return delete("Account.delete", accountId);
    }

    public List<Account> getAccountListByExample(Account account) {
    return queryForList("Account.getAccountListByExample",account);
    }

    public List<Map<String, Object>> getMapListByExample(Account account) {
    return queryForList("Account.getMapListByExample",account);
    }

    public List<IdDescription> getIdDescriptionListByExample(Account account) {
return queryForList("Account.getIdDescriptionListByExample",account);
    }

    public Account getById(Integer accountId) {
        return (Account)queryForObject("Account.getById", accountId);
    }

    public Account getById(Account account) {
        return getById(account.getAccountId());
    }
}

注意SqlMapDaoTemplate 这个父类


DAO使用进阶

内容:
- DAO的更多样例
- 使用Spring替代iBATIS
- 从头开始创建DAO层

1 不是基于SQLMap的DAO实现

1.1 Hibernate版本的DAO实现

1、定义DAO上下文

<context id="hibernate">
    <transactionManager type="HIBERNATE">
        <property name="hibernate.connection.driver_class" value="org.postgresql.Driver" />
        <property name="hibernate.connection.url" value="jdbc:postgresql:ibatisdemo" />
        <property name="hibernate.connection.username" value="ibatis" />
        <property name="hibernate.connection.password" value="ibatis" />
        <property name="hibernate.connection.pool_size" value="5" />
        <property name="hibernate.dialect" value= "net.sf.hibernate.dialect.PostgreSQLDialect" />
        <property name="map.Account" value= "${DaoHomeRes}/hibernate/Account.hbm.xml" />
    </transactionManager>
    <dao interface="${DaoHome}.AccountDao" implementation= "${DaoHome}.hibernate.AccountDaoImpl"/>
</context>

2、映射Account表

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
    <class name="org.apache.mapper2.examples.bean.Account" table="Account">
        <id name="accountId" type="int" column="accountid">
            <generator class="sequence">
                <param name="sequence">account_accountid_seq</param>
            </generator>
        </id>
        <property name="username" />
        <property name="password" />
        <property name="firstName" />
        <property name="lastName" />
        <property name="address1" />
        <property name="address2" />
        <property name="city" />
        <property name="state" />
        <property name="postalCode" />
        <property name="country" />
    </class>
</hibernate-mapping>

3、实际的DAO实现

public class AccountDaoImpl extends HibernateDaoTemplate implements AccountDao {
    private static final Log log =
    LogFactory.getLog(AccountDaoImpl.class);
    public AccountDaoImpl(DaoManager daoManager) {
        super(daoManager);
        if(log.isDebugEnabled()){
            log.debug("Creating instance of " + getClass());
        }
    }

    public Integer insert(Account account) {
        try {
            getSession().save(account);
        } catch (HibernateException e) {
            log.error(e);
            throw new DaoException(e);
        }
        return account.getAccountId();
    }
    //...

1.2 JDBC版本的DAO实现

    <context id="jdbc">
        <transactionManager type="JDBC">
            <property name="DataSource" value="SIMPLE"/>
            <property name="JDBC.Driver" value="org.postgresql.Driver" />
            <property name="JDBC.ConnectionURL" value="jdbc:postgresql:ibatisdemo" />
            <property name="JDBC.Username" value="ibatis" />
            <property name="JDBC.Password" value="ibatis" />
            <property name="JDBC.DefaultAutoCommit" value="true" />
        </transactionManager>
    <dao interface="${DaoHome}.AccountDao" implementation="${DaoHome}.jdbc.AccountDaoImpl"/>
    </context>

实现代码量远远多过Hibernate以及iBATIS


2 为其他数据源使用DAO模式

2.1 为LDAP使用DAO

LDAP是一个用于存储具有层次结构的数据的强大工具,经常被网络管理员用于追踪用户,组成员以及从属关系。
LDAP术语
构成LDAP目录的基本单元被称为记录项(entry),可以包含称为属性的数据,或者其他记录项,或者两者同时包含。每个记录项只有一个父记录项,并且被一个DN唯一标示。
存储在LDAP目录中的数据由若干属性组成,一个属性就是一个名/值对,实际上与Java中的Map非常相似。

从Java到LDAP记录项之间的映射

Bean特性LDAP属性
userIduid
mailmail
descriptiondescription
lastNamesn
firstNamegivenName

硬编码方式映射:

private Attributes getAttributes(Contact contact){
    Attributes returnValue = new BasicAttributes();
    returnValue.put("mail", contact.getMail());
    returnValue.put("uid", contact.getUserId());
    returnValue.put("objectClass", "inetOrgPerson");
    returnValue.put("description", contact.getDescription());
    returnValue.put("sn", contact.getLastName());
    returnValue.put("cn", contact.getUserId());
    returnValue.put("givenName", contact.getFirstName());
    return returnValue;
}

private Contact getContact(Attributes attributes) {
    Contact contact = new Contact();
    contact.setDescription(getAttributeValue(attributes, "description"));
    contact.setLastName(getAttributeValue(attributes, "sn"));
    contact.setFirstName(getAttributeValue(attributes, "givenName"));
    contact.setMail(getAttributeValue(attributes, "mail"));
    contact.setUserId(getAttributeValue(attributes, "uid"));
    return contact;
}

private String getAttributeValue(Attributes attributes, String attrID{
        Attribute attribute = attributes.get(attrID);
    try {
        return (null==attribute?"":(String)attribute.get());
    } catch (NamingException e) {
        throw new DaoException(e);
    }
}

Attributes接口来自sun的JDK的JNDI,使用BasicAttributes类实现。

获取目录上下文:

private DirContext getInitialContext() {
    DirContext ctx = null;
    try {
        ctx = new InitialDirContext(env);
    } catch (NamingException e) {
        log.error("Exception getting initial context", e);
        throw new DaoException(e);
    }
        return ctx;
}

实现方法:

public Contact getById(String id) {
    DirContext ctx = getInitialContext();
    Attributes attributes;
    try {
    attributes = ctx.getAttributes(getDn(id));
    } catch (NamingException e) {
    throw new DaoException(e);
    }
    return getContact(attributes);
}

2.2 为Web服务使用Dao

bean和接口:

public class SearchResult {
    private String url;
    private String summary;
    private String title;
    // getters and setters omitted...
}
public interface WebSearchDao {
    List<SearchResult> getSearchResults(String text);
}

dao实现:

public class GoogleDaoImpl implements WebSearchDao {
    private String googleKey;
    public GoogleDaoImpl(){
        this("insert-your-key-value-here");
    }

    public GoogleDaoImpl(String key){
        this.googleKey = key;
    }

    public List<SearchResult> getSearchResults(String text){
        List<SearchResult> returnValue = new
        ArrayList<SearchResult>();
        GoogleSearch s = new GoogleSearch();
        s.setKey(googleKey);
        s.setQueryString(text);
        try {
            GoogleSearchResult gsr = s.doSearch();
            for (int i = 0; i < gsr.getResultElements().length;i++){
                GoogleSearchResultElement sre =
                gsr.getResultElements()[i];
                SearchResult sr = new SearchResult();
                sr.setSummary(sre.getSummary());
                sr.setTitle(sre.getTitle());
                sr.setUrl(sre.getURL());
                returnValue.add(sr);
            }
                return returnValue;
        } catch (GoogleSearchFault googleSearchFault) {
            throw new DaoException(googleSearchFault);
        }
    }
}

3 使用Spring DAO

public class AccountDaoImplSpring extends SqlMapClientTemplate implements AccountDao{
    public Integer insert(Account account) {
        return (Integer) insert("Account.insert", account);
        delete(account.getAccountId());
    }

    public int delete(Integer accountId) {
        return delete("Account.delete", accountId);
    }

    public List<Account> getAccountListByExample(Account account) {
    return queryForList("Account.getAccountListByExample",account);
    }

    public List<Map<String, Object>> getMapListByExample(Account account) {
    return queryForList("Account.getMapListByExample",account);
    }

    public List<IdDescription> getIdDescriptionListByExample(Account account) {
    return queryForList("Account.getIdDescriptionListByExample", account);
}

    public Account getById(Integer accountId) {
        return (Account) queryForObject("Account.getById",accountId);
    }

    public Account getById(Account account) {
        return (Account) queryForList("Account.getById",account);
    }
}

扩展类不同SqlMapClientTemplate

4 创建自己的DAO层

创建一个DAO层,需要依次完成3件事情:
1、从实现中分离出接口。
2、用一个可外部配置的工厂来解耦实现。
3、提供事务管理和数据库连接管理。

4.1 从实现中分离出接口

4.2 创建一个工厂以解耦

如果不使用工厂,很有可能代码如下:
AccountDao accountDao = new AccountDaoImpl();
这样在通一个地方,同时引用了接口与实现,就失去了分离的价值。

更好的模式:
AccountDao accountDao = (AccountDao)DaoFactory.get(AccountDao.class);
这样代码就从实现中解耦,DaoFactory会处理一切,产生一个AccountDao接口。

DAO工厂实现:

public class DaoFactory {
    private static DaoFactory instance = new DaoFactory();
    private final String defaultConfigLocation = "DaoFactory.properties";
    private Properties daoMap;
    private Properties instanceMap;
    private String configLocation = System.getProperty("dao.factory.config",defaultConfigLocation);
    private DaoFactory(){
        daoMap = new Properties();
        instanceMap = new Properties();
        try {
            daoMap.load(getInputStream(configLocation));
        } catch (IOException e) {
        throw new RuntimeException(e);
        }
    }

    private InputStream getInputStream(String configLocation){
        return T    hread.currentThread().getContextClassLoader().getResourceAsStream(conf  igLocation);
    }

    public static DaoFactory getInstance() {
        return instance;
    }

    public Object getDao(Class daoInterface){
        if (instanceMap.containsKey(daoInterface)) {
            return instanceMap.get(daoInterface);
        }
        return createDao(daoInterface);
    }

    private synchronized Object createDao(Class daoInterface) {
        Class implementationClass;
        try {
            implementationClass = Class.forName((String)
            daoMap.get(daoInterface));
            Object implementation = implementationClass.newInstance();
            instanceMap.put(implementationClass, implementation);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return instanceMap.get(daoInterface);
    }
}

扩展iBATIS

内容
- 自定义类型处理器
- 高速缓存控制器
- 自定义数据源

1 可插拔组件的设计

一个可插拔组件的设计通常由3部分组成:接口实现工厂

可扩展的功能组件描述
TypeHandlerCallback实现自己的类型处理逻辑,以便处理非标准数据库、驱动程序和数据类型
CacheController用自己的高速缓存代码实现CacheController
DataSourceFactory实现任何标识的JDBC DataSource实现
TransactionConfig实现一个自定义事务管理器

2 使用自定义类型处理器

2.1 实现自定义类型处理器

要实现自定义的TypeHandler,只需要实现部分功能即可,此功能定义在一个被称为TypeHandlerCallback的简单接口中。

TypeHandlerCallback:

public interface TypeHandlerCallback {
    public void setParameter(ParameterSetter setter, Object parameter) throws SQLException;
    public Object getResult(ResultGetter getter) throws SQLException;
    public Object valueOf(String s);
}

如果一个数据库用YES和NO来代表布尔型,我们设计这样一个映射类:

public class User {
    private int id;
    private String username;
    private String passwordHashcode;
    private boolean enabled;
    // assume compliant JavaBeans properties
    // (getters/setters) below
}

enabled在数据库中是一个VARCHAR类型,存储YES和NO。

2.2 创建TypeHandlerCallback

骨架:

public class YesNoTypeHandlerCallback implements TypeHandlerCallback {
    private static final String YES = "YES";
    private static final String NO = "NO";
    public void setParameter(ParameterSetter setter, Object parameter) throws SQLException {}
    public Object getResult(ResultGetter getter) throws SQLException {}
    public Object valueOf(String s) {}
}

设置参数
如果布尔型为true则是YES,false则是NO,为空则抛出异常

private String booleanToYesNo(Boolean b) {
    if (b == null) {
        throw new IllegalArgumentException ("Could not convert null to a boolean value. " + "Valid arguments are 'true' and 'false'.");
    } else if (b.booleanValue()) {
        return YES;
    } else {
        return NO;
    }
}

setParameter

public void setParameter(ParameterSetter setter, Object parameter) throws SQLException {
    setter.setString(booleanToYesNo((Boolean) parameter));
}

获取结果

private Boolean yesNoToBoolean(String s) {
    if (YES.equalsIgnoreCase(s)) {
        return Boolean.TRUE;
    } else if (NO.equalsIgnoreCase(s)) {
        return Boolean.FALSE;
    } else {
        throw new IllegalArgumentException (
            "Could not convert " + s +
            " to a boolean value. " +
            "Valid arguments are 'YES' and 'NO'.");
    }
}

getResult

public Object getResult(ResultGetter getter) throws SQLException {
    return yesNoToBoolean(getter.getString());
}

处理空值
当数据库中使用一个可以为空的列,但却不需要对象模型中的可为空类型。

<result property="enabled" column="Enabled" nullValue="NO"/>

valueOf

public Object valueOf(String s) {
    return yesNoToBoolean(s);
}

2.3 注册TypeHandlerCallback以供使用

  • 在SqlMapConfig.xml文件中注册,以使它全局可用。
  • 在某个SqlMap.xml文件中注册,以使它局部可用。
  • 专门针对某个结果映射或参数映射进行注册。

全局使用:

<typeHandler callback="com.domain.package.YesNoTypeHandlerCallback" javaType="boolean" jdbcType="VARCHAR" />

3 使用CacheController

iBATIS提供了一个成为CacheController的接口,以允许你实现一个完全属于自己的自定义高速缓存解决方案。

public interface CacheController {
    public void configure(Properties props);
    public void putObject(CacheModel cacheModel, Object key, Object object);
    public Object getObject(CacheModel cacheModel, Object key);
    public Object removeObject(CacheModel cacheModel, Object key);
    public void flush(CacheModel cacheModel);
}

4 配置iBATIS不支持的DataSource

要配置一个新的DataSource,需要为iBATIS提供一个工厂,该工厂可以为框架提供DataSource实例。

public interface DataSourceFactory {
    public void initialize(Map map);
    public DataSource getDataSource();
}

简单的DataSource实现:

public class SimpleDataSourceFactory implements DataSourceFactory {
    private DataSource dataSource;
    public void initialize(Map map) {
        dataSource = new SimpleDataSource(map);
    }
    public DataSource getDataSource() {
        return dataSource;
    }
}

5 定制事务管理

iBATIS提供TransactionConfig和Transaction接口,来创建自己的事务适配器。

5.1 理解TransactionConfig接口

public interface TransactionConfig {
    public void initialize(Properties props) throws SQLException, TransactionException;
    public Transaction newTransaction(int transactionIsolation) throws SQLException, TransactionException;
    public int getMaximumConcurrentTransactions();
    public void setMaximumConcurrentTransactions(int max);
    public DataSource getDataSource();
    public void setDataSource(DataSource ds);
}

5.2 Transaction接口

public interface Transaction {
    public void commit() throws SQLException,TransactionException;
    public void rollback() throws SQLException,TransactionException;
    public void close() throws SQLException,TransactionException;
    public Connection getConnection() throws SQLException,TransactionException;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值