background

How does JDBC connect to the database?

Most people only know a few steps of JDBC, but they don’t really know how the underlying database is connected.

The interviewer is not asking too many questions, but if it is a beginner or intermediate level, it is.

But wouldn’t it be great if you knew the answer at an elementary or intermediate level?

Most people don’t know how to answer this question, but for a little bit of thinking, we can guess that today we are going to find out how JDBC is connected to the database at the bottom. No more guessing in the future.

On the other hand, if an interviewer asks you about JDBC, you can see how the underlying database is connected, which is likely to confuse many relatively inexperienced interviewers.

What is a JDBC?

JDBC (Java DataBase Connectivity) is a bridge between Java and databases, a “specification” rather than an implementation that can execute SQL statements. JDBC consists of a set of classes and interfaces written in the Java language. There are implementations for different types of databases. Note that the code in this article is implemented for the MySQL database.

The JDBC framework

It is divided into two-tier architecture and three-tier architecture.

double

What it does: In this architecture, a Java Applet or application accesses a data source directly.

Condition: The Driver is required to interact with the accessed database.

Mechanism: User commands are passed to a database or other data source and the results are returned.

Deployment: The data source can be on another machine and the user is connected over a network, called a C/S configuration (which can be an Intranet or the Internet).

Three layers

The side architecture is special in that it introduces mid-tier services.

Flow: Commands and structures pass through this layer.

Attractive: can increase enterprise data access control, as well as multiple types of updates; It also simplifies application deployment and, in most cases, provides performance benefits.

Historical trends: In the past, the middle layer was written in C or C++ because of performance problems. With the development of optimized compilers (which convert Java bytecode into efficient specific machine code) and technology such as EJB, Java began to be used for the development of the middle layer. With the use of Java as the server code language, JDBC has gained prominence.

An introduction to case

Here is a JDBC entry level example:

public class JdbcDemo {
    public static final String URL = "jdbc:mysql://localhost:3306/mblog";
    public static final String USER = "root";
    public static final String PASSWORD = "123456";

    public static void main(String[] args) throws Exception { 
        Class.forName("com.mysql.jdbc.Driver"); 
        Connection conn = DriverManager.getConnection(URL, USER, PASSWORD); 
        Statement stmt = conn.createStatement(); 
        ResultSet rs = stmt.executeQuery("SELECT id, name, age FROM m_user where id =1"); 
        while(rs.next()){
            System.out.println("name: "+rs.getString("name") +" 年龄:"+rs.getInt("age")); }}}Copy the code

JDBC steps

Database driver:

Class.forName("com.mysql.jdbc.Driver"); 
Copy the code

Get a connection:

Connection conn = DriverManager.getConnection(URL, USER, PASSWORD); 
Copy the code

Create a Statement or PreparedStatement object:

Statement stmt = conn.createStatement(); 
Copy the code

Execute SQL database query:

ResultSet rs = stmt.executeQuery("SELECT id, name, age FROM m_user where id =1"); 
Copy the code

Parsed result set:

System.out.println("name: "+rs.getString("name") +" 年龄:"+rs.getInt("age"));
Copy the code

Finally, the shutdown of various resources.

Database driver

MySql driver class:

Class.forName("com.mysql.jdbc.Driver"); 
Copy the code

After we have installed the database, our application can not directly use the database, must be through the corresponding database driver, through the driver to deal with the database. In fact, it is the DATABASE vendor’s JDBC interface implementation, that is, the Connection interface implementation class JAR file.

Driver interface

Java.sql.Driver This interface is provided for implementation by database vendors. MySQL, for example, depends on the corresponding JAR package.

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.016.</version>
</dependency>
Copy the code

MySQL database implementation driver implementation class:

package com.mysql.cj.jdbc;
import java.sql.SQLException; 
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    static {
        try {
            // Register the driver
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!"); }}public Driver(a) throws SQLException {}}Copy the code

DriverManager is a rt.jar class, (rt= Runtime), we need to register the driver class.

Method in the DriverManager class
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);
}
Copy the code

Load the Oracle driver accordingly:

Class.forName("oracle.jdbc.driver.OracleDriver"); 
Copy the code

Sql Server driver:

Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");
Copy the code

For a link

Give us what looks like this line of code:

Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
Copy the code

Now let’s take a closer look at this line of code. How does the underlying database connect?

The getConnection method takes three parameters: the link address, user name, and password.

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()));
 }
Copy the code

Create a Properties object, which is a subclass of HashTable.

public class Properties extends Hashtable<Object.Object> {
    / /...
}
Copy the code

Look at the getConnection method:

// Worker method called by the public getConnection() methods.
private static Connection getConnection( String url, java.util.Properties info, Class
        caller) throws SQLException { ClassLoader callerCL = caller ! =null ? caller.getClassLoader() : null;
  SQLException reason = null;
  // Iterate over the database driver registered with the valve
  for(DriverInfo aDriver : registeredDrivers) {  
           try { 
                // Get the connection
                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; }}}}Copy the code

The key to this code is this line:

Connection con = aDriver.driver.connect(url, info);
Copy the code

The connet() method is each database-driven implementation.

package com.mysql.cj.jdbc;
public class NonRegisteringDriver implements java.sql.Driver {
     @Override
    public java.sql.Connection connect(String url, Properties info) throws SQLException { 
        // Some non-essential code is omitted
        // Here are the main points
        ConnectionUrl conStr = ConnectionUrl.getConnectionUrlInstance(url, info);
        switch (conStr.getType()) {
                //SINGLE_CONNECTION("jdbc:mysql:", HostsCardinality.SINGLE), //
                case SINGLE_CONNECTION:
                    return com.mysql.cj.jdbc.ConnectionImpl.getInstance(conStr.getMainHost());
                case LOADBALANCE_CONNECTION:
                    return LoadBalancedConnectionProxy.createProxyInstance((LoadbalanceConnectionUrl) conStr);
                case FAILOVER_CONNECTION:
                    return FailoverConnectionProxy.createProxyInstance(conStr);
                case REPLICATION_CONNECTION:
                    return ReplicationConnectionProxy.createProxyInstance((ReplicationConnectionUrl) conStr);
                default:
                    return null; }}}Copy the code

ConnectionUrl from this class name should be able to guess that there is no real connection, just create a ConnectionUrl related information wrapper.

public abstract class ConnectionUrl implements DatabaseUrlContainer {
    private static final String DEFAULT_HOST = "localhost";
    private static final int DEFAULT_PORT = 3306;
    / /...
}    
Copy the code

Familiar figure, MySQL database default port. Let’s move on to the next important line of code:

ConnectionImpl.getInstance(conStr.getMainHost());

So this is getting an instance, and not surprisingly, this is where the connection is made. Continue to:

//ConnectionImpl
public static JdbcConnection getInstance(HostInfo hostInfo) throws SQLException {
     return new ConnectionImpl(hostInfo);
}
Copy the code

The ConnectionImpl constructor calls the createNewIO method:

    @Override
    public void createNewIO(boolean isForReconnect) {
        synchronized (getConnectionMutex()) {  
            try {
                if (!this.autoReconnect.getValue()) {
                    connectOneTryOnly(isForReconnect);
                    return;
                }
                connectWithRetries(isForReconnect);
            } catch (SQLException ex) { 
            }
        }
    }
private void connectOneTryOnly(boolean isForReconnect) throws SQLException {
        Exception connectionNotEstablishedBecause = null; 
            JdbcConnection c = getProxy();
            // See the familiar connet method again,
            this.session.connect(this.origHostInfo, this.user, this.password, this.database, DriverManager.getLoginTimeout() * 1000, c); 

            this.session.setQueryInterceptors(this.queryInterceptors); 

    }
Copy the code

Session is NativeSession.

public void connect(HostInfo hi, String user, String password, String database, int loginTimeout, TransactionEventHandler transactionManager)
            throws IOException {  
    SocketConnection socketConnection = new NativeSocketConnection();
    socketConnection.connect(this.hostInfo.getHost(), this.hostInfo.getPort(), this.propertySet, getExceptionInterceptor(), this.log, loginTimeout); 
    this.protocol.connect(user, password, database);                     this.protocol.getServerSession().setErrorMessageEncoding(this.protocol.getAuthenticationProvider().getEncodingForHandshake()); 
}
Copy the code

In this method, we see that the Socket name starts with the class, ha ha, is used to communicate with Socket.

Highlights continue:

 socketConnection.connect(this.hostInfo.getHost(), this.hostInfo.getPort(), ...) ;Copy the code

Go to the NativeSocketConnection class method:

//com.mysql.cj.protocol.a.NativeSocketConnection
@Override
public void connect(String hostName, int portNumber, PropertySet propSet, ExceptionInterceptor excInterceptor, Log log, int loginTimeout) {  
  this.mysqlSocket = this.socketFactory.connect(this.host, this.port, propSet, loginTimeout);
  / /...
}            
Copy the code

So the socketFactory here is the StandardSocketFactory. So we call StandardSocketFactory’s connect method:

//StandardSocketFactory
public <T extends Closeable> T connect(String hostname, int portNumber, PropertySet pset, int loginTimeout) throws IOException {
    this.rawSocket = createSocket(pset);
    this.rawSocket.connect(sockAddr, getRealTimeout(connectTimeout));
}   
protected Socket createSocket(PropertySet props) {
     return new Socket();
}
Copy the code

Here even if in the end, say plain JDBC bottom is to use ** “Socket” ** to connect to the database.

Commonly used method

To obtain the Statement

Three types of

To execute SQL statements, you must obtain an instance of java.sql.Statement, which can be of one of three types:

  • Execute static SQL statements. This is usually implemented through the Statement instance.
  • Execute dynamic SQL statements. This is typically implemented through a PreparedStatement instance.
  • Execute database stored procedures. This is usually implemented through the CallableStatement instance.

Specific acquisition method

Statement stmt = con.createStatement() ;   
PreparedStatement pstmt = con.prepareStatement(sql) ;   
CallableStatement cstmt =  con.prepareCall("{CALL demoSp(? , ?)}");Copy the code

The similarities and differences between Statement and PreparedStatement as well as their advantages and disadvantages

Both are used to execute SQL statements

Different: A PreparedStatement is created based on an SQL Statement. It can set parameters and specify corresponding values instead of using string concatenation.

Advantages of preparedStatements:

1, its use of parameter Settings, readable, not easy to remember mistakes. String concatenation in statement results in poor readability and maintenance.

2. It has a precompilation mechanism and performs faster than Statement.

3, it can effectively prevent SQL injection attacks.

Execute vs. executeUpdate

Similarities: Both can add, delete, and modify operations.

Difference:

1. Execute the query statement and fetch the result from getResult. ExecuteUpdate cannot execute the query.

Execute Returns Boolean. True indicates that a query statement is executed. False indicates that an INSERT, delete, or update statement is executed. The return value from executeUpdate is int, indicating how many pieces of data were affected.

ResultSet ResultSet processing

The result set returned here in the previous introductory case is ResultSetImpl

ResultSetImpl class diagram

Commonly used methods of getting values

  • GetString (int index) and getString(String columnName) : Obtain data objects of the types vARCHAR and CHAR in the database.
  • GetFloat (int Index), getFloat(String columnName) : Get data objects that are of type Float in the database.
  • GetDate (int index), getDate(String columnName) : Obtain data of the Date type in the database.
  • GetBoolean (int index), getBoolean(String columnName) : Get data in the database that is of Boolean type.
  • GetObject (int index), getObject(String columnName) : Obtains data of any type in the database.

Common method of getting rows

  • Next () : Moves to the next line
  • Previous() : Moves to the Previous row
  • Absolute (int row) : Moves to a specified row
  • BeforeFirst () : Moves the first of a resultSet.
  • AfterLast () : Moves to the end of the resultSet.

Common data type conversion

So that’s the result set, and that’s it.

Resources to shut down

Resource closing is not a part of the business code, mainly for some resources to close, so as not to hold resources all the time. In addition, resource closures that we handle are usually handled in finally.

conclusion

This paper mainly talks about the following contents:

  • What is JDBC?
  • How are database driven loads and registrations handled?
  • The highlight is what we call JDBC connecting to a database. How do you connect to a database at the bottom?
  • Common methods of result set processing