基于PostgreSQL链接JDBC源码分析


前面2篇博客分析了关于JDBC使用和源码的一些api的介绍。但是driver、connection、statement及resultSet都只是接口,定义了方法,但是并没有具体的实现,具体实现还是得各个数据库自己提供链接的jar,实现JDBC定义的接口。
这篇文章就让我们一起来看看基于PostgreSQL的JDBC连接代码的实现源码。

上篇文章讲了,在driverManager中,会自动初始化driver。

1、driver加载

实际上,在PostgreSQL的driver类中,也有自动初始化的方法。在 Class.forName(driver) 时,先会执行static方法块,也就是register()方法,而register()方法内部实际是通过DriverManager.registerDriver(registeredDriver)这个方法完成驱动注册,这样就完成了driver的加载。

  static {
    //静态方法,在初始化的时候就会调用register自动加载
      register();
    } catch (SQLException e) {
      throw new ExceptionInInitializerError(e);
    }
  }
  //这个方法内部可以看到是DriverManager.registerDriver(registeredDriver)
  public static void register() throws SQLException {
    if (isRegistered()) {
      throw new IllegalStateException(
          "Driver is already registered. It can only be registered once.");
    }
    Driver registeredDriver = new Driver();
    DriverManager.registerDriver(registeredDriver);
    Driver.registeredDriver = registeredDriver;
  }

2、获取connection

完成驱动加载之后,我们可以用DriverManager.getConnection()完成connection的获取,这个上篇文章讲过具体实现,也可以通过Driver.connect()获取。

public java.sql.Connection connect(String url, Properties info) throws SQLException {
    Properties defaults;
    //这里可以发现url必须是jdbc:postgresql:开头。
    if (!url.startsWith("jdbc:postgresql:")) {
      return null;
    }
    //把info中的key和value取出,放入自己定义的props
    try {
      defaults = getDefaultProperties();
    } catch (IOException ioe) {
      throw new PSQLException(GT.tr("Error loading default settings from driverconfig.properties"),
          PSQLState.UNEXPECTED_ERROR, ioe);
    }
    Properties props = new Properties(defaults);
    if (info != null) {
      Enumeration<?> e = info.propertyNames();
      while (e.hasMoreElements()) {
        String propName = (String) e.nextElement();
        String propValue = info.getProperty(propName);
        if (propValue == null) {
          throw new PSQLException(
              GT.tr("Properties for the driver contains a non-string value for the key ")
                  + propName,
              PSQLState.UNEXPECTED_ERROR);
        }
        props.setProperty(propName, propValue);
      }
    }
    if ((props = parseURL(url, props)) == null) {
      logger.debug("Error in url: " + url);
      return null;
    }
    try {
      if (logger.logDebug()) {
        logger.debug("Connecting with URL: " + url);
      }

      long timeout = timeout(props);
      if (timeout <= 0) {
        return makeConnection(url, props);
      }
      //ConnectThread的run()完成内部的connection初始化,getResult()方法获取已经初始化的connection。
      ConnectThread ct = new ConnectThread(url, props);
      Thread thread = new Thread(ct, "PostgreSQL JDBC driver connection thread");
      thread.setDaemon(true); // Don't prevent the VM from shutting down
      thread.start();
      //ConnectThread是driver的一个内部类,继承了Runnable
      return ct.getResult(timeout);
    } catch (PSQLException ex1) {
      logger.debug("Connection error:", ex1);
      throw ex1;
    } catch (java.security.AccessControlException ace) {
      throw new PSQLException(
          GT.tr(
              "Your security policy has prevented the connection from being attempted.  You probably need to grant the connect java.net.SocketPermission to the database server host and port that you wish to connect to."),
          PSQLState.UNEXPECTED_ERROR, ace);
    } catch (Exception ex2) {
      logger.debug("Unexpected connection error:", ex2);
      throw new PSQLException(
          GT.tr(
              "Something unusual has occurred to cause the driver to fail. Please report this exception."),
          PSQLState.UNEXPECTED_ERROR, ex2);
    }
  }

用ConnectThread的run()完成初始化Connection。
再通过ConnectThread的getResult()获得初始化的connection。

private static class ConnectThread implements Runnable {
    ConnectThread(String url, Properties props) {
      this.url = url;
      this.props = props;
    }
    //上面就是通过ConnectThread.run()方法初始化Connection。
    public void run() {
      Connection conn;
      Throwable error;

      try {
        conn = makeConnection(url, props);
        error = null;
      } catch (Throwable t) {
        conn = null;
        error = t;
      }

      synchronized (this) {
        if (abandoned) {
          if (conn != null) {
            try {
              conn.close();
            } catch (SQLException e) {
            }
          }
        } else {
          result = conn;
          resultException = error;
          notify();
        }
      }
    }
 }

ConnectThread的run()方法又用到makeConnection(),返回new PgConnection()的实例。

  //makeConnection(),调用到PgConnection的构造方法。
  private static Connection makeConnection(String url, Properties props) throws SQLException {
    return new PgConnection(hostSpecs(props), user(props), database(props), props, url);
  }

PgConnection实现了Connection接口。让我们来看看上面的Connection使用的构造函数。

public PgConnection(HostSpec[] hostSpecs,
                      String user,
                      String database,
                      Properties info,
                      String url) throws SQLException {
    this.creatingURL = url;
    //此构造函数过长,就不全部贴出代码。实际呢,就像是this.creatingURL = url这个一样,对PgConnection的内部数据进行赋值。
  }

这就是通过driver获取connection的完整的一个过程。

3、通过connection获取prepareStatement

  //prepareStatement(sql)方法。
  public java.sql.PreparedStatement prepareStatement(String sql) throws SQLException {
    return prepareStatement(sql, java.sql.ResultSet.TYPE_FORWARD_ONLY,
        java.sql.ResultSet.CONCUR_READ_ONLY);
  }
  //调用这方法
  public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
      throws SQLException {
    checkClosed();
    return prepareStatement(sql, resultSetType, resultSetConcurrency, getHoldability());
  }
  //再调用这方法。
  public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
      int resultSetHoldability) throws SQLException {
    checkClosed();
    return new PgPreparedStatement(this, sql, resultSetType, resultSetConcurrency,
        resultSetHoldability);
  }
  //通过此构造函数返回PgPreparedStatement对象。
  PgPreparedStatement(PgConnection connection, String sql, int rsType, int rsConcurrency,
      int rsHoldability) throws SQLException {
    this(connection, connection.borrowQuery(sql), rsType, rsConcurrency, rsHoldability);
  }

  PgPreparedStatement(PgConnection connection, CachedQuery query, int rsType,
      int rsConcurrency, int rsHoldability) throws SQLException {
    super(connection, rsType, rsConcurrency, rsHoldability);

    this.preparedQuery = query;
    this.preparedParameters = this.preparedQuery.query.createParameterList();
    setPoolable(true); 
  }
  //正真的返回的PgStatement的构造函数,获得Statement
  PgStatement(PgConnection c, int rsType, int rsConcurrency, int rsHoldability)
      throws SQLException {
    this.connection = c;
    forceBinaryTransfers |= c.getForceBinary();
    resultsettype = rsType;
    concurrency = rsConcurrency;
    setFetchSize(c.getDefaultFetchSize());
    setPrepareThreshold(c.getPrepareThreshold());
    this.rsHoldability = rsHoldability;
  }

4、Statement 执行executeQuery获得resultSet

  public java.sql.ResultSet executeQuery() throws SQLException {
    if (!executeWithFlags(0)) {
      throw new PSQLException(GT.tr("No results were returned by the query."), PSQLState.NO_DATA);
    }

    if (result.getNext() != null) {
      throw new PSQLException(GT.tr("Multiple ResultSets were returned by the query."),
          PSQLState.TOO_MANY_RESULTS);
    }
    //这里直接就可以通过result.getResultSet()
    return result.getResultSet();
  }

这里发现Statement没有去执行sql,而是通过ResultWrapper.getResultSet()直接获得结果。实际上是因为在上面获取statement的时候,

  //connection.borrowQuery(sql)已经完成对sql的执行。并且把查询到的结果放入ResultWrapper中。感兴趣的同学可以自己去查看源代码。
  PgPreparedStatement(PgConnection connection, String sql, int rsType, int rsConcurrency,
      int rsHoldability) throws SQLException {
    this(connection, connection.borrowQuery(sql), rsType, rsConcurrency, rsHoldability);
  }

5、对resultSet进行解析

这里就简单的介绍下resultSet.getString()方法。实际上在上面执行sql的时候,已经把获取的数据的都放在了resultSet的Field fields[]中,在getString()方法是,实际只是通过索引来获得fields中的数据而已。

 public String getString(int columnIndex) throws SQLException {
    checkResultSet(columnIndex);
    if (wasNullFlag) {
      return null;
    }

    if (isBinary(columnIndex) && getSQLType(columnIndex) != Types.VARCHAR) {
    //获得数据,之后只是进行判断和一些小处理
      Field field = fields[columnIndex - 1];
      Object obj = internalGetObject(columnIndex, field);
      if (obj == null) {
        return null;
      }

      if (obj instanceof java.util.Date) {
        int oid = field.getOID();
        return connection.getTimestampUtils().timeToString((java.util.Date) obj,
            oid == Oid.TIMESTAMPTZ || oid == Oid.TIMETZ);
      }
      if ("hstore".equals(getPGType(columnIndex))) {
        return HStoreConverter.toString((Map<?, ?>) obj);
      }
      return trimString(columnIndex, obj.toString());
    }

    Encoding encoding = connection.getEncoding();
    try {
      return trimString(columnIndex, encoding.decode(this_row[columnIndex - 1]));
    } catch (IOException ioe) {
      throw new PSQLException(
          GT.tr(
              "Invalid character data was found.  This is most likely caused by stored data containing characters that are invalid for the character set the database was created in.  The most common example of this is storing 8bit data in a SQL_ASCII database."),
          PSQLState.DATA_ERROR, ioe);
    }
  }

因为代码量实在太多,只是贴出一套流程的关键代码。其中也可能有理解有误的地方,如果你觉得哪里不对,可以在评论里指点一二。

转载自:https://blog.csdn.net/juewang_love/article/details/53891567

You may also like...