配置动态数据源

配置动态数据源

jdbc是直接获取数据库连接,然后操作数据库的过程。

数据源是借鉴了”池“的思想,从数据库连接池里获取数据库连接。

jdbc使用流程,如下

//网上jdbc使用模板比较多,比如 https://blog.csdn.net/Yuz_99/article/details/89763803


//加载数据库驱动程序(对应的Driver 实现类中有注册驱动的静态代码块)
Class.forName(driver);
//通过DriverManager 的getConnection() 方法获取数据库连接.
Connection conn = DriverManager.getConnection(jdbcUrl, user, password);

...
rs.close();
statement.close();
...
    
//关闭数据库连接
conn.close();

而datasource,从下面可以看到,连接池也有getConnection方法。

/*
 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package javax.sql;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Wrapper;

/**
 * <p>A factory for connections to the physical data source that this
 * {@code DataSource} object represents.  An alternative to the
 * {@code DriverManager} facility, a {@code DataSource} object
 * is the preferred means of getting a connection. An object that implements
 * the {@code DataSource} interface will typically be
 * registered with a naming service based on the
 * Java&trade; Naming and Directory (JNDI) API.
 * <P>
 * The {@code DataSource} interface is implemented by a driver vendor.
 * There are three types of implementations:
 * <OL>
 *   <LI>Basic implementation -- produces a standard {@code Connection}
 *       object
 *   <LI>Connection pooling implementation -- produces a {@code Connection}
 *       object that will automatically participate in connection pooling.  This
 *       implementation works with a middle-tier connection pooling manager.
 *   <LI>Distributed transaction implementation -- produces a
 *       {@code Connection} object that may be used for distributed
 *       transactions and almost always participates in connection pooling.
 *       This implementation works with a middle-tier
 *       transaction manager and almost always with a connection
 *       pooling manager.
 * </OL>
 * <P>
 * A {@code DataSource} object has properties that can be modified
 * when necessary.  For example, if the data source is moved to a different
 * server, the property for the server can be changed.  The benefit is that
 * because the data source's properties can be changed, any code accessing
 * that data source does not need to be changed.
 * <P>
 * A driver that is accessed via a {@code DataSource} object does not
 * register itself with the {@code DriverManager}.  Rather, a
 * {@code DataSource} object is retrieved though a lookup operation
 * and then used to create a {@code Connection} object.  With a basic
 * implementation, the connection obtained through a {@code DataSource}
 * object is identical to a connection obtained through the
 * {@code DriverManager} facility.
 * <p>
 * An implementation of {@code DataSource} must include a public no-arg
 * constructor.
 *
 * @since 1.4
 */

public interface DataSource  extends CommonDataSource, Wrapper {

  /**
   * <p>Attempts to establish a connection with the data source that
   * this {@code DataSource} object represents.
   *
   * @return  a connection to the data source
   * @exception SQLException if a database access error occurs
   * @throws java.sql.SQLTimeoutException  when the driver has determined that the
   * timeout value specified by the {@code setLoginTimeout} method
   * has been exceeded and has at least tried to cancel the
   * current database connection attempt
   */
  Connection getConnection() throws SQLException;

  /**
   * <p>Attempts to establish a connection with the data source that
   * this {@code DataSource} object represents.
   *
   * @param username the database user on whose behalf the connection is
   *  being made
   * @param password the user's password
   * @return  a connection to the data source
   * @exception SQLException if a database access error occurs
   * @throws java.sql.SQLTimeoutException  when the driver has determined that the
   * timeout value specified by the {@code setLoginTimeout} method
   * has been exceeded and has at least tried to cancel the
   * current database connection attempt
   * @since 1.4
   */
  Connection getConnection(String username, String password)
    throws SQLException;
}

现在生产环境一般都会引入数据源。一来解耦,二来,很多中间件都是和datasource交互的,不直接通过jdbc获取连接。

场景

参考:Spring项目中使用两种方法动态切换数据源,多数据源切换_u013034378的博客-CSDN博客_动态数据源

一般有两种动态切换数据库的场景。 场景一:数据源信息配置在xml中,适用于一般数据库切换。执行完某操作,切换数据库,执行另一个操作。 场景二:数据源信息配置在默认数据源中,适用于切换数据库操作同一方法,相当于批量执行方法。

场景二的实现

我遇到的情况是,模拟客户端,每个客户端都有一个sqlite库,需要对每个库作相同的操作。

恰好就是场景二,切换数据库操作同一方法。

package com.ycb.dao.datasource;

import org.springframework.jdbc.datasource.AbstractDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * 动态数据源(一次性,用完即丢,没注册bean)
 */
public class DynamicDataSource extends AbstractDataSource {
    public DynamicDataSource() {
    }

    protected DataSource determineTargetDataSource() {
        return DataSourceHolder.getDataSource();
    }

    @Override
    public Connection getConnection() throws SQLException {
        return determineTargetDataSource().getConnection();
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return determineTargetDataSource().getConnection(username, password);
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T unwrap(Class<T> iface) throws SQLException {
        if (iface.isInstance(this)) {
            return (T) this;
        }
        return determineTargetDataSource().unwrap(iface);
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return (iface.isInstance(this) || determineTargetDataSource().isWrapperFor(iface));
    }
}

//public class DynamicDataSourceV1 extends AbstractRoutingDataSource {
//    public DynamicDataSource() {}
//
//    @Override
//    protected DataSource determineTargetDataSource() {
//        return DataSourceHolder.getDataSource();
//    }
//
//    @Deprecated
//    @Override
//    protected Object determineCurrentLookupKey() {
//        return null;
//    }
//
//    @Deprecated
//    @Override
//    public void afterPropertiesSet() {
//    }
//}

这是每次切换数据源,都是新生成一个数据源,没有缓存下来。如果考虑数据源复用,可以用map缓存下来。

package com.ycb.dao.datasource;

import com.ycb.dao.db.SqliteUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.sqlite.JDBC;
import org.sqlite.SQLiteDataSource;

import javax.sql.DataSource;

/**
 * 封装的对数据源进行操作的类:
 */
public class DataSourceHolder {

    private static Logger logger = LoggerFactory.getLogger(DataSourceHolder.class);
    //线程  本地环境
    private static final ThreadLocal<DataSource> dataSources = new InheritableThreadLocal<>();

    //设置数据源
    public static void setDataSource(String jdbcUrl) {
        DataSource dataSource = SqliteUtil.createDateSource(jdbcUrl);
        dataSources.set(dataSource);
    }

    //设置数据源
    public static void setDataSource(DataSource datasource) {
        dataSources.set(datasource);
    }

    //获取数据源
    public static DataSource getDataSource() {
        return dataSources.get();
    }

    //清除数据源
    public static void clearDataSource() {
        dataSources.remove();
    }
}

使用

DataSourceHolder.setDataSource(jdbcUrl);
//...
//...
DataSourceHolder.clearDataSource();

参考:Spring Boot + Mybatis 实现动态数据源 – 朝雨忆轻尘 – 博客园 (cnblogs.com)


评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注