资料
mysql中set autocommit=0与start transaction区别
事务管理
Spring通过设置autocommit 为 0来实现事务:
org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin
事务中@DS失效
绑定连接
org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin
- 开启事务
con.setAutoCommit(false)
; - 绑定Connection
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
- 报错释放Connect
DataSourceUtils.releaseConnection(con, obtainDataSource());
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
txObject.setReadOnly(definition.isReadOnly());
// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
prepareTransactionalConnection(con, definition);
txObject.getConnectionHolder().setTransactionActive(true);
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// Bind the connection holder to the thread.
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
}
catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, obtainDataSource());
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
org.springframework.transaction.support.TransactionSynchronizationManager#bindResource
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
public static void bindResource(Object key, Object value) throws IllegalStateException {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Assert.notNull(value, "Value must not be null");
Map<Object, Object> map = resources.get();
// set ThreadLocal Map if none found
if (map == null) {
map = new HashMap<>();
resources.set(map);
}
Object oldValue = map.put(actualKey, value);
// Transparently suppress a ResourceHolder that was marked as void...
if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {
oldValue = null;
}
if (oldValue != null) {
throw new IllegalStateException(
"Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread");
}
}
获取连接
org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection
- 优先取线程绑定的连接;
- 否则,从DataSource里面获取(@DS的切换数据源的逻辑在这里)
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
Assert.notNull(dataSource, "No DataSource specified");
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
conHolder.requested();
if (!conHolder.hasConnection()) {
logger.debug("Fetching resumed JDBC Connection from DataSource");
conHolder.setConnection(fetchConnection(dataSource));
}
return conHolder.getConnection();
}
// Else we either got no holder or an empty thread-bound holder here.
logger.debug("Fetching JDBC Connection from DataSource");
Connection con = fetchConnection(dataSource);
......
}
对话GPT
1. 事务中@DS失效
@DS注解是基于AOP实现的,它通过拦截器来切换数据源。在Spring中,AOP是通过代理模式实现的。当一个Bean被Spring容器代理后,调用该Bean的方法时,实际上是调用代理对象的方法,而不是原始对象的方法。因此,如果在事务中调用带有@DS注解的方法,实际上是调用的代理对象的方法,而不是原始对象的方法。
默认情况下,Spring的事务管理器和数据源切换器是分开的。事务管理器在事务开始时获取连接,然后将该连接绑定到当前线程上,直到事务结束后才释放连接。而数据源切换器是通过拦截器来切换数据源,它在方法执行前切换数据源,在方法执行后再切换回原始数据源。因此,在事务中使用@DS注解时,可能会出现数据源切换失效的情况,即在事务中切换数据源后,该数据源可能会被后续的操作覆盖掉,导致数据源切换失效。
为了解决这个问题,可以在事务管理器和数据源切换器之间添加一个动态数据源切换器。动态数据源切换器可以在事务开始时获取当前线程绑定的连接,然后根据连接的元数据来切换数据源。这样,即使在事务中切换数据源,动态数据源切换器也可以根据连接的元数据来切换回原始数据源,保证数据源切换的正确性。
在Spring Boot中,可以使用@Transactional注解来开启事务,并使用@DS注解来切换数据源。如果在事务中使用@DS注解时出现失效问题,可以考虑使用DynamicDataSourceTransactionManager来作为事务管理器,从而解决数据源切换失效的问题。同时,需要确保动态数据源切换器和数据源切换拦截器的顺序正确,以保证数据源切换的正确性。
2. 连接绑定到当前线程 在哪个方法中
在Spring框架中,将连接绑定到当前线程是通过TransactionSynchronizationManager类实现的。具体来说,当一个事务开启时,事务管理器会获取一个数据库连接,并将该连接和当前线程进行绑定,以便在事务管理器操作数据库时使用该连接。当事务结束后,事务管理器会释放该连接,并将该连接和当前线程的绑定解除。
在TransactionSynchronizationManager类中,与连接绑定和解绑的方法包括:
bindResource(Object key, Object value)方法
该方法用于将一个资源对象和当前线程进行绑定。在事务管理器操作数据库时,可以通过TransactionSynchronizationManager.getResource(Objectkey)方法获取该资源对象,并使用该对象进行数据库操作。在Spring中,数据库连接就是一种资源对象,可以通过DataSourceUtils.getConnection(DataSource dataSource)方法获取连接,并将连接和当前线程进行绑定。unbindResource(Object key)方法
该方法用于将一个资源对象和当前线程的绑定解除。在事务结束后,事务管理器会调用该方法将连接和当前线程的绑定解除。getResource(Object key)方法
该方法用于获取一个资源对象,该对象已经和当前线程进行了绑定。在Spring中,可以通过该方法获取当前线程绑定的数据库连接。
/需要注意的是,为了确保连接和当前线程的绑定在整个事务过程中都有效,需要使用线程安全的方式来绑定和解绑连接。在Spring中,可以使用TransactionSynchronizationManager类来实现线程安全的连接绑定和解绑。同时,还需要确保在事务结束后,将连接和当前线程的绑定解除,以避免连接泄漏和资源浪费。在Spring中,可以通过TransactionSynchronizationManager类的事务同步器机制来实现连接的自动解绑。
3. spring获取Connection会优先读取线程绑定的Connection吗
在Spring中,当从数据源中获取连接时,首先会检查当前线程中是否已经有一个绑定的连接。如果有,就会直接使用该连接,而不会重新获取一个新的连接。这个机制是通过TransactionSynchronizationManager类实现的。
/
TransactionSynchronizationManager类是Spring事务管理器的核心类,它提供了一系列静态方法,用于将对象与当前线程进行绑定,并在事务结束后自动解绑。在Spring中,获取数据库连接时,可以使用DataSourceUtils类的getConnection方法,该方法内部会优先尝试从当前线程绑定的连接中获取连接。只有在当前线程没有绑定连接时,才会从数据源中获取一个新的连接。
/
需要注意的是,获取当前线程绑定的连接,需要使用DataSourceUtils类的getConnection方法,而不是直接使用Connection对象。因为在Spring中,使用Connection对象获取数据源时,并不会检查当前线程中是否已经有一个绑定的连接,而是直接从数据源中获取一个新的连接。因此,如果在事务中直接使用Connection对象获取数据源,可能会导致连接泄漏和资源浪费。
/
综上所述,当使用Spring获取数据库连接时,会优先尝试从当前线程绑定的连接中获取连接。这个机制可以避免重复获取连接和连接泄漏,提高程序性能和资源利用率。
4. 如果线程没有绑定Connection呢文章来源:https://www.toymoban.com/news/detail-487994.html
如果当前线程没有绑定连接,Spring会从数据源中获取一个新的连接。这个新的连接会在事务结束后自动关闭和释放。
/
在Spring中,获取新的连接的方式取决于使用的是哪种事务管理器。如果使用的是DataSourceTransactionManager事务管理器,那么在事务开始时,该事务管理器会获取一个新的连接,并将该连接绑定到当前线程上。在事务提交或回滚时,该连接会被自动关闭和释放,同时解绑当前线程。如果在事务过程中发生异常,该连接也会被自动关闭和释放。
/
如果使用的是JtaTransactionManager事务管理器,那么在事务开始时,该事务管理器会协调多个数据源中的事务,从而保证多个数据源之间的一致性。在这种情况下,每个数据源都会创建一个连接,并将连接绑定到当前线程上。在事务结束时,该事务管理器会将所有连接都关闭和释放,并解绑当前线程。
/
需要注意的是,在使用事务管理器时,不要手动关闭和释放连接,否则会导致事务管理器出现异常。如果需要在事务中执行一些特殊的操作(如设置事务隔离级别、设置连接属性等),可以使用TransactionCallback接口的doInTransaction方法,在事务中执行这些操作。在这种情况下,Spring会为每个事务创建一个新的连接,并在事务结束后自动关闭和释放该连接。文章来源地址https://www.toymoban.com/news/detail-487994.html
到了这里,关于窥探系列之多数据源切换的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!