application.xml
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath*:conf/develop.properties</value> <value>classpath*:conf/sso/config.properties</value> <value>classpath*:conf/db/jdbc.properties</value> </list> </property> </bean> <!-- 第一个数据库 --> <bean id="readDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!-- Connection Info --> <property name="driverClassName"> <value>${jdbc.read.driverClassName}</value> </property> <property name="url"> <value>${jdbc.read.url}</value> </property> <property name="username"> <value>${jdbc.read.username}</value> </property> <property name="password"> <value>${jdbc.read.password}</value> </property> <!-- Connection Pooling DBCP --> <!-- 初始化时创建的连接数 --> <property name="initialSize" value="5" /> <!-- 最大连接数据库连接数,设置为0时,表示没有限制; --> <property name="maxActive" value="100" /> <!-- 最大等待连接中的数量,设置为0时,表示没有限制; --> <property name="maxIdle" value="30" /> <!-- 最大等待秒数,单位为毫秒, 超过时间会报出错误信息; --> <property name="maxWait" value="100000" /> <!-- 是否可用预执行 --> <property name="poolPreparedStatements" value="true" /> <!-- 设置从数据源中返回的连接是否采用自动提交机制,默认值为 true;false支持事务 --> <property name="defaultAutoCommit" value="false" /> <!-- 连接关闭时是否要清除缓存 --> <property name="removeAbandoned" value="true"/> <!-- 清空等待时间 --> <property name="removeAbandonedTimeout" value="1"/> </bean> <context:annotation-config /> <context:component-scan base-package="com.woyo.travel.hotel"/> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="false"/> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="writeDataSource"/> </bean>
读写MAP:
<bean id="sqlMapClientRead" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="dataSource"> <ref bean="dataSourceRead"/> </property> <property name="configLocation"> <value>classpath:/conf/sqlmap/sqlmap-config.xml</value> </property> <!-- <property name="mappingLocations"> <value>classpath*:/conf/sqlmap/mysql/*.xml</value> </property>--> </bean> <bean name="sqlMapClientTemplateRead" class="org.springframework.orm.ibatis.SqlMapClientTemplate"> <constructor-arg ref="sqlMapClientRead"></constructor-arg> </bean>
SQLMAP 配置 :
<?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> <settings useStatementNamespaces="false" enhancementEnabled="true" lazyLoadingEnabled="true"/> <typeHandler jdbcType="DATE" javaType="java.util.Date" callback="com.ibatis.sqlmap.engine.type.DateTypeHandler" /> <!-- system user --> <sqlMap resource="conf/sqlmap/userdefine/sqlmap-user.xml"/> <sqlMap resource="conf/sqlmap/userdefine/sqlmap-category.xml"/> </sqlMapConfig>
SQLMAP:
<?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="activityProduct"> <resultMap class="com.woyo.brand.domain.model.ActivityProduct" id="activityProduct"> <result property="id" column="id" /> <result property="activityType" column="activity_type" /> <result property="productId" column="product_id" /> <result property="activityId" column="activity_id" /> </resultMap> <sql id="activityProduct_masterStatement"> <![CDATA[SELECT * FROM activity_product ]]> </sql> <update id="activityProduct_updateByMap" parameterClass="java.util.HashMap"> <![CDATA[ UPDATE activity_product ]]> </update> </sqlMap>
web.xml
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:spring/*.xml</param-value> </context-param> <context-param> <param-name>log4jConfigLocation</param-name> <param-value>classpath:log4j.properties</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
读写分离的 Java BaseDao 类:
/**
* DAO基类。针对数据库的读、写操作分别采用不同的Template,可以在一定程度上防止大量并发造成的死锁。
*
* @author CrayHu(hukl424@163.com)
* @version Revision: 1.00 Date: 2010-6-13
*/
public class BaseDaoImpl<T extends DomainObject> {
/**
* 分页statement后缀:_count
*/
protected static final String COUNT_STATEMENT_SUFFIX = "_count";
private String entityClass;
/**
* 用于写操作的Template
*/
private SqlMapClientTemplate writeTemplate;
/**
* 用于读操作的Template
*/
private SqlMapClientTemplate readTemplate;
/**
* @return the writeTemplate
*/
public SqlMapClientTemplate getWriteTemplate() {
return writeTemplate;
}
/**
* 需要在配置文件中配置id为writeSqlMapClient的bean
*
* @param sqlMapClient the SqlMapClient object to set
*/
@Resource(name="writeSqlMapClient")
public void setWriteSqlMapClient(SqlMapClient sqlMapClient) {
writeTemplate = new SqlMapClientTemplate(sqlMapClient);
}
/**
* @return the readTemplate
*/
public SqlMapClientTemplate getReadTemplate() {
return readTemplate;
}
/**
* 需要在配置文件中配置id为writeSqlMapClient的bean
*
* @param sqlMapClient the SqlMapClient object to set
*/
@Resource(name="readSqlMapClient")
public void setReadSqlMapClient(SqlMapClient sqlMapClient) {
readTemplate = new SqlMapClientTemplate(sqlMapClient);
}
/**
* 新增实体类
*
* @param statement
* @param entity
* @return
*/
protected DomainObject save(String statement, DomainObject entity) {
Integer entityId = (Integer)writeTemplate.insert(statement, entity);
if(entityId != null && entityId > 0) {
entity.setId(entityId);
}
return entity;
}
/**
* 新增
*
* @param statement
* @param entity
*/
protected void save(String statement, Object entity) {
writeTemplate.insert(statement, entity);
}
/**
* 新增返回本次会话自增Id。
* added by wallce_xu
*
* @param statement
* @param entity
*/
protected int savegetId(String statement, Object entity) {
return (Integer)(writeTemplate.insert(statement, entity));
}
/**
* 新增返回本次会话自增id
* added by wallce_xu
*
* @param statement
* @param entity
*/
protected void saveid(String statement, Object entity) {
int i = (Integer) writeTemplate.insert(statement, entity);
}
/**
* 修改
*
* @param statement
* @param params
*/
protected void update(String statement, Object params) {
writeTemplate.update(statement, params);
}
/**
* 根据参数删除
*
* @param statement
* @param params
*/
protected void delete(String statement, Object params) {
writeTemplate.delete(statement, params);
}
/**
* 批量删除
* @param statement
* @param params
*/
protected void deleteByIds(String statement, Object params) {
writeTemplate.delete(statement, params);
}
/**
* 根据参数取得<code>T</code>类型实体
*
* @param statement
* @param params
* @return
*/
@SuppressWarnings("unchecked")
protected T getEntity(String statement, Object params) {
return (T)readTemplate.queryForObject(statement, params);
}
/**
* 根据根据参数取得任意类型实体
*
* @param statement
* @param param
* @return
*/
protected Object getObject(String statement, Object param) {
return readTemplate.queryForObject(statement, param);
}
/**
* 根据参数查询列表,可分页
*
* @param statement
* @param params
* @return
*/
@SuppressWarnings("unchecked")
protected List<T> query(String statement, QueryParams params) {
if(params != null && params.getPaging() != null) {
int records = queryCount(statement, params);
//如果查询出符合条件的记录数为0,那么就直接返回一个空的List,因为后面的已经没有执行的必要
if(records == 0) {
return new ArrayList<T>(0);
}
params.getPaging().setRecords(records);
}
return (List<T>)readTemplate.queryForList(statement, params);
}
/**
* 根据参数查询列表,可分页
*
* @param statement
* @param params
* @return
*/
@SuppressWarnings("unchecked")
protected List queryEntities(String statement, QueryParams params) {
if(params != null && params.getPaging() != null) {
int records = queryCount(statement, params);
//如果查询出符合条件的记录数为0,那么就直接返回一个空的List,因为后面的已经没有执行的必要
if(records == 0) {
return new ArrayList<T>(0);
}
params.getPaging().setRecords(records);
}
return readTemplate.queryForList(statement, params);
}
/**
* 查询列表,不提供分页功能
*
* @param statement
* @param params
* @return
*/
@SuppressWarnings("unchecked")
protected List<T> query(String statement, Object params) {
return (List<T>)readTemplate.queryForList(statement, params);
}
/**
* 无参数查询列表,不提供分页功能
*
* @param statement
* @return
*/
@SuppressWarnings("unchecked")
protected List<T> query(String statement) {
return (List<T>)readTemplate.queryForList(statement);
}
/**
* 查询任意类型的对象列表。不局限于T类型的
*
* @param statement
* @param params
* @return
*/
@SuppressWarnings("unchecked")
protected List queryEntities(String statement, Object params) {
return readTemplate.queryForList(statement, params);
}
/**
* 批量新增/修改/删除。注意批量新增时是无法正确的获取自增主键的值(批处理中最后一个新增可以获取正确的值,其它皆不可以),
* 所以如果需要获取自增主键的值,不应该使用该方法。
*
* @param statement
* @param params
* @return 返回操作影响的行数
*/
protected int batch(final String statement, final Object[] params) {
return (Integer)writeTemplate.execute(new SqlMapClientCallback() {
@Override
public Object doInSqlMapClient(SqlMapExecutor executor)
throws SQLException {
executor.startBatch();
for(Object param : params) {
if(param == null) {
continue;
}
executor.update(statement, param);
}
return executor.executeBatch();
}
});
}
/**
* 根据参数判断该记录是否已存在(逻辑上存在)
*
* @param statement
* @param params
* @return
*/
protected boolean isExistEntity(String statement, Object params) {
return (Integer)readTemplate.queryForObject(statement, params) > 0;
}
/**
* 取得指定的statement的完全限定名称。形式为<code>namespace</code> + "." + <code>statement</code>
*
* @param namespace
* @param statement
* @return
*/
protected String getQualifiedName(String namespace, String statement) {
return new StringBuffer().append(namespace).append(".").append(statement).toString();
}
/**
* 取得指定的statement的完全限定名称。该方法以泛型<code>T</code>的实际类型的完全限定名 + "." + <code>statement</code>。这就要求在
* 书写iBatis配置文件时,namespace必须写成“泛型<code>T</code>的实际类型的完全限定名”。<br>
* 例如,如果有DAO实现类TravelGuideDaoImpl<TravelGuide>,TravelGuide位于com.woyoframework.travel.core.bo.guide下,
* 那么调用该方法将返回com.woyoframework.travel.core.bo.guide.TravelGuide.xxx。
*
* @param statement
* @return
*/
@SuppressWarnings("unchecked")
protected String getQualifiedName(String statement) {
/**
* 避免每次都去获取
*/
if(StringUtils.isBlank(entityClass)) {
entityClass = DomainObject.class.getName();
Type superClass = getClass().getGenericSuperclass();
if(superClass instanceof ParameterizedType) {
ParameterizedType genericType = (ParameterizedType)superClass;
Type[] typeArgs = genericType.getActualTypeArguments();
if(typeArgs.length > 0) {
entityClass = ((Class<T>)typeArgs[0]).getName();
}
}
}
return new StringBuffer().append(entityClass).append(".").append(statement).toString();
}
/**
* 根据条件查询整数结果。
*
* @param statement
* @param params
* @return
*/
protected int uniqueIntResult(String statement, Object params) {
if(params == null) {
return (Integer)readTemplate.queryForObject(statement);
}
Object result = (Integer)readTemplate.queryForObject(statement, params);
return result != null ? (Integer)result : 0;
}
/**
* 查询符合条件的记录数,仅供分页查询调用。
*
* @param statement
* @param params
* @return
* update andy_luo 2010-7-2 15:43 增加params非空判断,如果为空表示查询所有的数量
*/
protected int queryCount(String statement,QueryParams<?> params) {
if(params == null) {
return (Integer)readTemplate.queryForObject(statement + COUNT_STATEMENT_SUFFIX);
}
return (Integer)readTemplate.queryForObject(statement + COUNT_STATEMENT_SUFFIX, params);
}
}