【问题标题】:RoutingDataSource is not switching context on transactionsRoutingDataSource 没有在事务上切换上下文
【发布时间】:2019-08-28 14:31:11
【问题描述】:

我正在尝试使用 spring 类实现数据库读/写分离(我使用了本教程)

抽象路由数据源

我正在开发的应用程序正在使用多线程和连接池 (hikari)。所以我为每个租户创建了一个(主/副本数据源)

这就是我创建数据源的方式

公共数据源路由数据源(字符串tenantId, 字符串数据库MasterUrl, 字符串数据库ReplicaUrl, 字符串用户, 字符串密码){ final RoutingDataSource routingDataSource = new RoutingDataSource();

    final DataSource masterDataSource = buildTargetDataSource(tenantId,
        cachePrepStmtsValue,
        prepStmtCacheSize,
        prepStmtCacheSqlLimit,
        databaseMasterUrl,
        driverClass,
        user,
        password,
        MASTER_DATASOURCE_PREFIX);
    final DataSource replicaDataSource = buildTargetDataSource(poolName + tenantId,
        cachePrepStmtsValue,
        prepStmtCacheSize,
        prepStmtCacheSqlLimit,
        databaseReplicaUrl,
        driverClass,
        user,
        password,
        REPLICA_DATASOURCE_PREFIX);
    final Map<Object, Object> targetDataSources = new HashMap<>();
    targetDataSources.put(DbContext.DbType.MASTER, masterDataSource);
    targetDataSources.put(DbContext.DbType.REPLICA, replicaDataSource);
    routingDataSource.setTargetDataSources(targetDataSources);
    routingDataSource.setDefaultTargetDataSource(masterDataSource);
    routingDataSource.afterPropertiesSet();
    return routingDataSource;
}

这是确定上下文的方式

  public class RoutingDataSource extends AbstractRoutingDataSource {
        @Override
        protected Object determineCurrentLookupKey() {
            return DbContext.getDbType();

        }

这就是我告诉事务切换上下文的方式

  @Transactional(readOnly = true)
    public Opportunite consulter(UUID personUuid) {
      DbContext.setDbType(DbContext.DbType.REPLICA);
        //some work
        DbContext.reset();
        return some_result ;
    }

代码编译正常,但并没有真正切换上下文。事实上,调试后的问题是在设置事务之前请求了数据源。当事务最终建立时,为时已晚,数据源已经加载完毕。

如何解决此问题?谢谢。

【问题讨论】:

  • 您好。您找到解决问题的方法了吗?我在同一条船上,我无法即时更改上下文。无论第一次设置的上下文是什么,这就是我所坚持的。
  • @user1830833 我已经发布了这个问题的答案

标签: spring routing transactions datasource


【解决方案1】:

为了解决这个问题,我必须在进入事务之前定义要使用的数据库,所以我定义了一个过滤器(我使用的是 spring)

public class DbFilter extends  {
//les urls that do not only GET method
private final static String NOT_ONLY_GET_URL= "/api/parametre";

@Override
protected void doFilterInternal(HttpServletRequest request,
                                HttpServletResponse response,
                                FilterChain filterChain) throws IOException, ServletException, RestException {
    DbContext.setDbType(DbContext.DbType.MASTER);
    if (!NOT_ONLY_GET_URL.equals(request.getRequestURI()) && request.getMethod().equals("GET")) {
        DbContext.setDbType(DbContext.DbType.REPLICA);
    }
    filterChain.doFilter(request, response);
}

此方法在调用任何@transactional 方法之前运行,如果它是GET 方法,则正在切换数据库。

我已删除 DbContext.setDbType(DbContext.DbType.REPLICA);和 @transactional 方法中的 Dbcontext.reset()。

仅定义“不仅获取 urls 部分”是因为在我的应用程序中有一些 get 方法可以在内部进行更新,因此我检测到这些 url 并将它们影响到主数据库。

【讨论】:

    猜你喜欢
    • 2011-07-27
    • 2012-01-12
    • 2012-02-11
    • 2021-04-07
    • 2011-07-23
    • 2011-07-05
    • 2022-08-19
    • 2019-12-03
    相关资源
    最近更新 更多