八月初突然接到一个需求,要实现一个工具类。可以动态切换数据源,根据不同的数据源访问不同的数据库。
脑海中第一想法使用JDBC去实现,那么说干就干。
1.加载数据驱动
2.建立数据连接对象
3.创建Statement对象
4.执行sql,得到ResultSet对象
5.获取数据
6.关闭连接
’Talk is cheap,show me the code’
String driver = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/data_test";
String username = "root";
String password = "123456";
String sql = "SELECT * FROM S_STUDENT;";
try {
// 1.加载数据库驱动
Class.forName(driver);
// 2.建立数据库连接对象
Connection connection = DriverManager.getConnection(url, username, password);
// 3.创建Statement对象
Statement statement = connection.createStatement();
// 4.执行sql,得到ResultSet对象
ResultSet resultSet = statement.executeQuery(sql);
// 5.获取数据,操作数据
while (resultSet.next()) {
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
System.out.println("name = " + name + "; age = " + age);
}
// 6.释放数据
resultSet.close();
statement.close();
connection.close();
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
以上就是一个简单的JDBC连接小例子。好了,下面我们开始改造。
核心要求:动态切换数据源
拿上面的例子来说,要动态切换数据库驱动,同时动态的切换数据库连接信息。
思路分析:
1.动态传入数据库驱动和数据库连接信息。
2.定义构造方法为对应的属性进行初始化,从而来实现动态属性的赋值。
3.其他操作无大变动。
代码改造~~~
改造的代码就不贴出来了,会在最后贴一个最终版本。
改造完代码之后进行测试发现,并没有动态的切换数据库驱动。
排查代码以及设计逻辑,无果。
打开浏览器,开始面向Google编程。
直到搜到这篇博客,深入浅出的讲解了一下JDBC的驱动加载。链接奉上,感谢博主分享。
在搜索的时候下面这篇博客写的也不错,这里我也分享出来。大家可以看一看。
链接奉上,感谢博主分享
看过上面的博客之后,了解到DriverManager负责注册和注销数据库驱动。使用DriverManager.getConnection()获取连接对象时,会遍历其维护的Driver信息。然后调用acceptsURL(url)方法判断当前驱动是否打开该URL连接,如果该方法返回true,则返回对应的Driver。 好的了解到这,明白了原来可以使用static代码块将数据库驱动全部交由DriverManager维护。连接时只需要传入对应数据源的url、username、password就可以了。改造代码如下:
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* JDBC 工具类
*
* @author 山楂罐头
* @date 2019/8/6
*/
public class JdbcUtil implements Serializable {
private static final long serialVersionUID = 1382377502744233953L;
private static final Logger logger = LoggerFactory.getLogger(JdbcUtil.class);
// 数据库连接对象
private Connection connection = null;
// 执行静态SQL语句
private Statement statement = null;
// 执行动态SQL语句
private PreparedStatement preparedStatement = null;
// 结果集
private ResultSet resultSet = null;
// 数据库连接URL
private String url;
// 用户名
private String username;
// 密码
private String password;
static {
// 加载数据库驱动。注:我将数据源驱动放入到了一个枚举类中,此处是从枚举类中获取。
for (DB_DRIVER_ENUM dbDriverEnum : DB_DRIVER_ENUM.values()) {
try {
// 加载数据驱动,需确保引入相关的jar包
Class.forName(dbDriverEnum.getDataSourceDriver());
} catch (ClassNotFoundException e) {
logger.error("\r\n 初始化 JdbcUtil 数据驱动失败!失败的数据驱动类型:" + dbDriverEnum.getDataSourceDriver());
e.printStackTrace();
}
}
}
public JdbcUtil(String url, String username, String password) {
this.url = url;
this.username = username;
this.password = password;
}
/**
* 获取数据库连接
*
* @return 数据库连接
*/
private Connection getConnection() {
try {
// 建立数据库连接对象
connection = DriverManager.getConnection(url, username, password);
} catch (SQLException e) {
logger.error("\r\n JdbcUtil 连接数据库失败!失败原因:" + ExceptionUtils.getStackTrace(e));
e.printStackTrace();
}
return connection;
}
/**
* 执行查询操作
*
* @param sql SQL语句
* @return 返回值是一个结果集
*/
private ResultSet executeQuery(String sql) {
try {
connection = this.getConnection();
if (null != connection) {
statement = connection.createStatement();
if (null != statement) {
resultSet = statement.executeQuery(sql);
}
}
} catch (SQLException e) {
logger.error("\r\n JdbcUtil 执行查询SQL失败!失败原因:" + ExceptionUtils.getStackTrace(e));
e.printStackTrace();
}
return resultSet;
}
/**
* 关闭数据库连接
*/
private void close() {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
logger.error("\r\n JdbcUtil 关闭结果集失败!失败原因:" + ExceptionUtils.getStackTrace(e));
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
logger.error("\r\n JdbcUtil 关闭执行静态SQL实例对象失败!失败原因:" + ExceptionUtils.getStackTrace(e));
e.printStackTrace();
}
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
logger.error("\r\n JdbcUtil 关闭执行动态SQL实例对象失败!失败原因:" + ExceptionUtils.getStackTrace(e));
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
logger.error("\r\n JdbcUtil 关闭数据库连接失败!失败原因:" + ExceptionUtils.getStackTrace(e));
e.printStackTrace();
}
}
}
/**
* 处理resultSet结果集对象,并将结果集对象封装成 List
至此,工具类就初步成型了。调用方式为:
// 检查数据源是否可以连通
JdbcUtil jdbcUtil = new JdbcUtil(url, userName, password);
jdbcUtil.testDBConnect();
在面向Google编程的时候,我发现了好多coder都写了JDBC的连接工具。这里给大家推荐一个个人感觉比较好的工具类。有需要的小伙伴猛戳这里。
以下是DriverManager的相关源码,以对上面内容的补充和加深理解。
// List of registered JDBC drivers
private final static CopyOnWriteArrayList registeredDrivers = new CopyOnWriteArrayList<>();
// ...省略部分源码...
@CallerSensitive
public static Connection getConnection(String url,
String user, String password) throws SQLException {
java.util.Properties info = new java.util.Properties();
if (user != null) {
info.put("user", user);
}
if (password != null) {
info.put("password", password);
}
return (getConnection(url, info,Reflection.getCallerClass()));
}
// ...省略部分源码...
@CallerSensitive
public static Driver getDriver(String url)
throws SQLException {
println("DriverManager.getDriver(\"" + url + "\")");
Class> callerClass = Reflection.getCallerClass();
// Walk through the loaded registeredDrivers attempting to locate someone
// who understands the given URL.
for (DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if(isDriverAllowed(aDriver.driver, callerClass)) {
try {
if(aDriver.driver.acceptsURL(url)) {
// Success!
println("getDriver returning " + aDriver.driver.getClass().getName());
return (aDriver.driver);
}
} catch(SQLException sqe) {
// Drop through and try the next driver.
}
} else {
println(" skipping: " + aDriver.driver.getClass().getName());
}
}
println("getDriver: no suitable driver");
throw new SQLException("No suitable driver", "08001");
}
// ...省略部分源码...
private static Connection getConnection(
String url, java.util.Properties info, Class> caller) throws SQLException {
/*
* When callerCl is null, we should check the application's
* (which is invoking this class indirectly)
* classloader, so that the JDBC driver class outside rt.jar
* can be loaded from here.
*/
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
synchronized(DriverManager.class) {
// synchronize loading of the correct classloader.
if (callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
}
if(url == null) {
throw new SQLException("The url cannot be null", "08001");
}
println("DriverManager.getConnection(\"" + url + "\")");
// Walk through the loaded registeredDrivers attempting to make a connection.
// Remember the first exception that gets raised so we can reraise it.
SQLException reason = null;
for(DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
} else {
println(" skipping: " + aDriver.getClass().getName());
}
}
// if we got here nobody could connect.
if (reason != null) {
println("getConnection failed: " + reason);
throw reason;
}
println("getConnection: no suitable driver found for "+ url);
throw new SQLException("No suitable driver found for "+ url, "08001");
}