前言
JDBC即Java DataBase Connectivity,java数据库连接;JDBC 提供的API可以让JAVA通过API方式访问关系型数据库,执行SQL语句,获取数据;常见关系型数据库如Oracle、MySQL、SQLServer等;对于非关系型数据库如Redis、mongonDB等就显得无力;关系型数据库最典型的数据结构是表,易于维护,灵活使用(使用表结构,支持SQL语言的简单及复杂表及多表之间的查询),非关系型数据库严格上不是一种数据库,应该是一种数据结构化存储方法的集合(文档型、键值对型、列式数据库、图形数据库),相对于关系型数据库(读写性能比较差,效率低,灵活度低、高并发处理显得鸡肋);非关系型数据库优点多多,速度快、高拓展、低成本(就数据结构复杂,其他都比较简单,学习难度就有点大)。
言归正传,从根本来讲,JDBC是一种规范,它提供的接口,是一套完整的、可移植的访问底层数据库的程序。
JDBC驱动结构模型
分两种架构模型:三层和两层;下面是三层软件架构的模型,分别是表示层UI、业务逻辑层BLL、数据访问层DAL;两层架构的就把表现层UI拿掉。
java中使用JDBC连接数据库
在应用程序中进行数据库连接,调用JDBC接口,首先要将特定厂商的JDBC驱动实现加载到系统内存中,然后在应用程序中就可以直接根据URL和用户密码来识别获取对应的驱动实例,与数据库建立连接,对数据库进行相关操作。
//1.加载mysql数据库驱动
static String driverName = "com.mysql.jdbc.Driver";
//2.加载oracle数据库驱动
//static String driverName = "oracle.jdbc.driver.OracleDriver";
//3.加载SQL Server数据库驱动
//static String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
static String url = "jdbc:mysql://127.0.0.1:3306/mysql";
static String username = "root";
static String password = "123456";
public void getConnection() {
try {
// 1.1、加载驱动,使用Class.forName()将驱动类加载到内存
Class.forName(driverName);
//1.2或者使用
//Driver driver = (Driver) Class.forName(driverName).newInstance();
// 2.1、获取connection
Connection conn = DriverManager.getConnection(url,username,password);
//2.2或者使用
/* Properties info = new Properties();
info.put("user", username);
info.put("password", password);
Connection conn = driver.connect(url, info);
*/
System.out.println(conn);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
JDBC驱动原理详解
对于已经安装好的数据库之后,我们想要直接使用数据库,比如保存用户数据资料等,要通过该数据库厂商提供的驱动程序才能与数据库交互,其实就是数据库厂商的JDBC接口实现;我们只需使用他们提供的对JDBC接口实现类的jar文件。如Oracle数据库对应使用ojdbc8.jar包等等。不同关系型数据库版本使用的JDBC的jar包都不同。
1.上面加载的JDBC驱动的方法有两种,一种使用DriverManager管理(推荐),一种直接Driver(耦合太强,不推荐)
1.1 Class.forName(driverName)和Driver driver = (Driver) Class.forName(driverName).newInstance();
因为驱动本质就是一个class,Class.forName()的作用就是将class加载到内存中,而这个driveName其实就是实现了java.sql.Driver(在java.lang.Object包下找到)接口的类(此类可以在数据库厂商提供的JDBC jar包中找到);当使用Class.forName(driverName)加载驱动类到内存中的时候,同时会执行这个驱动类中的静态代码块,创建一个Driver实例,然后调用DriverManager.registerDriver(Driver实例)进行驱动注册,供后面使用。
下面使用oracle数据库为例来源码分析:
1.1.1在oracle.jdbc.driver包下的OracleDriver驱动类,实现了java.sql.Driver类
1.1.2当使用Class.forName()加载驱动类oracle.jdbc.driver.OracleDriver.class的时候,会执行oracle.jdbc.driver.OracleDriver.class中的静态代码块和java.sql.DriverManager.class中的静态代码块:
//oracle.jdbc.driver.OracleDriver.class中的静态代码块
package oracle.jdbc.driver;
import java.security.AccessController;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import oracle.jdbc.OracleDatabaseMetaData;
import oracle.jdbc.driver.OracleDriver.1;
import oracle.security.pki.OracleSecretStore;
import oracle.security.pki.OracleWallet;
public class OracleDriver implements Driver {
private static OracleDriver defaultDriver = null;
private static final String _Copyright_2004_Oracle_All_Rights_Reserved_;
//省略。。。
static {
Timestamp localTimestamp = Timestamp.valueOf("2000-01-01 00:00:00.0");
try {
if (defaultDriver == null) {
//1.不存在OracleDriver驱动实例就创建该实例--自己加的注释
defaultDriver = new OracleDriver();
//2.将Oracle驱动实例进行注册--自己加的注释
DriverManager.registerDriver(defaultDriver);
}
} catch (RuntimeException localRuntimeException) {
;
} catch (SQLException localSQLException) {
;
}
_Copyright_2004_Oracle_All_Rights_Reserved_ = null;
}
}
// java.sql.DriverManager.class中的静态代码块
package java.sql;
import java.util.Iterator;
import java.util.ServiceLoader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.concurrent.CopyOnWriteArrayList;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;
public class DriverManager {
// List of registered JDBC drivers
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
private static volatile int loginTimeout = 0;
private static volatile java.io.PrintWriter logWriter = null;
private static volatile java.io.PrintStream logStream = null;
// Used in println() to synchronize logWriter
private final static Object logSync = new Object();
/* Prevent the DriverManager class from being instantiated. */
private DriverManager(){}
/**
* Load the initial JDBC drivers by checking the System property
* jdbc.properties and then use the {@code ServiceLoader} mechanism
*/
static {
//加载配置在jdbc.drivers系统变量中的驱动driver--自己的注释
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
private static void loadInitialDrivers() {
String drivers;
try {
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty("jdbc.drivers");
}
});
} catch (Exception ex) {
drivers = null;
}
// If the driver is packaged as a Service Provider, load it.
// Get all the drivers through the classloader
// exposed as a java.sql.Driver.class service.
// ServiceLoader.load() replaces the sun.misc.Providers()
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
/* Load these drivers, so that they can be instantiated.
* It may be the case that the driver class may not be there
* i.e. there may be a packaged driver with the service class
* as implementation of java.sql.Driver but the actual class
* may be missing. In that case a java.util.ServiceConfigurationError
* will be thrown at runtime by the VM trying to locate
* and load the service.
*
* Adding a try catch block to catch those runtime errors
* if driver not available in classpath but it's
* packaged as service and that service is there in classpath.
*/
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
});
println("DriverManager.initialize: jdbc.drivers = " + drivers);
if (drivers == null || drivers.equals("")) {
return;
}
String[] driversList = drivers.split(":");
println("number of Drivers:" + driversList.length);
for (String aDriver : driversList) {
try {
println("DriverManager.Initialize: loading " + aDriver);
Class.forName(aDriver, true,
ClassLoader.getSystemClassLoader());
} catch (Exception ex) {
println("DriverManager.Initialize: load failed: " + ex);
}
}
}
}
当使用Class.forName(driverName)加载驱动类到内存中的时候,同时会执行这个驱动类中的静态代码块,创建一个Driver实例;然后调用DriverManager 进行注册Driver;DriverManager 作为 Driver 的管理器,它在第一次被调用的时候,它会被加载到内存中,然后执行其定义的static静态代码段,在静态代码段中,有一个 loadInitialDrivers() 静态方法,用于加载配置在jdbc.drivers 系统属性内的驱动Driver,配置在jdbc.drivers 中的驱动driver将会首先被加载上面的静态代码块使用的是DriverManager来统一管理操作Driver和获取Connection对象;DriverManager驱动管理类方便管理存在多个Driver驱动实例的情况;可以进行注册和删除加载的驱动程序,可以根据给定的Url获取符合url协议的驱动Driver并建立Connection连接,进行数据交互。
java.sql.DriverManager类的方法摘要:
DriverManager.registerDriver(Driver driver)的参数是一个java.sql.Driver接口的实现类实例对象,
java.sql.Driver接口的方法摘要有:
1.2使用DriverManager获取Connection时,使用DriverManager提供的getConnection()方法,该方法会自动根据url匹配对应的驱动Driver实例,然后调用Driver实例实现java.sql.Driver接口方法connect()获取对应的URL的数据库连接,返回Connection对象实例;如果不使用DriverManager可以直接Driver实例调用java.sql.Driver接口的connect(String url,Properties info)方法获取连接。如上面代码两者是等价的
//加载JDBC驱动
Class.forName(driverName);
//获取数据库连接
Connection conn = DriverManager.getConnection(url,username,password);
跟下面是一样的效果:
//获取JDBC驱动
Driver driver = (Driver) Class.forName(driverName).newInstance();
//或者Driver driver = DriverManager.getDriver(url);
Properties info = new Properties();
info.put("user", username);
info.put("password", password);
//获取数据库链接
Connection conn = driver.connect(url, info);
1.3获取数据库时的认证
利用Driver的接口方法acceptsURL(String url) ,该方法只会校验url是否符合协议和是否能打开该url连接,每个数据库厂商都会制定自己的url协议,只有符合自己的协议形式的url才认为自己能够打开这个url;但不会判断url是否有效。
例如oracle定义的自己的url协议如下:
jdbc:oracle:thin:@//:/ServiceName
jdbc:oracle:thin:@::
oracle的acceptsURL(String url)方法源码如下:
package oracle.jdbc.driver;
import java.security.AccessController;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import oracle.jdbc.OracleDatabaseMetaData;
import oracle.jdbc.driver.OracleDriver.1;
import oracle.security.pki.OracleSecretStore;
import oracle.security.pki.OracleWallet;
public class OracleDriver implements Driver {
//省略。。。
public boolean acceptsURL(String var1) {
if (var1.startsWith("jdbc:oracle:")) {
return this.oracleDriverExtensionTypeFromURL(var1) > -2;
} else {
return false;
}
}
private int oracleDriverExtensionTypeFromURL(String var1) {
int var2 = var1.indexOf(58) + 1;
if (var2 == 0) {
return -2;
} else {
int var3 = var1.indexOf(58, var2);
if (var3 == -1) {
return -2;
} else if (!var1.regionMatches(true, var2, "oracle", 0, var3 - var2)) {
return -2;
} else {
++var3;
int var4 = var1.indexOf(58, var3);
if (var4 == -1) {
return -3;
} else {
String var5 = var1.substring(var3, var4);
if (var5.equals("thin")) {
return 0;
} else {
return !var5.equals("oci8") && !var5.equals("oci") ? -3 : 2;
}
}
}
}
}
}