Chapter 25 JDBC and database connection pool
821. Schematic diagram of JDBC
-
JDBC provides a unified interface for accessing different databases and shields the use details for users. Java programmers can use JDBC API to connect to any database system that provides jdbc driver, so as to complete various operations on the database
-
The Java designer defines the interface specification for operating the database, which is implemented by their respective database manufacturers. Java programmers only need to program for this set of interfaces.
822. JDBC Simulation Implementation
823. JDBC quick start
-
JDBC API is a series of interfaces. It unifies and standardizes the connection between application and database, executes SQL statements, and obtains return results. The relevant interfaces and classes are in Java SQL and javax SQL package
-
JDBC programming steps:
- Register Driver: load Driver class
- Get Connection: get Connection object
- Execute SQL: get Statement object
- Release resources
- Database connection method 1:
package p823; import com.mysql.jdbc.Driver; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; /** * Connect to database in static loading mode * @author Spring-_-Bear * @version 2021-11-08 20:56 */ public class Jdbc01 { public static void main(String[] args) throws SQLException { // Database connected to: jdbc:mysql: / / host IP address: port / db_name String url = "jdbc:mysql://localhost:3306/temp"; // Set user name and password Properties properties = new Properties(); properties.setProperty("user", "springbear"); properties.setProperty("password","123"); // Load driver: new com mysql. jdbc. Driver() Driver driver = new Driver(); // Get connection Connection connect = driver.connect(url, properties); // SQL statement String insert = "INSERT INTO actor VALUES (NULL,'Zhang San','male','1970-01-01','10086');"; // Get an object that executes a static SQL statement and returns the results it generates Statement statement = connect.createStatement(); // Returns the number of rows affected int rows = statement.executeUpdate(insert); System.out.println("Returns the number of rows affected = " + rows); statement.close(); connect.close(); } }
824. Database connection mode 2
/** * Mode 1 belongs to static loading, with poor flexibility and strong dependence. Mode 2 is considered to use reflection mechanism */ public void connect02() throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException { // Database connected to: jdbc:mysql: / / host IP address: port / db_name String url = "jdbc:mysql://localhost:3306/temp"; // Set user name and password Properties properties = new Properties(); properties.setProperty("user", "springbear"); properties.setProperty("password","123"); // Load class information Class<?> aClass = Class.forName("com.mysql.jdbc.Driver"); // Get class instance Driver driver = (Driver)aClass.newInstance(); // Get connection Connection connect = driver.connect(url, properties); }
825. Database connection mode 3
/** * Replace Driver with Driver manager for unified management */ public void connect03() throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException { // Load class information Class<?> aClass = Class.forName("com.mysql.jdbc.Driver"); // Get class instance Driver driver = (Driver)aClass.newInstance(); String url = "jdbc:mysql://localhost:3306/temp"; String user = "springbear"; String pwd = "123"; // Register driver DriverManager.registerDriver(driver); // Get connection Connection connection = DriverManager.getConnection(url, user, pwd); }
826. Database connection mode 4
/** * Use class Forname auto complete registration driver */ public void connect04() throws ClassNotFoundException, SQLException { // Load class information and automatically complete the registration during the process of loading Driver Class.forName("com.mysql.jdbc.Driver"); /* * static { * try { * DriverManager.registerDriver(new Driver()); * } catch (SQLException var1) { * throw new RuntimeException("Can't register driver!"); * } * } */ String url = "jdbc:mysql://localhost:3306/temp"; String user = "springbear"; String pwd = "123"; // Get connection Connection connection = DriverManager.getConnection(url, user, pwd); }
827. Database connection mode 5
- mysql-connector-java-5.1.37-bin.jar driver file 5.1 Class is not required after 6 Forname ("com.mysql.jdbc.Driver") can also get a connection directly. Reason: jdbc4 has been used since jdk5, so it is no longer necessary to explicitly call class Forname() registers the driver, but automatically calls meta-inf \ services \ Java. Inf under the driver jar package sql. Register the class name in the driver text
/** * On the basis of mode 4, the configuration file is used for connection, which is more flexible */ public void connect05() throws IOException, ClassNotFoundException, SQLException { // Read information from configuration file Properties properties = new Properties(); properties.load(new FileInputStream("src\\mysql.properties")); String user = properties.getProperty("user"); String password = properties.getProperty("password"); String driver = properties.getProperty("driver"); String url = properties.getProperty("url"); // Load the class information, automatically register the driver and obtain the connection Class.forName(driver); Connection connection = DriverManager.getConnection(url, user, password); }
828. ResultSet bottom layer
- ResultSet: indicates the result set of the data table read from the database. The ResultSet object maintains a cursor pointing to the current data row. Initially, the cursor is positioned before the first line. It has a next method to move the cursor to the next row, and because it returns false when there are no more rows in the ResultSet object, it often uses a while loop to traverse the result set
- com. mysql. jdbc. There is a rowData (Interface) type field under the jdbc42 resultset class. There is an ArrayList type collection rows in the rowData, and there is an internalRowData of byte [] type in the rows. Where the data is really stored
829. SQL injection
- Statement object, which is used to execute static SQL statements and return the generated results
- There are three ways to operate the database after the connection is established:
- Statement: SQL injection exists
- PreparedStatement: preprocessing
- CallableStatement: used to execute database stored procedures
- SQL injection is to use some systems to inject illegal SQL statement segments or commands into the data input by users without sufficient inspection of the data input by users, so as to maliciously attack the database
package p823; import java.io.FileInputStream; import java.io.IOException; import java.sql.*; import java.util.Properties; import java.util.Scanner; /** * @author Spring-_-Bear * @version 2021-11-09 09:48 */ public class SqlInjection { public static void main(String[] args) throws IOException, ClassNotFoundException, SQLException { // Get the user name and password that the user wants to query // Input userName = 1' or // Input pwd = or '1' = '1 Scanner scanner = new Scanner(System.in); System.out.print("Input the name that you want to query:"); String userName = scanner.nextLine(); System.out.print("Input the password that you want to query:"); String pwd = scanner.nextLine(); // Load profile Properties properties = new Properties(); properties.load(new FileInputStream("config\\temp.properties")); // Load the driver class information and automatically register the driver Class.forName(properties.getProperty("driver")); // Get connection Connection connection = DriverManager.getConnection(properties.getProperty("url"), properties); Statement statement = connection.createStatement(); String select = "SELECT * FROM admin WHERE name='" + userName + "' AND pwd= '" + pwd + "'"; ResultSet resultSet = statement.executeQuery(select); while (resultSet.next()) { userName = resultSet.getString(1); pwd = resultSet.getString(2); System.out.println(userName + "\t" + pwd); } resultSet.close(); statement.close(); connection.close(); } }
830. Statement
831. Preprocessing query
- The parameters in the SQL statement executed by PreparedStatement are represented by question marks (?), and the setXxx() method of PreparedStatement object is called to set these parameters. The setXxx() method has two parameters. The first parameter is the index of the parameter in the SQL statement to be set, starting from 1. The second parameter is to set the value of the parameter in the SQL statement
- Benefits of pretreatment:
String select = "SELECT * FROM admin WHERE name = ? AND pwd= ?"; // SQL statement preprocessing PreparedStatement preparedStatement = connection.prepareStatement(select); preparedStatement.setString(1, userName); preparedStatement.setString(2, pwd); // Execute the SQL statement to get the result set ResultSet resultSet = preparedStatement.executeQuery();
832. Pretreatment DML
833. JDBC API
Interface name | Method name | function |
---|---|---|
Connection | createStatement() | Create an object that executes a static SQL statement |
createPreparedStatement(sql) | Get SQL statement preprocessing object | |
Statement | executeUpdate(sql) | Execute the DML statement and return the number of affected rows |
executeQuery(sql) | Execute DQL statement and return result set | |
execute(sql) | Execute any SQL statement and return a Boolean value | |
PreparedStatement | executeUpdate(sql) | Execute the DML statement and return the number of affected rows |
executeQuery(sql) | Execute DQL statement and return result set | |
execute(sql) | Execute any SQL statement and return a Boolean value | |
setXxx(index,value) | Set values in SQL statements | |
setObject(index,value) | Set values in SQL statements | |
ResultSet | next() | Move down one row and return false at the end of the table |
previous() | Move up one line and return false to the header | |
getXxx(index || colLabel) | Gets the value of the specified column | |
getObject(index || colLabel) | Gets the value of the specified column |
834. JDBC Utils development
package utils; import java.io.FileInputStream; import java.io.IOException; import java.sql.*; import java.util.Properties; /** * Database connection and resource closing tools * * @author Spring-_-Bear * @version 2021-11-09 11:40 */ public class JdbcUtils { private static String driver; private static String url; private static String user; private static String password; private static String path = "config\\temp.properties"; /** * Read file information and initialize fields */ static { Properties properties = new Properties(); try { properties.load(new FileInputStream(path)); driver = properties.getProperty("driver"); url = properties.getProperty("url"); user = properties.getProperty("user"); password = properties.getProperty("password"); } catch (IOException e) { // In actual development, such compilation exceptions will be converted into running exceptions, which can be handled by the caller, which is more convenient throw new RuntimeException(e); } } /** * Get a connection to the database * * @return Database connection object */ public static Connection getConnection() { try { return DriverManager.getConnection(url, user, password); } catch (SQLException e) { // In actual development, such compilation exceptions will be converted into running exceptions, which can be handled by the caller, which is more convenient throw new RuntimeException(e); } } /** * Close corresponding resources * * @param resultSet none * @param statement none * @param connection none */ public static void close(ResultSet resultSet, Statement statement, Connection connection) { try { if (resultSet != null) { resultSet.close(); } if (statement != null) { statement.close(); } if (connection != null) { connection.close(); } } catch (SQLException e) { // In actual development, such compilation exceptions will be converted into running exceptions, which can be handled by the caller, which is more convenient throw new RuntimeException(e); } } }
835. JDBC Utils DML
836. JDBC Utils query
837. Introduction to services
- In the JDBC program, when a Connectioon object is created, the transaction is automatically committed by default: each time an SQL statement is executed, if the execution is successful, it will be automatically committed to the database instead of rolling back
- You can call the setAutoCommit(false) method of the Connection interface to cancel the auto commit transaction
- After all the SQL statements are executed successfully, the commit() method is called to commit the transaction. When one of the operations fails or an exception occurs, the rollback() method is called to roll back the transaction
838. Transaction processing
public void transaction() { Connection connection = null; PreparedStatement preparedStatement = null; String sub = "UPDATE account SET balance = balance - 100 WHERE id = 1"; String add = "UPDATE account SET balance = balance + 100 WHERE id = 2"; try { // Get connection connection = JdbcUtils.getConnection(); // Turn off auto commit to turn on transactions connection.setAutoCommit(false); // Execute SQL preparedStatement = connection.prepareStatement(add); preparedStatement.executeUpdate(); int temp = 1 / 0; preparedStatement = connection.prepareStatement(sub); preparedStatement.executeUpdate(); // Commit transaction connection.commit(); System.out.println("All SQL The operation is successful. Commit the transaction!"); } catch (SQLException | ArithmeticException e) { try { // If an exception occurs, undo the operation and roll back the transaction System.out.println("Exception occurred during program execution, roll back all operations!!!"); connection.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); } finally { JdbcUtils.close(null, preparedStatement, connection); } }
839. Batch applications
- When you need to insert or update records in batches, you can use the batch update mechanism of Java, which allows multiple statements to be submitted to the database for batch processing at one time. Generally, it is more efficient than submitting processing alone
Method name | function |
---|---|
addBatch() | Add SQL statements or parameters that require batch processing |
executeBatch() | Batch send execution |
clearBatch() | Empty batch package |
- When JDBC connects to MySQL, if batch processing is required, you need to add parameters to the url:
url = "jdbc:mysql://localhost:3306/temp?rewriteBatchedStatements=true"
- Batch processing is often used together with PreparedStatement, which can not only reduce the compilation times, but also reduce the running times, and greatly improve the efficiency
preparedStatement.addBatch(); if (i % 1000 == 0) { preparedStatement.executeBatch(); preparedStatement.clearBatch(); }
840. Batch source code analysis
/** * The first time an ArrayList < elementdata > is created * elementData => Object[] The preprocessed SQL statements will be stored * When the elementDate is full, it will be expanded by 1.5 times * When the specified capacity is reached, it will be sent to MySQL for execution * Batch processing will reduce the network overhead of sending SQL statements and reduce the number of compilations, so as to improve efficiency * @throws SQLException none */ public void addBatch() throws SQLException { synchronized(this.checkClosed().getConnectionMutex()) { if (this.batchedArgs == null) { this.batchedArgs = new ArrayList(); } for(int i = 0; i < this.parameterValues.length; ++i) { this.checkAllParametersSet(this.parameterValues[i], this.parameterStreams[i], i); } this.batchedArgs.add(new com.mysql.jdbc.PreparedStatement.BatchParams(this.parameterValues, this.parameterStreams, this.isStream, this.streamLengths, this.isNull)); } }
841. Analysis of disadvantages of traditional links
- The traditional JDBC database Connection is obtained by using DriverManager. Each time a Connection is established to the database, the Connection needs to be loaded into memory, and then the IP address, user name Whether the password (time-consuming 0.05s ~ 1s) is correct. When you need to connect to the database, you will request a Connection from the database. Frequent request operations will occupy too many system resources and easily cause the server to crash
- Every time the database connection is used, it must be disconnected in time. If the program fails to close normally due to exceptions, it will lead to database memory leakage and eventually database crash and restart
- The traditional method of obtaining connections cannot control the number of connections created. If there are too many connections, it may also lead to memory leakage and MySQL crash
842. Principle of database connection pool
- Put a certain number of connections in the buffer pool in advance. When you need to establish a database connection, just take one from the "buffer pool" and return it to the buffer pool after use (and continuously open the connection with the database)
- Database connection pool is responsible for allocating, managing and releasing database connections. It allows applications to reuse an existing database connection instead of re establishing one
- When the number of connections requested by the application from the connection pool exceeds the maximum number of connections, these requests will be added to the waiting queue
- The database connection pool of JDBC uses javax sql. DataSource is only an interface, and the specific implementation of the interface is left to the third party
Connection pool | characteristic |
---|---|
C3P0 | The speed is relatively slow and the stability is good (hibernate and spring are used at the bottom) |
Druid | The database connection pool provided by Alibaba integrates the advantages of DBCP, C3P0 and Proxool |
Proxool | It has the function of monitoring the status of the connection pool, and its stability is slightly worse than that of C3P0 |
BoneCP | Fast speed |
DBCP | The speed is faster than C3P0, but unstable |
843. C3P0 mode 1
// Create data source object ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource(); // Setting related information comboPooledDataSource.setDriverClass(driver); comboPooledDataSource.setJdbcUrl(url); comboPooledDataSource.setUser(user); comboPooledDataSource.setPassword(pwd); comboPooledDataSource.setInitialPoolSize(10); comboPooledDataSource.setMaxPoolSize(50); // Get connection Connection connection = comboPooledDataSource.getConnection(); connection.close();
844. C3P0 mode 2
// Set c3p0 config Copy the XML file to the project src directory ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("spring_bear"); Connection connection = comboPooledDataSource.getConnection(); connection.close();
845. Druid connection pool
// Load profile Properties properties = new Properties(); properties.load(new FileInputStream("config\\druid.properties")); // Create a new connection pool DataSource dataSource = DruidDataSourceFactory.createDataSource(properties); // Get connection Connection connection = dataSource.getConnection(); connection.close();
846. Druid tools
package utils; import com.alibaba.druid.pool.DruidDataSourceFactory; import javax.sql.DataSource; import java.io.FileInputStream; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; /** * Druid Connection pool * * @author Spring-_-Bear * @version 2021-11-09 22:17 */ public class JdbcUtilsByDruid { static DataSource dataSource; static String path = "config\\druid.properties"; static { Properties properties = new Properties(); try { properties.load(new FileInputStream(path)); dataSource = DruidDataSourceFactory.createDataSource(properties); } catch (Exception e) { e.printStackTrace(); } } /** * Returns a database connection from the Druid connection pool * * @return Connection * @throws SQLException none */ public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } /** * Close the connection and return the connection to the connection pool * * @param resultSet none * @param statement none * @param connection none */ public static void close(ResultSet resultSet, Statement statement, Connection connection) { try { if (resultSet != null) { resultSet.close(); } if (statement != null) { statement.close(); } if (connection != null) { connection.close(); } } catch (SQLException e) { throw new RuntimeException(e); } } }
847. ApDBUtils export
- After closing the connection, the resultSet result set cannot continue to be used; However, many times we want to continue to use the queried data after closing the connection. The way the resultSet stores the query results is not conducive to data management; When obtaining data from the resultSet result set, the operation method is not clear enough, and the getXxx() method is error prone and ambiguous
- Define a class that corresponds to the fields of the database table one by one. Such a class is generally called JavaBean or PoJo or Domain
- Encapsulate the field value of the returned result set into the object of the user-defined class, and put several such objects into the set to directly access the set, so as to obtain the query results of the database table
848. Complete the packaging in a native way
849. ApDBUtils query
- Commons dbutils is an open source JDBC tool class provided by Apache organization. It encapsulates JDBC. Using dbutils can greatly simplify the amount of JDBC coding
- QueryRunner class encapsulates the execution of SQL and is thread safe. It can add, delete, modify, query and batch processing
- The resultsethandler interface is used to handle Java sql. Resultset, which converts the queried data into another form
Interface name | function |
---|---|
ArrayHandler | Converts the first row of data in the result set to an array of objects |
ArrayListHandler | Convert each row in the result set into an array and store it in the List |
BeanHandler | Encapsulate the first row of data in the result set into a corresponding JavaBean instance |
BeanListHandler | Encapsulate each row of data in the result set into the corresponding JavaBean instance and store it in the List |
ColumnListHandler | Store a column of data in the result set in the List |
KeyedHandler(name) | Encapsulate each row of data into a map, and then store the map into another map |
MapHandler | Encapsulate the data of the first row of the result set into the Map. key is the column name and value is the corresponding value |
MapListHandler | Encapsulate each row of data in the result set into a Map and then store it in a List |
public void testApache() throws SQLException { // Get connection Connection connection = JdbcUtilsByDruid.getConnection(); // Get the query object implemented by Apache QueryRunner queryRunner = new QueryRunner(); String select = "SELECT * FROM cost WHERE id >= ? AND id <= ?"; List<Fishing> fishings = queryRunner.query(connection, select, new BeanListHandler<>(Fishing.class), 1, 10); for (Fishing fishing : fishings) { System.out.println(fishing); } JdbcUtilsByDruid.close(null, null, connection); }
850. ApDBUtils source code analysis
- When creating a JavaBean class, the data type of the field of the class is forced to use the wrapper class corresponding to the eight basic data types, because the field value in the MySQL database table may be empty, and only the reference data type of Java has NULL value
851. ApDBUtils query 2
852. ApDBUtilsDML
853. BasicDAO issues
- Apache dbutils + Druid connectionpoll simplifies JDBC development, but it still has the following shortcomings:
- SQL statements are fixed and cannot be passed in through parameters. They are not universal and need to be improved to facilitate CRUD operation
- For the query SELECT operation, if there is a return value and the return type cannot be determined, it needs to be solved by using generics
- In the future, there will be many tables in the database, and the business requirements are complex. It is impossible to rely on only one Java class
- Solution: a JavaBean class is designed for each table, and a class Dao specially operating it is designed for each database table. The common parts of all specific Dao classes are abstracted from the parent class BasciDao, so as to make better use of polymorphism to complete functions
- DAO (data access object): an object that accesses database data
- When creating a JavaBean class, you must give a parameterless constructor to facilitate the reflection mechanism to obtain this class information
854. BasicDAO analysis
855. BasicDAO implementation 1
package dao.dao; import dao.utils.JdbcUtilsByDruid; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import org.apache.commons.dbutils.handlers.BeanListHandler; import org.apache.commons.dbutils.handlers.ScalarHandler; import java.sql.Connection; import java.sql.SQLException; import java.util.List; /** * Encapsulates operations on database tables * * @author Spring-_-Bear * @version 2021-11-10 11:03 */ public class BasicDao<T> { QueryRunner queryRunner = new QueryRunner(); /** * DML operation * * @param sql SQL sentence * @param params SQL Value of parameter in * @return Number of rows affected */ public int update(String sql, Object... params) { Connection connection = null; try { connection = JdbcUtilsByDruid.getConnection(); return queryRunner.update(connection, sql, params); } catch (SQLException e) { // The compilation exception is converted into a running exception for the convenience of the caller throw new RuntimeException(e); } finally { JdbcUtilsByDruid.close(null, null, connection); } } /** * Query multiple rows * * @param sql SQL sentence * @param clazz Class object of concrete class * @param params SQL Concrete value in statement * @return The result set queried from the database is encapsulated in ArrayList */ public List<T> getMultiRows(String sql, Class<T> clazz, Object... params) { Connection connection = null; try { connection = JdbcUtilsByDruid.getConnection(); return queryRunner.query(connection, sql, new BeanListHandler<T>(clazz), params); } catch (SQLException e) { // The compilation exception is converted into a running exception for the convenience of the caller throw new RuntimeException(e); } finally { JdbcUtilsByDruid.close(null, null, connection); } } /** * Query a row * * @param sql SQL sentence * @param clazz Class object of concrete class * @param params SQL Concrete value in statement * @return A row of data queried from the database */ public T getOneRow(String sql, Class<T> clazz, Object... params) { Connection connection = null; try { connection = JdbcUtilsByDruid.getConnection(); return queryRunner.query(connection, sql, new BeanHandler<T>(clazz), params); } catch (SQLException e) { // The compilation exception is converted into a running exception for the convenience of the caller throw new RuntimeException(e); } finally { JdbcUtilsByDruid.close(null, null, connection); } } /** * Query single row and single column * * @param sql SQL sentence * @param params SQL Concrete value in statement * @return A cell data queried from the database */ public Object getOneObj(String sql, Object... params) { Connection connection = null; try { connection = JdbcUtilsByDruid.getConnection(); return queryRunner.query(connection, sql, new ScalarHandler(), params); } catch (SQLException e) { // The compilation exception is converted into a running exception for the convenience of the caller throw new RuntimeException(e); } finally { JdbcUtilsByDruid.close(null, null, connection); } } }