1. 概述
jdbc 是什么?
JDBC 英文名为:
Java Data Base Connectivity(Java 数据库连接), 从编程的角度来看, JDBC API 是一组 Java 类和方法, 它们允许将数据库调用嵌入到服务器应用程序中. 更具体地说, JDBC 规范是每个 JDBC 驱动程序供应商都必须实现的一组接口. 驱动程序处理应用程序中的 JDBC 语句, 并将它们包含的 SQL 参数路由到数据库引擎
简单说它就是 JAVA 与数据库的连接的桥梁或者插件, 用 JAVA 代码就能操作数据库的增删改查, 存储过程, 事务等.
JDBC 的结构如下图
JDBC 的作用
加载对应数据库驱动 (Load Driver)
与数据库建立连接(connection)
发送 操作数据库的语句(createStatement)
执行并处理返回结果(executeQuery)
1-1
他们的流程图则是在下面
1-2
如上两个图可以十分清晰的看出来整个 JDBC 的运行方式. 我们就一个一个的讲解
加载对应数据库驱动 (Load Driver)
这个部分是图 1-1 的1部分, 图 1-2 的开始到 Driver 的那条线.
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- try {
- Class.forName("oracle.jdbc.OracleDriver");// 显式地加载 JDBC 驱动程序.
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- } // 创建连接得部分
- }
到这里你可能想问 Class.forName("oracle.jdbc.OracleDriver")是啥.
这个就比较复杂, 涉及到了反射相关的知识. 在这里简述一下.(不看也没啥, 反正 JDBC 里面把这句话背下来就行)
条子的拖堂时间
java 是面向对象语言, 最独特的特点就是一切皆为对象.
但是对象还是有区别的, 主要分为两种.
一种是实例对象.(就是用 new 生成那个, 你平时总用.)
一种则是 Class 对象(没错, 就叫这个名字, 首字母大写. 不是我神经失常又写了一遍).
这个 Class 对象中包含的是每个类的运行时的类型信息. 所有与类有关的信息全在里面.
其实我们的实例对象就是靠 Class 对象来创建的.
每当编译产生一个新类就产生一个 Class 对象.
可能你想问了, 为什么我平时没见过这个 Class 对象?
这就对了, 因为 Class 对象没有公共的构造方法, Class 对象实在类加载时由 java 虚拟机以及通过类加载器中的 defineClass 方法自动构建的. 所以你是没有办法声明 Class 对象的.
一个类加载到虚拟机需要以下三个步骤:
Class.forName("oracle.jdbc.OracleDriver"); 也是执行下面三个步骤.
加载
这是由类加载器 (ClassLoader) 执行的. 通过一个类的全限定名来获取其定义的二进制字节流(Class 字节码), 将这个字节流所代表的静态存储结构转化为方法去的运行时数据接口, 根据字节码在 java 堆中生成一个代表这个类的 java.lang.Class 对象.
链接
在链接阶段将验证 Class 文件中的字节流包含的信息是否符合当前虚拟机的要求, 为静态域分配存储空间并设置类变量的初始值(默认的零值), 并且如果必需的话, 将常量池中的符号引用转化为直接引用.
初始化
到了此阶段, 才真正开始执行类中定义的 java 程序代码. 用于执行该类的静态初始器和静态初始块, 如果该类有父类的话, 则优先对其父类进行初始化.(赋值也是此阶段)
所有的类都是在对其第一次使用时, 动态加载到 JVM 中的(懒加载). 当程序创建第一个对类的静态成员的引用时, 就会加载这个类
而我们的 Class.forName("oracle.jdbc.OracleDriver"); 的作用就是加载这个类, 并返回与带有给定字符串名的类或接口相关联的 Class 对象.
2, 类的初始化过程与类的实例化过程的异同?
类的初始化是指类加载过程中的初始化阶段对类变量按照程序猿的意图进行赋值的过程; 而类的实例化是指在类完全加载到内存中后创建对象的过程.
初始化和实例化的区别
其实你只要看过 oracle.jdbc.OracleDriver 源码就知道, 在其中有大量的静态变量.
- public static final char slash_character = '/';
- public static final char at_sign_character = '@';
- public static final char left_square_bracket_character = '[';
- public static final char right_square_bracket_character = ']';
- public static final String oracle_string = "oracle";
- public static final String protocol_string = "protocol";
- public static final String user_string = "user";
- public static final String password_string = "password";
- public static final String database_string = "database";
- public static final String server_string = "server";
- // 很多很多
源码
调用这个源码后, 这些个代码会被直接被赋值.
同时还有在网上找到一个博文, 其中发现
Class.forName(driver)的根本目的就是为了调用 DriverManager.registerDriver.
jdbc 中的 class.forName 详解
还有另外一篇博文, 从 MySQL 方面论证了这个情况, 如下是 MySQL 中的代码, 可以看到也有一个同样的 static 代码块.
这个是图 1-1JDBCDriver 到 DriverManager 的那条线, 图 1-2 Driver 到 DriverManager 那条线.
- package com.MySQL.jdbc
- public class Driver extends NonRegisteringDriver implements java.sql.Driver {
- // ~ Static fields/initializers
- // --------------------------------------------- //
- // Register ourselves with the DriverManager
- //
- static {
- t ry {
- java.sql.DriverManager.registerDriver(new Driver());// 将 Driver 注册给 DriverManager
- } catch (SQLException E) {
- throw new RuntimeException("Can't register driver!");
- } // 这个是图 1-1JDBCDriver 到 DriverManager 的那条线, 图 1-2Driver 到 DriverManager
- /**
- }
- // ~ Constructors
- // -----------------------------------------------------------
- /**
- * Construct a new driver and register it with DriverManager
- *
- * @throws SQLException
- * if a database error occurs.
- */
- public Driver() throws SQLException {
- // Required for Class.forName().newInstance()
- }
- }
- // 从而通过 Class.forName(DriverString)会向 DriverManager 注册该 Driver 类. 所以可以直接调用. 而 Class.forName("").newInstance()则等于是将该 Driver 驱动类实例化, 返回该类的一个实例, 所以, 如果只是取 JDBC 的 Driver 驱动, 可以不必用 newInstance().
- ----------------
- we just want to load the driver to jvm only, but not need to user the instance of driver, so call Class.forName(xxx.xx.xx) is enough, if you call Class.forName(xxx.xx.xx).newInstance(), the result will same as calling Class.forName(xxx.xx.xx), because Class.forName(xxx.xx.xx).newInstance() will load driver first, and then create instance, but the instacne you will never use in usual, so you need not to create it.
- ----------------
- //DriverManager
- static {
- loadInitialDrivers();
- println("JDBC DriverManager initialized");
- }
- private static void loadInitialDrivers() {
- ...
- AccessController.doPrivileged(new PrivilegedAction<Void>() {
- public Void run() {
- // 搜索服务的实现类(驱动实例)
- ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
- Iterator<Driver> driversIterator = loadedDrivers.iterator();// 这个明显实现了 Iterator 接口, 可以迭代. 也就是搜索所有驱动.
- try{
- while(driversIterator.hasNext()) {
- driversIterator.next();// 此处初始化 并注册了
- }
- } catch(Throwable t) {
- // Do nothing
- }
- return null;
- //ServiceLoader
- public S next() {
- if (acc == null) {
- return nextService();
- } else {
- ...
- //ServiceLoader
- private S nextService() {
- ...
- try {
- c = Class.forName(cn, false, loader);// 这个就是注册驱动的代码. 看看和我们前一个部分长得像不像
- ...
- Connection connection=DriverManager.getConnection(
- "jdbc:oracle:thin:@192.168.80.10:1521:orcl", // 你的 Oracle 数据库 URL
- // 192.168.80.10 这个部分改成你的数据库地址或者 localhost : 1521// 这个是端口号 : orcl // 这个是实例名
- "system",// 你的 oracle 数据库帐号
- "aA107824"// 你的 oracle 数据库密码
- );
- public static Connection getConnection(String url)
- throws SQLException {
- java.util.Properties info = new java.util.Properties();// 这个是读取配置文件的类的实例
- return (getConnection(url, info, Reflection.getCallerClass()));
- //eflection.getCallerClass()可以得到调用者的类. 这个方法是很好用的.
- }
- // 用于根据 url, 对应的属性信息在 registeredDrivers 中找到合适的注册的驱动创建数据库链接
- 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();
- }
- }
- //url 没输入, 报错
- 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.
- // 一个应用中有可能会有多个数据库驱动, 需要判断数据库连接的调用者 (调用 getConnection() 方法的对象)是否与驱动相匹配(别让 Oracle 调用了 MySQL 的驱动)
- 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");
- }
- }
- private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {
- boolean result = false;
- if(driver != null) {
- Class<?> aClass = null;
- try {
- aClass = Class.forName(driver.getClass().getName(), true, classLoader);// 方法获得类, 该方法返回与给定字符串名的类或接口的 Class 对象, 使用给定的类加载器进行加载, 然后通过
- } catch (Exception ex) {
- result = false;
- }
- result = ( aClass == driver.getClass() ) ? true : false;// 如果不是同一个加载器这个值不会相等.
- }
- return result;
- }
- @CallerSensitive
- public static Connection getConnection(String url)
- throws SQLException {
- java.util.Properties info = new java.util.Properties();// 这个是读取配置文件的类的实例
- return (getConnection(url, info, Reflection.getCallerClass()));
- //eflection.getCallerClass()可以得到调用者的类. 这个方法是很好用的.
- }
- // 用于根据 url, 对应的属性信息在 registeredDrivers 中找到合适的注册的驱动创建数据库链接
- 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.
- // 一个应用中有可能会有多个数据库驱动, 需要判断数据库连接的调用者 (调用 getConnection() 方法的对象)是否与驱动相匹配
- 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");
- }
- }
- // 一个应用中有可能会有多个数据库驱动, 需要判断数据库连接的调用者 (调用 getConnection() 方法的对象)是否与驱动相匹配
- private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {
- boolean result = false;
- if(driver != null) {
- Class<?> aClass = null;
- try {
- aClass = Class.forName(driver.getClass().getName(), true, classLoader);
- } catch (Exception ex) {
- result = false;
- }
- result = ( aClass == driver.getClass() ) ? true : false;
- }
- return result;
- }
- /** 管理一个集合 JDBC 驱动的基础服务
- 注意: 在新的 JDBC2.0api 中实现了新的 DataSource 接口, 提供了另一种链接数据源的方式.
- 使用 DataSource 的对象是首选方案
- */
- public class DriverManager {
- // 注册了 JDBC 驱动的集合, 记不记得上面讲的注册过程. 这个就是那个形成的东西
- private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
- /**
- 通过检查 jdcb.properties 加载初始化 JDBC 驱动
- */
- static {
- loadInitialDrivers();
- println("JDBC DriverManager initialized");
- }
- /*
- * 对外使用提供数据库连接的方法,
- * 重点说明 Reflection.getCallerClass()方法, 该方法返回调用者的类
- */
- @CallerSensitive
- public static Connection getConnection(String url)
- throws SQLException {
- java.util.Properties info = new java.util.Properties();
- return (getConnection(url, info, Reflection.getCallerClass()));
- }
- // 用于根据 url, 对应的属性信息在 registeredDrivers 中找到合适的注册的驱动创建数据库链接
- 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.
- // 一个应用中有可能会有多个数据库驱动, 需要判断数据库连接的调用者 (调用 getConnection() 方法的对象)是否与驱动相匹配
- 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");
- }
- }
- private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {
- boolean result = false;
- if(driver != null) {
- Class<?> aClass = null;
- try {
- aClass = Class.forName(driver.getClass().getName(), true, classLoader);
- } catch (Exception ex) {
- result = false;
- }
- result = ( aClass == driver.getClass() ) ? true : false;
- }
- return result;
- }
- // 根据 URL 从 registeredDrivers 中获得驱动, 根据 Reflection.getCallerClass()返回的调用者类, 在 registeredDrivers 中进行匹配
- //API 文档: 试图查找能理解给定 URL 的驱动程序. DriverManager 试图从已注册的驱动程序集中选择一个适当的驱动程序.
- @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");
- }
- /*
- * 注册数据驱动, 使用同步方式, 最终保存在 CopyOnWriteArrayList 的集合中
- */
- public static synchronized void registerDriver(java.sql.Driver driver,
- DriverAction da)
- throws SQLException {
- /* Register the driver if it has not already been added to our list */
- if(driver != null) {
- registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
- } else {
- // This is for compatibility with the original DriverManager
- throw new NullPointerException();
- }
- println("registerDriver:" + driver);
- }
- Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@192.168.80.10:1521:orcl", "system","aA107824");
- connection.SetAtuoCommit(true);// 设置为自动提交状态
- System.out.println(connection.getAutoCommit());// 检索此 Connection 对象的当前自动提交模式.
- //true
- Statement statement=connection.createStatement();// 创建一个 statement 对象, 之后会讲
- int count = statement.executeUpdate("insert into student values(5,'郭美美','女',58,'北京市海淀区')");
- connection.commit();// 手动提交代码
- connection.rollback();// 手动回滚更改
- connection.close():// 立刻关闭这个资源
- connection.isclose();//true
- try {
- Connection conn=DriverManager.getConnection("jdbc:oracle:thin:@192.168.80.10:1521:orcl","system","aA107824");
- Statement statement=conn.createStatement();
- }catch (SQLException e) {
- // TODO: handle exception
- e.printStackTrace();
- }
- Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
- ResultSet.CONCUR_UPDATABLE);
- ResultSet.CONCUR_READ_ONLY
- ResultSet.CONCUR_UPDATABLE
- ```java
- //executeUpdate 方法, 向数据库传递增删改的 SQL 语句
- int count = statement.executeUpdate("insert into student values(20,'郭美美','女',58,'北京市海淀区')");
- // 返回从数据库搜索的数据, 用 ResultSet 格式存储.
- ResultSet resultSet=statement.executeQuery("select*from student");
- // 获取结果集合的行数, 该数是根据此 Statement 对象生成的 ResultSet 对象的默认获取大小.
- statement.getFetchSize();//1, 因为只插入了一条数据
- // 获取从数据库表获取行的方向, 该方向是根据此 Statement 对象生成的结果集合的默认值.
- statement.getFetchDirection()//1000, 这个应该是一个常量, 就是正向的意思
- // 关闭 staement 对象, 释放占有的 jdbc 资源, 而不是等连接关闭的时候
- statement.close();
- PreparedStatement statement2 = connection.prepareStatement("update student set sname=? where sid=?");// 有一个预编译的过程.
- statement2.setString(1, "许佳琪");// 设置 String 的数据类型
- statement2.setInt(2, 4);// 设置 int 的数值类型, 第一个参数是? 所在的位置, 比如 sname=? 是第一个?,sid=? 是第二个问好.
- // statement2.executeUpdate("insert into student values(3,'K 律','女',57,'北京市')");// 插入升级语句
- statement2.execute();// 似乎是必须有这一步, 要不然发送不上去
- System.out.println("是否是自动提交模式" + connection.getAutoCommit());// getAutoCommit()用来判断是否是用来提交的
- connection.commit();
来源: https://www.cnblogs.com/yanzezhong/p/12678639.html