Spring配置多数据源以及基于Atomikos的事务一致性配置

开发生涯第一篇博客,请各位同仁多多指教微笑

    最近项目中需要用到多个数据源,查阅资料后完成了配置,包括多数据源对应的分布式事务的配置,这里用的是Atomikos,Atomikos是一款Java/JTA 事务处理工具,可以在多数据源的环境中进行分布式事务管理。在该博文中只配置2个mysql的数据源作示范,其他的诸如SqlServer,oracle等数据库的配置请查阅相关资料。

    首先,集成Atomikos需用用到的jar包可以到该链接进行下载,请点击此处下载

    集成了Atomikos之后,再在项目的applicationContext.xml文件中作如下配置(这里只给出与数据源和事务相关的配置,其他的基础配置请查阅相关资料):

1、数据源配置(mysql的连接属性这里是另外写了一个.properties配置文件,然后用el表达式访问的):

<bean id="MSDataSource1" class="com.atomikos.jdbc.AtomikosDataSourceBean" 
        init-method="init" destroy-method="close">
        <!-- Set unique name for this DataSource -->  
        <property name="uniqueResourceName"><value>ms1</value></property>
        <!-- Set XADatasource class name-->  
        <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
        <property name="xaProperties">
            <props>
                <prop key="user">${database1.user}</prop>
                <prop key="password">${database1.passwd}</prop>
                <prop key="url">jdbc:mysql://${database1.url}:${database1.port}/${database1.name}</prop>
            </props>
        </property>
        <!-- set properties for datasource connection pool -->  
        <property name="poolSize" value="3" />
        <!-- 管理 Connection 被占用的时间 -->
        <!-- 如果不设置这个值,Atomikos使用默认的300秒(即5分钟),那么在处理大批量数据读取的时候,一旦超过5分钟,就会抛出类似 Resultset is close 的错误 -->
        <property name="reapTimeout"><value>20000</value></property>  
    </bean>
    
    <bean id="MSDataSource2" class="com.atomikos.jdbc.AtomikosDataSourceBean" 
        init-method="init" destroy-method="close">
        <!-- Set unique name for this DataSource -->  
        <property name="uniqueResourceName"><value>ms2</value></property>
        <!-- Set XADatasource class name-->  
        <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
        <property name="xaProperties">
            <props>
                <prop key="user">${database2.user}</prop>
                <prop key="password">${database2.passwd}</prop>
                <prop key="url">jdbc:mysql://${database2.url}:${database2.port}/${database2.name}</prop>
            </props>
        </property>
        <!-- set properties for datasource connection pool -->  
        <property name="poolSize" value="3" />
        <!-- 管理 Connection 被占用的时间 -->
        <!-- 如果不设置这个值,Atomikos使用默认的300秒(即5分钟),那么在处理大批量数据读取的时候,一旦超过5分钟,就会抛出类似 Resultset is close 的错误 -->
        <property name="reapTimeout"><value>20000</value></property>  
    </bean>
2、SessionFactory配置:
<bean id="MSSessionFactory1"
		class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<!-- 				<prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
				<prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop> -->
				<prop key="hibernate.show_sql">true</prop>
				<prop key="hibernate.use_outer_join">true</prop>
				<prop key="hibernate.jdbc.fetch_size">30</prop>
				<prop key="hibernate.jdbc.batch_size">30</prop>
				<prop key="hibernate.jdbc.batch_versioned_data">true</prop>
				<prop key="hibernate.cache.use_second_level_cache">true</prop>
				<prop key="hibernate.cache.use_query_cache">true</prop>
				<prop key="hibernate.cache.provider_class">net.sf.ehcache.hibernate.EhCacheProvider</prop>
				<prop key="hibernate.cache.provider_configuration_file_resource_path">/resources/ehcache-cabinet-eip.xml</prop>
				<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory
				</prop>
				<prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext
				</prop>
				<prop key="javax.persistence.validation.mode">none</prop>
			</props>
		</property>
		<property name="dataSource" ref="MSDataSource1" />
		<property name="packagesToScan">
			<list>
				<value>com.tieline.model</value>
			</list>
		</property>
	</bean>
	
	<bean id="MSSessionFactory2"
		class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
				<!-- <prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
				<prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop> -->
				<prop key="hibernate.show_sql">true</prop>
				<prop key="hibernate.use_outer_join">true</prop>
				<prop key="hibernate.jdbc.fetch_size">30</prop>
				<prop key="hibernate.jdbc.batch_size">30</prop>
				<prop key="hibernate.jdbc.batch_versioned_data">true</prop>
				<prop key="hibernate.cache.use_second_level_cache">true</prop>
				<prop key="hibernate.cache.use_query_cache">true</prop>
				<prop key="hibernate.cache.provider_class">net.sf.ehcache.hibernate.EhCacheProvider</prop>
				<prop key="hibernate.cache.provider_configuration_file_resource_path">/resources/ehcache-cabinet-eip.xml</prop>
				<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory
				</prop>
				<prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext
				</prop>
				<prop key="javax.persistence.validation.mode">none</prop>
			</props>
		</property>
		<property name="dataSource" ref="MSDataSource2" />
		<property name="packagesToScan">
			<list>
				<value>com.tieline.model</value>
			</list>
		</property>
	</bean>

3、事务配置

	<!-- atomikos事务管理器 -->
    <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" 
        init-method="init" destroy-method="close">
        <property name="forceShutdown">
            <value>true</value>
        </property>
    </bean>

    <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
        <property name="transactionTimeout" value="300" />
    </bean>

    <!-- spring 事务管理器 -->
    <bean id="springTransactionManager"
        class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManager" ref="atomikosTransactionManager"/>
        <property name="userTransaction" ref="atomikosUserTransaction" />
        <property name="allowCustomIsolationLevels" value="true"/> 
    </bean>

    <!-- 使用annotation定义事务,对于要加入事物的类,只需对该类加 @Transactional  -->
    <tx:annotation-driven transaction-manager="springTransactionManager" />

接下来,我们写两个与上述两个数据源相对应的dao,获取访问这两个数据源的session对象,代码如下(接口省略):

package core.dao
import java.lang.reflect.ParameterizedType;
import javax.annotation.Resource;
import org.apache.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.stereotype.Repository;
import core.dao.BaseDao;

@Repository
public class BaseDaoImpl<E> implements BaseDao<E> {

	private SessionFactory sessionFactory;
	protected Class<E> entityClass;
	
	@SuppressWarnings("unchecked")
	public BaseDaoHisImpl() {
		ParameterizedType superClass = (ParameterizedType) this.getClass().getGenericSuperclass();
		this.entityClass = (Class<E>) superClass.getActualTypeArguments()[0];
	}

	public SessionFactory getSessionFactory() {
		return this.sessionFactory;
	}

	@Resource(name = "MSSessionFactory1")
	public void setSF(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	public Session getSession() {
		return this.sessionFactory.getCurrentSession();
	}

}

package core.dao
import java.lang.reflect.ParameterizedType;
import javax.annotation.Resource;
import org.apache.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.stereotype.Repository;
import core.dao.BaseDao;

@Repository
public class BaseDaoHisImpl<E> implements BaseDaoHis<E> {

	private SessionFactory sessionFactory;
	protected Class<E> entityClass;
	
	@SuppressWarnings("unchecked")
	public BaseDaoHisImpl() {
		ParameterizedType superClass = (ParameterizedType) this.getClass().getGenericSuperclass();
		this.entityClass = (Class<E>) superClass.getActualTypeArguments()[0];
	}

	public SessionFactory getSessionFactory() {
		return this.sessionFactory;
	}

	@Resource(name = "MSSessionFactory2")
	public void setSF(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	public Session getSession() {
		return this.sessionFactory.getCurrentSession();
	}

}


有了上述两个基础类,我们就可以在业务类中调用getSession()方法来获取session对象来访问数据库了,当然了,也可以在这两个基类里封装一些公用性的增删改查的方法,供给业务类调用,代码编写完成之后,测试如下:在同一个service方法里通过这两个基类去访问那两个数据源,且在方法上打上@Transactional来开启事务,先在第一个dao里对第一个库做了一个正常的插入,然后在第二个dao里对第二个库做了一个插入,且另外写了一个1/0的表达式来制造一个异常,结果发现对第一个库的插入同样并没有生效,发生了回滚,也就是说,对两个库的操作保持了事务一致性。

    最后,还有一个问题,按上述步骤启动项目没有任何问题,但是在浏览器打开时报了找不到sessionFactory这个bean的异常,这个是OpenSessionInViewFilter的问题。这个类中有个属性叫sessionFactoryBeanName默认值为sessionFactory,而我在上述配置中的SessionFactory不叫这个名字,查看API有如下解释:
Looks up the SessionFactory in Spring's root web application context. Supports a "sessionFactoryBeanName"
 filter init-param in web.xml; the default bean name is "sessionFactory". Looks up the SessionFactory on each request,
 to avoid initialization order issues (when using ContextLoaderServlet, the root application context will get initialized after this filter).
 问题就出在这里,我们在web.xml中配置OpenSessionInViewFilter这个过滤器的时候,需要指定一个默认的SessionFactory的名字,要不然它就默认为sessionFactory,我们上面的配置中取的名字是MSSessionFactory1和MSSessionFactory2,随便取一个,放在OpenSessionInViewFilter过滤器的参数那里就可以了,代码如下:

<filter>
    <filter-name>openSessionInView</filter-name>
    <filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
    <async-supported>true</async-supported>
    <init-param>  
        <param-name>sessionFactoryBeanName</param-name>  
        <param-value>MSSessionFactory1</param-value>  
    </init-param> 
  </filter>
  <filter-mapping>
    <filter-name>openSessionInView</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
至此,spring多数据源以及事务的配置就全部结束了,欢迎各位道友沟通交流 微笑





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot中,我们可以使用`@Transactional`注解来实现事务管理。在多数据源的情况下,我们需要使用JTA(Java Transaction API)来实现分布式事务一致性。 JTA提供了一个全局事务管理器来管理多个数据源的事务,在Spring Boot中,我们可以使用类似Atomikos、Bitronix和Narayana等JTA事务管理器。 具体来说,我们需要在Spring Boot中配置JTA事务管理器,并在代码中使用`@Transactional`注解来声明事务。同时,我们还需要使用`@Transactional(value = "transactionManager")`注解来指定特定数据源的事务管理器。 具体的实现过程可以参考以下步骤: 1. 在Spring Boot中添加JTA事务管理器的依赖,例如Atomikos: ```xml <dependency> <groupId>com.atomikos</groupId> <artifactId>atomikos-core</artifactId> <version>4.0.6</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>atomikos-essentials</artifactId> <version>4.0.6</version> </dependency> ``` 2. 配置数据源和JTA事务管理器: ```properties spring.datasource.url=jdbc:mysql://localhost:3306/db1 spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource2.url=jdbc:mysql://localhost:3306/db2 spring.datasource2.username=root spring.datasource2.password=root spring.datasource2.driver-class-name=com.mysql.jdbc.Driver spring.jta.atomikos.connectionfactory.borrow-connection-timeout=30 spring.jta.atomikos.connectionfactory.min-pool-size=5 spring.jta.atomikos.connectionfactory.max-pool-size=20 spring.jta.atomikos.connectionfactory.test-query=SELECT 1 spring.jta.atomikos.datasource.ds1.xa-data-source-class-name=com.mysql.jdbc.jdbc2.optional.MysqlXADataSource spring.jta.atomikos.datasource.ds1.unique-resource-name=ds1 spring.jta.atomikos.datasource.ds1.user=root spring.jta.atomikos.datasource.ds1.password=root spring.jta.atomikos.datasource.ds1.xa-properties.url=jdbc:mysql://localhost:3306/db1 spring.jta.atomikos.datasource.ds2.xa-data-source-class-name=com.mysql.jdbc.jdbc2.optional.MysqlXADataSource spring.jta.atomikos.datasource.ds2.unique-resource-name=ds2 spring.jta.atomikos.datasource.ds2.user=root spring.jta.atomikos.datasource.ds2.password=root spring.jta.atomikos.datasource.ds2.xa-properties.url=jdbc:mysql://localhost:3306/db2 ``` 3. 在代码中使用`@Transactional`注解来声明事务,并使用`@Transactional(value = "transactionManager")`注解来指定特定数据源的事务管理器: ```java @Service public class MyService { @Autowired private MyRepository myRepository; @Transactional(value = "transactionManager") public void doSomething() { // 在这里使用myRepository进行数据库操作 } @Transactional(value = "transactionManager2") public void doSomethingElse() { // 在这里使用myRepository进行数据库操作 } } ``` 通过以上步骤,我们可以在Spring Boot中实现多数据源事务管理,并且保证分布式事务一致性

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值