一般的企业管理系统免不了要访问多个数据库,如框架数据库、仓库数据库等,如何实现Spring的多数据源事务管理?下面就以两个数据库为例,描述我的整个开发过程及出现的问题,以及最后的解决方法。
我采用的是开发环境:spring4.2 + hibernate4.2 + tomcat7.0
一、设置两个事务出现的问题
开始的设计的时候就设置两个数据源,两个SessionFactory和两个事务HibernateTransactionManager,采用HibernateTemplate进行数据库操作。下面是主要配置:
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd"
>
<import resource="applicationContext_framework.xml"/>
<import resource="applicationContext_stk.xml"/>
</beans>
导入两个针对不同数据库的配置
applicationContext_framework.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd"
>
<context:component-scan base-package="com.framework.dao"/>
<context:component-scan base-package="com.framework.dao.mssql"/>
<context:component-scan base-package="com.framework.service"/>
<context:component-scan base-package="com.framework.action"/>
<bean id="propertyConfigurerFW"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:framework.properties"/>
</bean>
<bean id="datasourceFW"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="${jdbc.driverClassName}"
p:url="${jdbc.url}"
p:username="${jdbc.username}"
p:password="${jdbc.password}" />
<bean id="sessionFactoryFW"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="datasourceFW"/>
<!-- -->
<property name="packagesToScan" value="com.framework.domain"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">com.util.SQLServerDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<!-- 4.0-->
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
</props>
</property>
</bean>
<bean id="hibernateTemplateFW"
class="org.springframework.orm.hibernate4.HibernateTemplate"
p:sessionFactory-ref="sessionFactoryFW" />
<bean id="transactionManagerFW"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactoryFW" />
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManagerFW">
<tx:attributes>
<tx:method name="select*" read-only="true" propagation="REQUIRED"/>
<tx:method name="get*" read-only="true" propagation="REQUIRED"/>
<tx:method name="load*" read-only="true" propagation="REQUIRED"/>
<tx:method name="find*" read-only="true" propagation="REQUIRED"/>
<tx:method name="query*" read-only="true" propagation="REQUIRED"/>
<tx:method name="read*" read-only="true" propagation="REQUIRED"/>
<tx:method name="sync*"/>
<tx:method name="*" propagation="REQUIRED" rollback-for="Exception"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.framework.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
</beans>
applicationContext_stk.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd"
>
<context:component-scan base-package="com.stk.dao.mssql"/>
<context:component-scan base-package="com.stk.service"/>
<context:component-scan base-package="com.stk.action"/>
<bean id="datasourceStk"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="${jdbcstk.driverClassName}"
p:url="${jdbcstk.url}"
p:username="${jdbcstk.username}"
p:password="${jdbcstk.password}" />
<bean id="sessionFactoryStk"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="datasourceStk"/>
<!-- -->
<property name="packagesToScan" value="com.stk.domain"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">com.util.SQLServerDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.">true</prop>
<!-- 4.0-->
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
</props>
</property>
</bean>
<bean id="hibernateTemplateStk"
class="org.springframework.orm.hibernate4.HibernateTemplate"
p:sessionFactory-ref="sessionFactoryStk" />
<bean id="transactionManagerStk"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactoryStk" />
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManagerStk">
<tx:attributes>
<tx:method name="select*" read-only="true"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="load*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="query*" read-only="true"/>
<tx:method name="read*" read-only="true"/>
<tx:method name="*" propagation="REQUIRED" rollback-for="Exception"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.stk.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
</beans>
设计Domain层,Service层,Dao层
关于Dao层的设计,我设计了一个BasoDao abstract类,以供所有的Dao继承。由于需要操作多个数据库,在BasoDao类中,加入了一个abstract函数获取hibernatetemplate.getSession函数,先通过getCurrentSession()获取,如果失败再通过openSession获取。
基本结构如下:
package com.framework.dao;
/**
* DAO基类,其它DAO可以直接继承这个DAO,不但可以复用共用的方法,还可以获得泛型的好处。
*/
public abstract class BaseDao<T> {
private Class<T> entityClass;
public abstract HibernateTemplate getHibernateTemplate();
/**
* 通过反射获取子类确定的泛型类
*/
public BaseDao() {
Type genType = getClass().getGenericSuperclass();
Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
entityClass = (Class) params[0];
}
/**
* 根据ID加载PO实例
*
* @param id
* @return 返回相应的持久化PO实例
*/
public T load(Serializable id) {
return (T) getHibernateTemplate().load(entityClass, id);
}
/**
* 根据ID获取PO实例
*
* @param id
* @return 返回相应的持久化PO实例
*/
public T get(Serializable id) {
return (T) getHibernateTemplate().get(entityClass, id);
}
/**
* 获取PO的所有对象
*
* @return
*/
public List<T> loadAll() {
return getHibernateTemplate().loadAll(entityClass);
}
/**
* 保存PO
*
* @param entity
*/
public void save(T entity) {
getHibernateTemplate().save(entity);
}
public void saveOrUpdate(T entity){
getHibernateTemplate().saveOrUpdate(entity);
}
/**
* 删除PO
*
* @param entity
*/
public void remove(T entity) {
getHibernateTemplate().delete(entity);
}
/**
* 更改PO
*
* @param entity
*/
public void update(T entity) {
getHibernateTemplate().update(entity);
}
public Session getSession() {
Session session = getHibernateTemplate().getSessionFactory().getCurrentSession();
if (session == null)
session = getHibernateTemplate().getSessionFactory().openSession();
return session;
}
}
针对两种数据,再设计两个Dao类,继承于BaseDao.
框架DAO,FrameworkDao,通过spring自动注入hibernateTemplateFW
public class FrameworkDao<T> extends BaseDao<T> {
@Autowired
private HibernateTemplate hibernateTemplateFW;
@Override
public HibernateTemplate getHibernateTemplate() {
return hibernateTemplateFW;
}
}
仓库DAO,StkDao,通过spring自动注入hibernateTemplateStk
public class StkDao<T> extends BaseDao<T> {
@Autowired
private HibernateTemplate hibernateTemplateStk;
@Override
public HibernateTemplate getHibernateTemplate() {
return hibernateTemplateStk;
}
}
经过测试,这个的配置出现如下的情况:
1.框架的事务配置没有起作用,查询是没问题的,但增删改不成功。
调用框架的dao的方法的时候,会出下如下错误信息:
Could not retrieve pre-bound Hibernate session
Could not obtain transaction-synchronized Session for current thread
分析了hibernatetemplate的执行代码:
try {
session = this.getSessionFactory().getCurrentSession();
} catch (HibernateException var12) {
this.logger.debug("Could not retrieve pre-bound Hibernate session", var12);
}
if(session == null) {
session = this.getSessionFactory().openSession();
session.setFlushMode(FlushMode.MANUAL);
isNew = true;
}
getCurrentSession()返回null, session.setFlushMode(FlushMode.MANUAL),增删改需要COMMIT,但MANUAL小于COMMIT
protected void checkWriteOperationAllowed(Session session) throws InvalidDataAccessApiUsageException {
if(this.isCheckWriteOperations() && session.getFlushMode().lessThan(FlushMode.COMMIT)) {
throw new InvalidDataAccessApiUsageException("Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove \'readOnly\' marker from transaction definition.");
}
}
2.stk事务一切正常。
出现这样的问题,后来我把如下配置注释掉:
<import resource="applicationContext_stk.xml"/>
对框架的事务配置起作用了,增删改都没问题。
结论:spring的jdbc事务(hibernate事务)只支持一个事务配置。
二、解决的方法:
1.不要使用hibernatetemplate,直接用SessionFactory
2.采用atomikos框架
3.采用jta事务,但tomcat不支持,只能用jboss,weblogic等支持jta的服务器了。
分享到:
相关推荐
实现系统对多数据源的操作。 实现系统对多数据源的分布式事务管理,包括事务的提交和回滚。
实现系统对多数据源的操作。 实现系统对多数据源的分布式事务管理,包括事务的提交和回滚。
1.1.2 解决方案 1 1.1.3 工作原理 3 1.2 配置Spring IoC容器中的Bean 4 1.2.1 问题 4 1.2.2 解决方案 4 1.2.3 工作原理 4 1.3 调用构造程序创建Bean 14 1.3.1 问题 14 1.3.2 解决方案 14 1.3.3 ...
1.1.2 解决方案 1 1.1.3 工作原理 3 1.2 配置Spring IoC容器中的Bean 4 1.2.1 问题 4 1.2.2 解决方案 4 1.2.3 工作原理 4 1.3 调用构造程序创建Bean 14 1.3.1 问题 14 1.3.2 解决方案 14 1.3.3 ...
Spring框架为开发提供了一系列的解决方案,比如利用控制反转的核心特性,并通过依赖注入实现控制反转来实现管理对象生命周期容器化,利用面向切面编程进行声明式的事务管理,整合多种持久化技术管理数据访问,提供...
增加mybatis多数据源操作,引用seate1.2处理分布式事务,多数据源事务,引用shardingSphere进行分库分表处理 项目布署图 它有什么作用 本框架使用spring cloud为基本架构,结合阿里dubbo + nacos提供服务层 再结合...
9.9. 公共问题的解决方案 9.9.1. 对一个特定的 DataSource 使用错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 11.1....
9.9. 公共问题的解决方案 9.9.1. 对一个特定的 DataSource 使用错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 11.1. 简介 ...
8.4.3 Spring的数据源实现类 8.5 小结 第9章 Spring的事务管理 9.1 数据库事务基础知识 9.1.1 何为数据库事务 9.1.2 数据并发的问题 9.1.3 数据库锁机制 9.1.4 事务隔离级别 9.1.5 JDBC对事务支持 9.2 ThreadLocal...
9.5.1. 理解Spring的声明式事务管理实现 9.5.2. 第一个例子 9.5.3. 回滚 9.5.4. 为不同的bean配置不同的事务语义 9.5.5. <tx:advice/> 有关的设置 9.5.6. 使用 @Transactional 9.5.7. 事务传播 9.5.8. 通知...
Seata是一个开源的分布式事务解决方案,用于解决分布式系统中的事务一致性问题。它提供了高性能和高可靠性的分布式事务支持,可以在微服务架构中保证数据的一致性和可靠性。 Seata的核心概念包括三个组件:事务协调...
9.9. 公共问题的解决方案 9.9.1. 对一个特定的 DataSource 使用错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 11.1....
9.5.1. 理解Spring的声明式事务管理实现 9.5.2. 第一个例子 9.5.3. 回滚 9.5.4. 为不同的bean配置不同的事务语义 9.5.5. <tx:advice/> 有关的设置 9.5.6. 使用 @Transactional 9.5.7. 事务传播 9.5.8. 通知...
8.4.3 Spring的数据源实现类 8.5 小结 第9章 Spring的事务管理 9.1 数据库事务基础知识 9.1.1 何为数据库事务 9.1.2 数据并发的问题 9.1.3 数据库锁机制 9.1.4 事务隔离级别 9.1.5 JDBC对事务支持 9.2 ThreadLocal...
Seata是alibaba开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。 1、TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的ID。 2、XID 在微服务调用链路的...
虽然可以通过属性文件(在属性文件中可以指定 JDBC 事务的数据源、全局作业和/或触发器侦听器、插件、线程池,以及更多)配置 Quartz,但它根本没有与应用程序服务器的上下文或引用集成在一起。结果就是作业不能访问...
3. 在session 中完成对数据的增删改查和事务提交等. 4. 在用完之后关闭session 。 5. 在java 对象和 数据库之间有做mapping 的配置文件,也通常是xml 文件。 mybatis实战教程(mybatis in action)之一:开发环境搭建 ...
以上是Spring+Hibernate将文件二进制数据持久化到数据库的解决方案,而Struts通过将表单中file类型的组件映射为ActionForm中类型为org.apache.struts.upload. FormFile的属性来获取表单提交的文件数据。 工程...
“基于SSM框架开发的数据学院教务管理系统”是一个专为数据学院量身定制的教务管理解决方案。该系统采用SSM(Spring + SpringMVC + MyBatis)框架进行开发,结合了前端展示、后端逻辑处理以及数据库存储等多个层面,...
基于SSM(Spring+SpringMVC+MyBatis)和Vue.js的...总之,基于SSM+Vue的农业信息管理系统是一个功能强大、易于使用的农业信息化解决方案,可以帮助农民和农业企业更好地管理和运营农业生产活动,提高生产效率和经济效益。