之前在面试的时候总是有很多的面试官问我怎么配置两套或者是多套数据库连接,正好之前的项目上也用到两套数据库连接,分别是访问AS400数据库和DB2数据库,接下来就简单说说(以两套为例):
第一:jdbc.properties需要配置两套数据源的基本参数
#--------------------IBM DB2 Database Config--------------------
jdbc.driverClassName=com.ibm.db2.jcc.DB2Driver
jdbc.url=jdbc:db2://10.14.134.67:50000/manage:currentSchema=DB2INST1;
jdbc.username=db2inst1
jdbc.password=2wsx#EDC
jdbc.validationQuery=SELECT 1 from sysibm.sysdummy1
hibernate.dialect=org.hibernate.dialect.DB2Dialect
mybatis.dialect=com.pactera.powerweb.ibatis.database.DB2Dialect
#--------------------IBM DB2 as/400 Database Config--------------------
jdbc.as400.driverClassName=com.ibm.as400.access.AS400JDBCDriver
jdbc.as400.url=jdbc:as400://10.14.138.2/P4DTACFGAP;naming=sql;errors=full;
jdbc.as400.username=ecas400
jdbc.as400.password=lovecxyxl
jdbc.as400.validationQuery=SELECT 1 from sysibm.sysdummy1
hibernate.as400.dialect=org.hibernate.dialect.DB2400Dialect
mybatis.as400.dialect=com.pactera.powerweb.ibatis.database.DB2Dialect
第二:Spring配置文件配置两套bean(当然如果用的不是Spring的话道理一样),我这里因为是mybatis和hibernate都用到了,所以配置了两个,一个是hibernate访问数据库创建session的sessionFactory,另外一个是mybatis访问数据库用到的sqlMapClientTemplate
<!-- dbcp数据源 -->
<bean name="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="initialSize" value="20"/><!-- 初始化连接数量 -->
<property name="maxActive" value="100"/><!-- 最大连接数量 -->
<property name="minIdle" value="5"/><!-- 最小空闲连接 -->
<property name="maxIdle" value="20"/><!-- 最大空闲连接 -->
<property name="logAbandoned" value="true"/><!-- logAbandoned:是否在自动回收超时连接的时候打印连接的超时错误 -->
<property name="removeAbandoned" value="true"/><!-- removeAbandoned:是否自动回收超时连接 -->
<property name="removeAbandonedTimeout" value="10"/><!--removeAbandonedTimeout: 超时时间(以秒数为单位) -->
<property name="maxWait" value="60000"/><!--maxWait: 超时等待时间以毫秒为单位 10000毫秒/1000等于10秒 -->
<property name="testOnBorrow"
value="false"/><!--testOnBorrow: 指明是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个.注意: 设置为true后如果要生效,validationQuery参数必须设置为非空字符串。若配置为true,对性能有非常大的影响,性能会下降7-10倍 -->
<property name="validationQuery"
value="${jdbc.validationQuery}"/><!-- validationQuery:SQL查询,用来验证从连接池取出的连接,在将连接返回给调用者之前.如果指定,则查询必须是一个SQL SELECT并且必须返回至少一行记录 -->
<property name="testWhileIdle" value="true"></property><!--指明空闲连接是否在连接池中去除 -->
<property name="timeBetweenEvictionRunsMillis" value="60000"></property><!-- 设置空闲连接回收时长单位毫秒 -->
</bean>
<!--Hibernate配置,使用注解 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="hibernateProperties">
<props>
<!--【配置hibernate方言】 -->
<!--org.hibernate.dialect.Oracle10gDialect -->
<!--org.hibernate.dialect.Oracle9iDialect -->
<!--org.hibernate.dialect.DB2Dialect -->
<!--org.hibernate.dialect.InformixDialect -->
<!--org.hibernate.dialect.SybaseDialect -->
<!--org.hibernate.dialect.SQLServerDialect -->
<!--org.hibernate.dialect.MySQL5Dialect -->
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">false</prop><!--【是否显示SQL】 -->
<prop key="hibernate.format_sql">false</prop><!--【是否格式化SQL】 -->
<prop key="hibernate.cache.use_second_levle_cache">false</prop><!--【是否开启二级缓存】 -->
<prop key="hibernate.cache.use_query_cache">false</prop><!--【是否开启查询缓存】 -->
<prop key="hibernate.cache.provider_class">net.sf.ehcache.hibernate.EhCacheProvider
</prop><!--【使用缓存类型】 -->
<prop key="hibernate.cache.configurationResourceName">ehcache-failsafe.xml</prop><!--【缓存配置文件】 -->
</props>
</property>
<property name="packagesToScan">
<list>
<value>com.pactera.**.model</value><!-- 带注解的hibernate实体类 -->
</list>
</property>
</bean>
<!-- 配置Hibernate事务管理器 -->
<bean name="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- ibatis 配置 -->
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocations">
<list>
<value>classpath:/config/ibatis-config.xml</value>
</list>
</property>
<property name="mappingLocations">
<list>
<!-- 读取所有ibatis的sql配置 -->
<value>classpath:/com/pactera/**/*ServiceSql.xml</value>
</list>
</property>
<property name="dataSource">
<ref bean="dataSource"/>
</property>
<!-- 事务配置必须和iBatis一致 -->
<property name="useTransactionAwareDataSource" value="true"></property>
</bean>
<!-- 由于使用了注解,在BaseService中使用sqlMapClientTemplate的时候需要定义 -->
<bean id="sqlMapClientTemplate" class="org.springframework.orm.ibatis.SqlMapClientTemplate">
<property name="sqlMapClient" ref="sqlMapClient"></property>
</bean>
<!-- dbcp数据源 -->
<bean name="dataSourceAs400" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.as400.driverClassName}"></property>
<property name="url" value="${jdbc.as400.url}"></property>
<property name="username" value="${jdbc.as400.username}"></property>
<property name="password" value="${jdbc.as400.password}"></property>
<property name="initialSize" value="3"/><!-- 初始化连接数量 -->
<property name="maxActive" value="15"/><!-- 最大连接数量 -->
<property name="minIdle" value="3"/><!-- 最小空闲连接 -->
<property name="maxIdle" value="5"/><!-- 最大空闲连接 -->
<property name="logAbandoned" value="true"/><!-- logAbandoned:是否在自动回收超时连接的时候打印连接的超时错误 -->
<property name="removeAbandoned" value="true"/><!-- removeAbandoned:是否自动回收超时连接 -->
<property name="removeAbandonedTimeout" value="10"/><!--removeAbandonedTimeout: 超时时间(以秒数为单位) -->
<property name="maxWait" value="60000"/><!--maxWait: 超时等待时间以毫秒为单位 10000毫秒/1000等于10秒 -->
<property name="testOnBorrow"
value="false"/><!--testOnBorrow: 指明是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个.注意: 设置为true后如果要生效,validationQuery参数必须设置为非空字符串。若配置为true,对性能有非常大的影响,性能会下降7-10倍 -->
<property name="validationQuery"
value="${jdbc.as400.validationQuery}"/><!-- validationQuery:SQL查询,用来验证从连接池取出的连接,在将连接返回给调用者之前.如果指定,则查询必须是一个SQL SELECT并且必须返回至少一行记录 -->
<property name="testWhileIdle" value="true"></property><!--指明空闲连接是否在连接池中去除 -->
<property name="timeBetweenEvictionRunsMillis" value="60000"></property><!-- 设置空闲连接回收时长单位毫秒 -->
</bean>
<!--Hibernate配置,使用注解 -->
<bean id="sessionFactoryAs400" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSourceAs400"/>
<property name="hibernateProperties">
<props>
<!--【配置hibernate方言】 -->
<!--org.hibernate.dialect.Oracle10gDialect -->
<!--org.hibernate.dialect.Oracle9iDialect -->
<!--org.hibernate.dialect.DB2Dialect -->
<!--org.hibernate.dialect.InformixDialect -->
<!--org.hibernate.dialect.SybaseDialect -->
<!--org.hibernate.dialect.SQLServerDialect -->
<!--org.hibernate.dialect.MySQL5Dialect -->
<prop key="hibernate.dialect">${hibernate.as400.dialect}</prop>
<prop key="hibernate.show_sql">false</prop><!--【是否显示SQL】 -->
<prop key="hibernate.format_sql">false</prop><!--【是否格式化SQL】 -->
<prop key="hibernate.cache.use_second_levle_cache">false</prop><!--【是否开启二级缓存】 -->
<prop key="hibernate.cache.use_query_cache">false</prop><!--【是否开启查询缓存】 -->
<prop key="hibernate.cache.provider_class">net.sf.ehcache.hibernate.EhCacheProvider
</prop><!--【使用缓存类型】 -->
<prop key="hibernate.cache.configurationResourceName">ehcache-failsafe.xml</prop><!--【缓存配置文件】 -->
</props>
</property>
<property name="packagesToScan">
<list>
<value>com.pactera.**.model</value><!-- 带注解的hibernate实体类 -->
</list>
</property>
</bean>
<!-- 配置Hibernate事务管理器 -->
<bean name="transactionManagerAs400" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactoryAs400"></property>
</bean>
<tx:advice id="txAdviceAs400" transaction-manager="transactionManagerAs400">
<tx:attributes>
<!-- 数据插入的方法 -->
<tx:method name="save*" rollback-for="Exception"/>
<tx:method name="add*" rollback-for="Exception"/>
<tx:method name="insert*" rollback-for="Exception"/>
<tx:method name="create*" rollback-for="Exception"/>
<!-- 数据修改的方法 -->
<tx:method name="update*" rollback-for="Exception"/>
<tx:method name="edit*" rollback-for="Exception"/>
<!-- 数据删除操作 -->
<tx:method name="delete*" rollback-for="Exception"/>
<tx:method name="remove*" rollback-for="Exception"/>
<tx:method name="batch*" rollback-for="Exception" propagation="NOT_SUPPORTED"/>
<tx:method name="exis*" rollback-for="Exception" propagation="NOT_SUPPORTED"/>
<!-- 剩下的查询方法 -->
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- ibatis 配置 -->
<bean id="sqlMapClientAs400" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocations">
<list>
<value>classpath:/config/ibatis-config.xml</value>
</list>
</property>
<property name="mappingLocations">
<list>
<!-- 读取所有ibatis的sql配置 -->
<value>classpath:/com/pactera/**/*ServiceSql.xml</value>
</list>
</property>
<property name="dataSource">
<ref bean="dataSourceAs400"/>
</property>
<!-- 事务配置必须和iBatis一致 -->
<property name="useTransactionAwareDataSource" value="true"></property>
</bean>
<!-- 由于使用了注解,在BaseService中使用sqlMapClientTemplate的时候需要定义 -->
<bean id="sqlMapClientTemplateAs400" class="org.springframework.orm.ibatis.SqlMapClientTemplate">
<property name="sqlMapClient" ref="sqlMapClientAs400"></property>
</bean>
第三:接下来就是我们怎样通过代码来知道用那一个数据源的问题,楼主是通过注解的方式来说明的(感觉注解还是方便一点的),这是封装好的访问数据库的基类(代码没有贴全,但是道理大家懂就行)
public class BaseService {
public static final String BEAN_ID = "baseService";
public static final boolean isIgnoreCaseWithLike = true;
@Autowired
public SessionFactory sessionFactory;
@Autowired
public SqlMapClientTemplate sqlMapClientTemplate;
@Autowired
public JdbcTemplate jdbcTemplate;
@Autowired
public SqlExecutor sqlExecutor;
public BaseService() {
}
public Session getCurrentSession() {
return this.sessionFactory.getCurrentSession();
}
public void init() throws Exception {
if (this.sqlExecutor != null) {
ReflectUtil.setFieldValue(((SqlMapClientImpl)this.sqlMapClientTemplate.getSqlMapClient()).getDelegate(), "sqlExecutor", SqlExecutor.class, this.sqlExecutor);
}
}
public <T> T get(Class entityClass, Serializable id) {
return this.getCurrentSession().get(entityClass, id);
}
public void save(Object entity) {
this.getCurrentSession().save(entity);
}
public void update(Object entity) {
this.getCurrentSession().update(entity);
}
public void saveOrUpdate(Object entity) {
this.getCurrentSession().saveOrUpdate(entity);
}
public void delete(Object entity) {
this.getCurrentSession().delete(entity);
}
public void delete(Class entityClass, Serializable id) throws DataAccessException {
this.getCurrentSession().delete(this.get(entityClass, id));
}
从代码中我们可以到主要是注入两个类在起作用sessionFactory和sqlMapClientTemplate,所以要想切换访问不同的数据库只要改变这两个类就行,所以我们可以继承这个类然后改变这个类
public class ManageBaseService extends BaseService {
@Resource(name="db2Seq")
private DB2SequenceMaxValueIncrementer sequence;
@Override
public Session getCurrentSession() {
SessionManager session = this.getClass().getAnnotation(SessionManager.class);
if(session == null){
return sessionFactory.getCurrentSession();
}
SessionFactory factory = SpringUtils.getBean(session.factory().name());
return factory.getCurrentSession();
}
通过上面重写getCurrentSession方法,获取不同的sessionFactory,这里是用SessionManage自定义的注解来完成的,另外一个也是用相同的策略
public class ManageAs400BaseService extends ManageBaseService {
@Resource(name="sqlMapClientTemplateAs400")
public SqlMapClientTemplate sqlMapClientTemplate;
@Override
@PostConstruct
public void init() throws Exception {
super.sqlMapClientTemplate = this.sqlMapClientTemplate;
super.init();
}
}
@Service("as400Pffe52Service")
@SessionManager(factory = SessionFactoryEnum.sessionFactoryAs400)
public class As400Pffe52Service extends ManageAs400BaseService {
public static final String BEAN_ID = "as400Pffe52Service"; // 请开发人员注意此处命名规范
总结:其实以上只是一种配置方式,每个项目不同也会采取不同的配置方式 ,但是可以肯定的是大体的思路应该是一样的,都是通过改变不同的session工厂和模板从而对不同的数据库进行访问。