- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
项目中是有用到多数据源的,是用AbstractRoutingDataSource这个类来实现数据源的切换。在使用的过程中,发现在一个事务中,是没办法切换数据源的。本文就简单介绍一下场景及原因。
项目中是有用到多数据源的,是用AbstractRoutingDataSource这个类来实现数据源的切换。
在使用的过程中,发现在一个事务中,是没办法切换数据源的。
下面就简单介绍一下场景及原因。
Mapper类如下:
@Mapper
public interface UserMapper {
@Results(value = {
@Result(property = "id", column = "id", javaType = Long.class, jdbcType = JdbcType.BIGINT),
@Result(property = "age", column = "age", javaType = Integer.class, jdbcType = JdbcType.INTEGER),
@Result(property = "name", column = "name", javaType = String.class, jdbcType = JdbcType.VARCHAR),
@Result(property = "createTime", column = "create_time", javaType = Date.class, jdbcType = JdbcType.DATE)
})
@Select("SELECT id, age, name, create_time FROM user WHERE id = #{id}")
User selectUser(Long id);
}
自定义AbstractRoutingDataSource:
public class MyDynamicDataSource extends AbstractRoutingDataSource {
@Setter
@Getter
private String key;
@Override
protected Object determineCurrentLookupKey() {
return key;
}
}
一些配置:
@Configuration
@MapperScan("cn.eagle.li.spring.datasource")
@EnableTransactionManagement
public class Config {
@Bean
public MyBean myBean() {
return new MyBean();
}
@Bean(name = "sqlSessionFactory")
@ConditionalOnMissingBean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("myDynamicDataSource") DataSource dataSource) throws Exception {
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
return sessionFactory.getObject();
}
@Bean(name = "transactionManager")
public DataSourceTransactionManager transactionManager(@Qualifier("myDynamicDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean("myDynamicDataSource")
public MyDynamicDataSource dataSource() {
MyDynamicDataSource myDynamicDataSource = new MyDynamicDataSource();
Map<Object, Object> targetDataSource = Maps.newHashMap();
targetDataSource.put("d1", getDataSource1());
targetDataSource.put("d2", getDataSource2());
myDynamicDataSource.setDefaultTargetDataSource(getDataSource1());
myDynamicDataSource.setTargetDataSources(targetDataSource);
return myDynamicDataSource;
}
private static DataSource getDataSource1() {
MysqlConnectionPoolDataSource dataSource = new MysqlConnectionPoolDataSource();
dataSource.setUser("root");
dataSource.setPassword("root");
dataSource.setUrl("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8");
return dataSource;
}
private static DataSource getDataSource2() {
MysqlConnectionPoolDataSource dataSource = new MysqlConnectionPoolDataSource();
dataSource.setUser("root");
dataSource.setPassword("root");
dataSource.setUrl("jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=utf-8");
return dataSource;
}
}
一个测试的bean:
@Slf4j
@Component
public class MyBean {
@Autowired
private UserMapper userMapper;
@Autowired
private MyDynamicDataSource myDynamicDataSource;
public void test1() {
myDynamicDataSource.setKey("d1");
log.info("user:{}", userMapper.selectUser(1L));
myDynamicDataSource.setKey("d2");
log.info("user:{}", userMapper.selectUser(1L));
}
@Transactional
public void test2() {
test1();
}
}
测试类:
@Slf4j
public class DataSourceMain {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(Config.class);
System.out.println(context.getBean(PlatformTransactionManager.class));
MyBean myBean = context.getBean(MyBean.class);
myBean.test1();
myBean.test2();
}
}
返回结果如下:
其中有两个数据库
test数据库里的数据如下:
test2数据库里的数据如下:
运行结果如下:
1473 [main] INFO c.eagle.li.spring.datasource.MyBean - user:User(age=2, name=3, id=1, createTime=Thu Nov 04 00:00:00 CST 2021)
1482 [main] INFO c.eagle.li.spring.datasource.MyBean - user:User(age=2, name=kkk, id=1, createTime=Sat Nov 20 00:00:00 CST 2021)
1494 [main] INFO c.eagle.li.spring.datasource.MyBean - user:User(age=2, name=kkk, id=1, createTime=Sat Nov 20 00:00:00 CST 2021)
1495 [main] INFO c.eagle.li.spring.datasource.MyBean - user:User(age=2, name=kkk, id=1, createTime=Sat Nov 20 00:00:00 CST 2021)
从结果可以看出,第一个不带事务的方法,分别从test和test2两个数据库选出了数据
而第二个带事务的方法,只从test2这一个数据库选出了两条相同的数据。
我们从这里打断点,看谁调到了这里
不带事务的调用顺序如下:
SimpleExecuto.doQuery
SimpleExecutor.prepareStatement
BaseExecutor.getConnection
SpringManagedTransaction.getConnection
SpringManagedTransaction.openConnection
DataSourceUtils.getConnection
DataSourceUtils.doGetConnection
DataSourceUtils.fetchConnection
AbstractRoutingDataSource.getConnection
AbstractRoutingDataSource.determineTargetDataSource
MyDynamicDataSource.determineCurrentLookupKey
SimpleExecutor.prepareStatement
如下:
带事务的调用顺序如下:
TransactionAspectSupport.invokeWithinTransaction
TransactionAspectSupport.createTransactionIfNecessary
AbstractPlatformTransactionManager.getTransaction
AbstractPlatformTransactionManager.startTransaction
DataSourceTransactionManager.doBegin
AbstractRoutingDataSource.getConnection
AbstractRoutingDataSource.determineTargetDataSource
MyDynamicDataSource.determineCurrentLookupKey
DataSourceTransactionManager.doBegin
如下:
从SimpleExecutor.prepareStatement
开始
不带事务情况下:
带事务的情况下:
可以看到带事务的方法中,conHolder
不为null
,从这里是可以直接获得Connection
而不带事务的方法中,conHolder
为null
,每次都会获取一个新的Connection
conHolder
的看一下是怎么获取conHolder
的,DataSourceUtils.doGetConnection
如下:
进去看一下,TransactionSynchronizationManager.getResource
如下:
继续调用,TransactionSynchronizationManager.doGetResource
如下:
从上图可以看到,conHolder
是从resources
中获取的
resources的定义如下:
private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources");
经过调试,调用顺序是这样的:
DataSourceTransactionManager.doBegin
TransactionSynchronizationManager.bindResource
DataSourceTransactionManager.doBegin
如下:
TransactionSynchronizationManager.bindResource
如下:
结论就是在事务的方法中,会提前获得一个connection
放到ThreadLocal
里,然后整个事务都会使用同一个connection
。
而不带事务的的方法,不会这么做,每次都去获得新的connection
。
我试图通过预准备语句使用同一连接执行多个查询,但无法完全实现! 代码片段: public class PostPrReqDaoImpl implements PostPrReqDaoInterface
我目前有一个 2 列宽的 DataGridView,第一列是 DataGridViewTextBoxColumn,第二列是 DataGridViewComboBoxColumn。我还有一个预生成的通用
当我在一台机器上运行以下代码时,我得到了 org.apache.tomcat.dbcp.dbcp.BasicDataSource 的 tomcat 实现,当我在另一台机器上运行它时,我得到了 org.
不确定这是否可行,但这是我的设置。 我有一台带有双启动功能的笔记本电脑。 一个一个分区我有 WinXP 和 MSAccess 2000在另一个分区上,Ubuntu 10.04,带有 apache we
我试过: czmlDataSource.load(czmlurl).then(function(){ viewer.dataSource
我有一个 TableView 和一个数组源。当我在 viewDidLoad 方法中初始化数组时,tableview 显示数组中的数据。当我从 Internet 上的 XML 数据的 URL 填充数组时
我对 DataSource 和 SessionFactory 之间的区别感到困惑。 我认为SessionFactory是一个用于检索 session 的管理器(我猜这实际上是与数据库的连接)。 Dat
我想存储大量(~数千)个字符串并能够使用通配符执行匹配。 例如,这里是一个示例内容: Folder1 文件夹 1/Folder2 Folder1/* Folder1/Folder2/Folder3 文
我有一个 DataGridView 和一个从 SQL 表填充的一些对象的列表。我曾使用两种方法将列表绑定(bind)到网格。 1.直接使用列表到数据源 grdSomeList.DataSource =
我正在尝试在 DataGridView 中设置一些内容。看起来这应该很简单,但我遇到了麻烦。我想显示三列: 代码ID 代号 带有 TypeName 的 DisplayMember 和 TypeID 的
在我的 Config.groovy我把线: grails.config.locations = [ "classpath:app-config.properties"] 我在哪里设置数据源的定义。文件
为了这个问题,假设我有一个包含各种酒类的 Excel 数据源电子表格。 (Cell A) | (Cell B) Bacardi | Rum Smirnoff | Vodka Another Vodka
由于我经常使用第三方 API,我认为创建一些 Magento 模块以实现轻松连接和查询它们会很有帮助。理想情况下,您可以像这样查询 API... $data = Mage::getModel( 'to
将后台线程频繁更新的数据源与 GUI 主线程同步的最佳方法是什么? 我应该在每个方法调用周围放置一个 pthread 互斥体吗?这对我来说似乎也很重。 编辑:我正在寻找 10.5 解决方案 最佳答案
经过几个小时的点击和试用,在查看各种帖子寻求帮助后,这段代码终于起作用了。但我希望有人帮助我理解函数(i,dat),这意味着什么?下面是我的完整代码 - function get_assignedta
我使用的是 Wildfly 10.1 版本,有两个数据源,如下所示, jdbc:mysql://${dbhostn
我正在学习数据源,我想我开始理解它,但我不明白这一段。 据我所知,MySQL 和 PostgreSQL 等数据库供应商编写了自己的不同 DataSource 接口(interface)的实现。现在,这
我有一个关于 TomEE 和使用 tomee.xml 中指定的数据源的奇怪问题。值得注意的是,我使用的是 Netbeans、TomEE 和 MySQL。在 Ubuntu 13.04(Xubuntu 最
WWDC 2019 确实充满了 iOS 的新内容以及 TableViews 和 CollectionView 的新数据源,即 UITableViewDiffableDataSource . 我已成功将
我在独立模式下运行 jboss 并将 standalone.xml 中的数据源设置为以下内容: jdbc:sqlserver://myip:1433;databaseNam
我是一名优秀的程序员,十分优秀!