iBATIS系统学习笔记三

时间:2021-11-08 09:38:00

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


高级特性:使用高速缓存提高性能,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属性

readOnly serialize 结果 原因
True False 可以快速地检索出已高速缓存的对象。返回一个共享的实例。
False True 返回一个缓存对象的深副本
False False 警告 高速缓存仅仅同调用线程会话的生命周期有关,且不能被其他线程调用。
True Ture 同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属性
userId uid
mail mail
description description
lastName sn
firstName givenName

硬编码方式映射:

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;
}