Database Transaction Processing and Resource Pooling

[TOC]

0x00 database transaction

What is a transaction?

Answer: Transaction refers to a group of operations containing multiple tiny logical units. As long as one of the logic fails, then this group of operations will all end in failure and all data will return to the original state (rollback) , There is no half-success, half-unsuccessful situation.

Why have business?

A: To ensure the success of the logic. Transactions may not be commonly used in ordinary CRUD, but if we have a requirement that a set of operations must be successfully executed, the task is completed. As long as there is an error, all tasks will return to the original state. This is the application scenario of using transactions, such as: bank transfer example;

Operational transaction processes and commands in the database:

-- Check autocommit Whether auto-commit is turned off
> SHOW VARIABLES LIKE 'autocommit';  
"autocommit"	"ON"

-- The current terminal temporarily turns off autocommit
> SET autocommit = off;

-- start transaction
> START TRANSACTION;

-- Commit or roll back the transaction
commit;     --- Commit the transaction and the data will be written to the database on disk
rollback ;  --- The data is rolled back, back to the original state.
copy

Test SQL statement:

CREATE TABLE account(
    `id` INT PRIMARY KEY AUTO_INCREMENT,
    `name` VARCHAR(64) NOT NULL,
    `money` FLOAT DEFAULT 0
);

INSERT INTO account VALUES (null,'WeiyiGeek',1000),(null,'muzi',1000);
copy
1. Transaction processing in JDBC

Description: The transaction is only for the connection connection object. If another connection object is opened, then it is the default commit (note: the transaction will be automatically committed ).

  1. Turn off autocommit by setting (transactions are only for connections) conn.setAutoCommit(false)
  2. Commit transaction conn.commit();
  3. Rollback transaction conn.rollback();

JDBC uses JAVA code to demonstrate transactions:

package top.weiyigeek.Web;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.junit.Test;
import top.weiyigeek.Util.db;

public class Test_transaction {
  @Test
  public void transaction() {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    try {
      conn = db.getConn();
      
      //1. Connection: Transactions are automatically committed by default.  Turn off autocommit.
      conn.setAutoCommit(false);
      
      String sql = "update account set money = money - ? where id = ?";
      ps = conn.prepareStatement(sql);
      
      //2. Deduct money: deduct 100 yuan with ID 1
      ps.setInt(1, 100);
      ps.setInt(2, 1);
      ps.executeUpdate();
      
      //3. Set the exception to view the impact of transaction submission
      int a = 10 /0 ;
      
      //4. Add money, add 100 yuan to ID 2
      ps.setInt(1, -100);
      ps.setInt(2, 2);
      ps.executeUpdate();
      
      //5. Success: Commit the transaction.
      conn.commit();
      
    } catch (SQLException e) {
      try {
        //6. If an exception occurs in step 3, enter catch{} to roll back the transaction
        conn.rollback();
        System.out.println("\nSQL Transaction commit exception, transaction has been rolled back automatically!");
      } catch (SQLException e1) {
        e1.printStackTrace();
      }
      e.printStackTrace();
      
    }finally {
      db.release(conn, ps, rs);
    }
  }
}
copy

WeiyiGeek. Transaction Processing

2. Characteristics of transactions

Description: Transactions have four ACID properties including:

  • Atomicity (Atomicity English /ˌætəˈmɪsəti/): The logic in a transaction must be fully executed and inseparable. (Atom is the smallest unit in physics) Refers to the logic contained in the transaction, which is indivisible.
  • Consistency (English /kənˈsɪstənsi/): Refers to the integrity of the data before and after the execution of the transaction is consistent Refers to before and after the transaction is executed. data integrity
  • Isolation (Isolation English /ˌaɪsəˈleɪʃn/ ): means that a transaction should not be affected by other transactions during its execution Refers to the transaction should not be affected by other transactions during execution
  • Durability (Durability English /ˌdjʊərəˈbɪləti/ ): the transaction execution ends (commit or rollback), and the data should be persisted to the data Refers to the successful execution of the transaction, then the data should be persisted to disk.
3. Transaction security risks

Description: When the isolation level setting is not considered, the following problems will occur.

  • 1) Problems when reading
    • dirty read
    • non-repeatable read
    • hallucinations
  • 2) Problems while writing (missing updates)
    • pessimistic lock
    • optimistic locking

(1) Problems when reading

  • Dirty read: refers to a transaction that has read data that has not yet been committed by another transaction
  • Non-repeatable read: Refers to a transaction that reads data submitted by another transaction, resulting in inconsistent results of multiple queries.
  • Phantom read: Refers to a transaction that has read the inserted data (INSERT) committed by another transaction, resulting in inconsistent results of multiple queries.

(2) Problems when writing Lost update: refers to a transaction to modify the database and another transaction to modify the database, the last transaction, whether it is committed or rolled back, will cause the data update of the previous transaction to be lost;

WeiyiGeek. Lost Update

There are usually two ways to solve lost updates: pessimistic locking and optimistic locking

  • Pessimistic lock: It means that the transaction believes that lost updates will happen at the beginning. This is a very pessimistic thing. The specific operation steps are as follows:
    • 1. All transactions should query the data once before executing the operation. The query statement is as follows: select * from student for update ; the latter for update is actually a database lock mechanism, an exclusive lock.
    • 2. Which transaction executes this statement first, whichever transaction holds the lock, can query the data, and then execute this statement again in the subsequent transaction, there will be no data displayed, so you can only wait.
    • 3. Wait until the previous transaction submits the data, and the subsequent transaction data will come out, then you can continue to operate.
    • The mechanism of the pessimistic lock: It is a bit similar to when you go to the bathroom, whoever comes first can go in and squat, and the person who comes behind has to wait, and only the person inside can come in when they come out. This is actually the concept of synchronization in java.
  • Optimistic locking: refers to never feeling that lost updates will happen. So what exactly does it do? The programmer is required to add a field to the database and then when the subsequent update is performed, the field will be judged and compared, and the update will be allowed if it is consistent.
    • 1. An additional version field is added to the database table to record the version. By default, starting from 0, as long as the data in the table is modified, the version will be +1.
    • 2. Start A transaction and then start B transaction.
    • 3.A Perform database table operations first. Because no one has modified it before, transaction A is allowed to modify the database, but after the modification, the value of version is changed to 1.
    • 4. If the B transaction wants to perform modification at this time, modification is not allowed. Because transaction B has not queried the database content before, it thinks that the database version is still 0; but the version of the database has been modified by A, and it is already 1; so it is not allowed to modify at this time, and it is required to re-query.
    • 5. After B re-queries, it will get the data whose version is 1. This data is the data modified by the previous transaction of A. When B is modifying, it is also modified on the basis of A. So there will be no loss of updates.
    • Optimistic locking mechanism: It is actually implemented by comparing versions or fields, which is the same as the version control software [SVN, GIT] mechanism used.

WeiyiGeek. Optimistic Lock

4. Isolation level

Description: The isolation according to the transaction is often the following four isolation levels:

  • 1.Read Uncommitted [read uncommitted]: cause dirty reading
  • 2.Read Committed [Read committed]: Solve dirty reads and cause non-repeatable reads
  • 3.Repeatable Read [Repeatable Read]: MySQL data volume defaults to this isolation level; resolves dirty reads, non-repeatable reads, and unresolved phantom reads
  • 4.Serializable [Serializable]: Solution: Dirty read, non-repeatable read, phantom read but lack of performance;

Q: How to query the isolation level of the current session and change the isolation level of the current session?

-- Inquire
SQL > select @@tx_isolation;
-- +-----------------+
-- | @@tx_isolation  |
-- +-----------------+
-- | REPEATABLE-READ |
-- +-----------------+
-- 1 row in set (0.00 sec)

-- Fix setting the transaction isolation level of the current window to
SQL > set session transaction isolation level read uncommitted;
-- Query OK, 0 rows affected (0.00 sec)
copy

1) Read Uncommitted Description: A transaction can read data that has not been committed by another transaction; it will cause a "dirty read" to read the data in the database memory, not the real data on the disk.

SQL Example 1:

-- 1.Modify the current isolation level to`read uncommitted`
db_window-1 > set session transaction isolation level read uncommitted;
-- Query OK, 0 rows affected (0.00 sec)

-- 2.Check whether the modification is successful
db_window-1 > select @@tx_isolation;
+------------------+
| @@tx_isolation   |
+------------------+
| READ-UNCOMMITTED |
+------------------+
1 row in set (0.00 sec)

-- 3.The isolation level of terminal 2 is repeatable read by default
db_window-2 > select @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)

db_window-2 > update account set money = money + 100 where id = 1;
Query OK, 1 row affected (0.11 sec)
Rows matched: 1  Changed: 1  Warnings: 0

-- 4.open transaction
db_window-2 > start transaction;
Query OK, 0 rows affected (0.00 sec)
db_window-1 > start transaction;
Query OK, 0 rows affected (0.00 sec)


db_window-2 > update account set money = money - 100 where id = 2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

-- 5.Terminal 1 has read data that has not been submitted by terminal 2
db_window-1 > select * from account;
| id | name      | money |
|  1 | WeiyiGeek |  1100 |
|  2 | muzi      |  900 |


-- 6.Commit transaction (actual terminal 1 has read data from memory)
db_window-2 > commit;
copy

WeiyiGeek. Read Uncommitted

2) Read Committed Description: It is just the opposite of the previous read uncommitted, it can only read the data that has been committed by other transactions, and those uncommitted data cannot be read. Cause the problem: The results read before and after are different, which cannot be repeated!!!, The so-called non repeatable read means that you cannot perform multiple reads, otherwise the results will be different (at this time, we introduce the isolation level of repeatable read) Simply put: this isolation level can block the phenomenon of dirty reads, but it causes another problem that is non-repeatable reads.

-- 1.Set the isolation level for terminal 1
db_window-1 > set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)

db_window-1 > select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+
1 row in set (0.00 sec)

-- 2.Terminal 1 starts the transaction
db_window-1 > start transaction;
Query OK, 0 rows affected (0.00 sec)

-- 3.Terminal 1 queries raw data
db_window-1 > select * from account;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | WeiyiGeek |  1000 |
|  2 | muzi      |  1000 |
+----+-----------+-------+
2 rows in set (0.00 sec)

-- 4.Terminal 2 starts transaction
db_window-2 > select @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)

db_window-2 > start transaction;
Query OK, 0 rows affected (0.00 sec)


-- 5.Terminal 2 performs the update operation
db_window-2 > update account set money = money - 100 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

db_window-2 > update account set money = money + 100 where id = 2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

-- 6.Terminal 1 performs a query operation (the data has not changed)
db_window-1 > select * from account;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | WeiyiGeek |  1000 |
|  2 | muzi      |  1000 |
+----+-----------+-------+
2 rows in set (0.00 sec)

-- 7.Terminal 2 executes commit
db_window-2 > commit;
Query OK, 0 rows affected (0.05 sec)

-- 8.After submission, terminal 1 queries the data and finds that it has changed
db_window-1 > select * from account;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | WeiyiGeek |   900 |
|  2 | muzi      |  1100 |
+----+-----------+-------+
2 rows in set (0.00 sec)
copy

WeiyiGeek. Read Submitted

3) Repeatable Read [Repeat Read] Description: MySQL's default isolation level is this. This isolation level allows a transaction to repeatedly read data in its own session, and there will be no different results. Even if other transactions have committed, the previous data is still displayed. Repeatable read (REPEATABLE READ) function: ensure that the result of reading the same data multiple times in the same transaction is the same to avoid the occurrence of dirty reads and non-repeatable reads.

-- 1.Terminal 1 sets the isolation to repeat the read and view the current isolation
db_window-1 > set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)

db_window-1 > select @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ | 
+-----------------+
1 row in set (0.00 sec)

-- 2.Terminal 1 displays raw data
db_window-1 > select * from account;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | WeiyiGeek |  1000 |
|  2 | muzi      |  1000 |
+----+-----------+-------+
2 rows in set (0.00 sec)

-- 3.Terminal 1 starts transaction
db_window-1 > start transaction;
Query OK, 0 rows affected (0.00 sec)


-- 4.Terminal 2 checks the current isolation and opens a transaction
db_window-2 > select @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)

db_window-2 > start transaction;
Query OK, 0 rows affected (0.00 sec)

-- 5.Terminal 2 performs the update operation
db_window-2 > update account set money = money - 100 where id = 1;
Query OK, 1 row affected (0.06 sec)
Rows matched: 1  Changed: 1  Warnings: 0

db_window-2 > update account set money = money + 100 where id = 2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

-- 6.Terminal 1 executes the query statement and the result remains unchanged
db_window-1 > select * from account;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | WeiyiGeek |  1000 |
|  2 | muzi      |  1000 |
+----+-----------+-------+
2 rows in set (0.00 sec)

-- 7.Terminal 2 executes submit data
db_window-2 > commit;
Query OK, 0 rows affected (0.08 sec)


-- 8.Terminal 1 executes the query data and still does not change(The query results are consistent with the previous query results and will not change.)
db_window-1 > select * from account;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | WeiyiGeek |  1000 |
|  2 | muzi      |  1000 |
+----+-----------+-------+
2 rows in set (0.00 sec)
copy

WeiyiGeek. Repeatable Read

4) Serializable [Serializable] Description: This transaction level is the most advanced transaction level, which is stronger than the previous ones, that is, the previous problems [dirty read, non repeatable read, phantom read] can be solved, but this isolation level is generally more less

Other transactions must wait for the transaction that is currently operating the table to commit first before going down, otherwise they can only wait for the blocker; that is, if the isolation level of a connection is set to serialize whoever opens the transaction first, whoever opens the transaction first With the right to execute first (preconceived) and the previous transaction, it can be executed after committing or rolling back.

SQL example:

-- 1.Terminal 1 settings Serializable Serializable isolation level
db_window-1 > set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)

db_window-1 > select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| SERIALIZABLE   |
+----------------+
1 row in set (0.00 sec)

-- 2.Terminal 2 current isolation
db_window-2 > select @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)

-- 3.Terminal 1 queries raw data
db_window-1 > select * from account;
| id | name      | money |
|  1 | WeiyiGeek |  1000 |
|  2 | muzi      |  1000 |

-- 4.Terminal 2 starts the transaction first
db_window-2 > start transaction;
Query OK, 0 rows affected (0.00 sec)

-- 5.Terminal 1 starts the transaction next
db_window-1 > start transaction;
Query OK, 0 rows affected (0.00 sec)


-- 6.Terminal 2 executes CRUD SQL(There is no problem)
db_window-2 > insert into account values (null,'mariadb',1000);
Query OK, 1 row affected (0.00 sec)


-- 7.Terminal 1 waits for the transaction of terminal 2 to complete and executes, otherwise it will block including(CURD)
db_window-1 > update account set money = money - 100 where id = 3;
-- wait for terminal 2 Commit commit transaction 
Query OK, 1 row affected (46.09 sec)
Rows matched: 1  Changed: 1  Warnings: 0

-- 8.After the transaction execution of terminal 2 is completed, the above update statement can be executed
db_window-2 > commit;
Query OK, 0 rows affected (0.07 sec)

-- 9.Terminal 1 can execute any arbitrary CURD statement
db_window-1 > select * from account;
+----+-----------+-------+
| id | name      | money |
+----+-----------+-------+
|  1 | WeiyiGeek |  1000 |
|  2 | muzi      |  1000 |
|  3 | mariadb   |   900 |
+----+-----------+-------+
3 rows in set (0.00 sec)
copy

WeiyiGeek.SERIALIZABLE

Summarize: By efficiency, from high to low

read uncommitted > read committed > repeatable read > serializable

According to the degree of interception, from high to bottom

Serializable > Repeatable Read > Read Committed > Read Uncommitted

Database default isolation level:

  • The default isolation level of mySql is Repeatable Read
  • Oracle's default isolation level is read committed

0x01 database connection pool

What is connection pooling?

Connection pool refers to: creating a pool (container) actually opens up a space (collection) in memory, and places multiple connection objects in the pool to manage connection objects;

WeiyiGeek.

What does the connection pool do?

1. Faster response speed: The connection in the connection pool has been created at the beginning, and if you need to take it directly later, there is no need to create it. 2. Reuse of resources and avoid repeated creation of objects: After the connection objects are used, they can be returned to the pool for unified management.

What are the problems with custom connection pools?

  • 1. An additional addBack method is required to return the connection object
  • 2. It is necessary to set a singleton to prevent repeated instantiation of objects;
  • 3. Interface-oriented programming is not possible because we use the JDBC jar package provided by MySQL/Oracle, and the addBack method is not defined in the interface;

How to solve?

  • 1. With addBack as the entry point, you need to remember this method where you use this connection pool, and you can't program for the interface;
  • 3. Using the decoration in the design mode, the mode can directly use the rewritten close method. Calling the close method does not really close the database connection object but returns the connection object (just close the database result set);

Complementary Java Design Patterns (four very important) in order from easiest to hardest:

  • Singleton pattern (java foundation)
  • Engineering mode (java foundation)
  • Decoration mode (java foundation)
  • Dynamic proxy (java foundation)

Basic example:

// Call the connection pool using: /User/src/top/weiyigeek/Web/Test_Pool.java
public class Test_Pool {
  @Test
  public void testPool() throws SQLException {
    Connection conn = null;
    PreparedStatement ps =null;
    //Note that it is necessary to avoid repeated application of objects here, so you need to use a singleton;
    CustomDatasource ds = CustomDatasource.getInstance();
    try {
      //Get the link object of the resource pool
      conn = ds.getConnection();
      String sql = "SELECT * FROM user LIMIT 0,10";
      ps = conn.prepareStatement(sql);
      ResultSet rs = ps.executeQuery();
      while(rs.next()) {
        System.out.println("ID: " + rs.getInt("id") + " , Name = " + rs.getString("name")+", Log in time = " + rs.getDate("uptime"));
      }
      rs.close();
    } catch (Exception e) {
      // TODO: handle exception
      e.printStackTrace();
    } finally {
      try {
        //Close the PreparedStatementd object
        ps.close();
      } catch (Exception e2) {
        // TODO: handle exception
        e2.printStackTrace();
      }
      //return object
      //ds.addBack(conn); //This method is not used after decoration
      conn.close();
    }
  }
}
copy

Decoration mode class: /User/src/top/weiyigeek/pool/ConnectionWrap.java

/**
 * @Desc:Decorate design pattern database connection close return
 * @author WeiyiGeek
 * @CreatTime 2:48:41 PM
 */
public class ConnectionWrap implements Connection {
  //1. The construction method accesses the connection connection and the connection pool collection
  Connection connection = null;
  List <Connection> list ;
  public ConnectionWrap(Connection connection , List <Connection> list) {
    super();
    this.connection = connection;
    this.list = list;
  }
  
  //2. Parse the prepared SQL statement
  @Override
  public PreparedStatement prepareStatement(String sql) throws SQLException {
    return connection.prepareStatement(sql);
  }
  
  //3. Override the Close method
  @Override
  public void close() throws SQLException {
    // TODO Auto-generated method stub
    //connection.close(); //You can also call the original close
    System.out.println("Someone has come to return the connection object> Before returning, the number of available connections in the pool:"+list.size());
    list.add(connection);
    System.out.println("Someone has come to return the connection object> After the return, the number of available connections in the pool:"+list.size());
    }

    ......   
}
copy

Custom connection pool class: /User/src/top/weiyigeek/pool/CustomDatasource.java

import top.weiyigeek.Util.db;
public class CustomDatasource implements DataSource {
  // The method of database resource pool establishment
  //0. Create objects of this class and provide public access methods to the outside world
  private static CustomDatasource s = new CustomDatasource();
  public static CustomDatasource getInstance() {
    return s;
  }
  // 1. Create a collection of storage connection pool objects
  List <Connection> list = new ArrayList<Connection>();
  
   public CustomDatasource()  {
     //2. Establish a connection object and store it in the resource pool
     for(int i = 0; i < 10;i++) {
      Connection conn;
      try {
        conn = db.getConn();
        list.add(conn);
      } catch (SQLException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
     }
  }
  
  //3. The method for obtaining connections published by the connection pool 
  @Override
  public Connection getConnection() throws SQLException {
    // TODO Auto-generated method stub
    //4. To determine whether the resource pool is used up, add 3 connection objects to the resource pool (of course, the resource pool is limited)
     if (list.size() == 0 ) {
       try {
         for(int i = 0; i < 5; i++)
           list.add(db.getConn());
      } catch (SQLException e) {
        e.printStackTrace();
      }
     }
    //return and remove the first element
    Connection conn = list.remove(0); 
    //The decoration mode wraps the object when it is thrown out. (The key point is worth learning)
    Connection connection = new ConnectionWrap(conn, list);
    return connection;
  }

  
  //5. Return the connection object
  public void addBack(Connection conn) {
    list.add(conn);
  }

    .....
}
copy

Results of the:

WeiyiGeek. Connection Pool

Summarize:

  • 1. We generally do not write connection pools ourselves in actual development. We have to stand on the shoulders of giants, but we need to understand its operation principle;

0x02 Open source connection pool

1.OBCP

Description: DBCP (DataBase Connection Pool) database connection pool is a kind of java database connection pool, developed by Apache through the database connection pool, the program can automatically manage the release and disconnection of database connections; Official website download address: http://commons.apache.org/proper/commons-dbcp/download_dbcp.cgi Current 2020306 latest version:

Environment dependency: import the jar packages commons-dbcp.jar,commons-pool.jar after downloading;

WeiyiGeek.

actual case: 1. Do not use the configuration file method /User/src/top/weiyigeek/DBCP/OBCPDemo1.java

package top.weiyigeek.DBCP;
/**
 * 
 * @Desc: OBCP Apache Use of open source data connection management frameworks
 * @author WeiyiGeek
 * @CreatTime 12:45:22 PM
 */
public class OBCPDemo1 {
  @SuppressWarnings("resource")
  @Test
  public void testOBCP () {
    //1. Build the data source object
    BasicDataSource dataSource = new BasicDataSource();
    
    //2. Get the connection object
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    
    //3. Database configuration (manual)
    dataSource.setDriverClassName("org.mariadb.jdbc.Driver");
    //Main Protocol Subprotocol Database IP:Port Database
    dataSource.setUrl("jdbc:mariadb://127.0.0.1:3306/student");
    dataSource.setUsername("root");
    dataSource.setPassword("");
    
    try {
      conn = dataSource.getConnection();
      //4. Parse the SQL statement
      ps = conn.prepareStatement("SELECT * FROM user ORDER BY id DESC LIMIT 0,?");
      ps.setInt(1, 10);
      rs = ps.executeQuery();
      while(rs.next()) {
        System.out.println(rs.getInt("id") + " ---- " + rs.getString("name") +  " ---- " + rs.getInt("grade"));
      }
      rs.close();
    } catch (SQLException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } finally {
      //db.release(conn, ps, rs);
      //It is recommended to rewrite the close method to release resources
    }
  }
}
copy

2. Use the configuration file method. dbcp.properties:

#connection settings
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc
username=root
password=

#<!-- Initialize connection -->
initialSize=10

#Maximum number of connections
maxActive=50

#<!-- Maximum idle connections -->
maxIdle=20

#<!-- Minimum idle connection -->
minIdle=5

#<!-- Timeout wait time in milliseconds 6000ms/1000 equals 60 seconds -->
maxWait=60000


#The format of the connection property attribute attached when the JDBC driver establishes a connection must be as follows: [property name=property;] 
#Note: The "user" and "password" properties are passed explicitly, so there is no need to include them here.
connectionProperties=useUnicode=true;characterEncoding=utf8

#Specifies the auto-commit state of connections created by the connection pool.
defaultAutoCommit=true

#The driver default specifies the transaction level (TransactionIsolation) of the connection created by the connection pool.
#Available values ​​are one of the following: (See the javadoc for details.) NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
copy
/**
 * 
 * @Desc: OBCP Read the configuration file to configure the database connection
 * @author WeiyiGeek
 * @CreatTime 1:26:18 PM
 */
public class OBCPDemo2 {
  
  @Test
  public void testDemo2() throws Exception {
    //1. Use Properties to read the data in the configuration file
    Properties prop = new Properties();
    InputStream is = this.getClass().getClassLoader().getResourceAsStream("dbcp.properties");  //Read jdbc.properties in the project into the input stream
    prop.load(is);

    //2. Build the connection object
    BasicDataSourceFactory factory = new BasicDataSourceFactory();
    DataSource datasource = factory.createDataSource(prop);  //Introduce properties file
    
    //3. Get the connection object
    Connection conn = datasource.getConnection();
    PreparedStatement ps = conn.prepareStatement("SELECT * FROM user LIMIT 0,10");
    ResultSet rs = ps.executeQuery();
    
    //4. Print out the data read from the database
    while(rs.next()) {
      System.out.println(rs.getInt("id") + " ---- " + rs.getString("name") +  " ---- " + rs.getInt("grade"));
    }
    
    //5. Close the connection
    System.out.println("connection closed");
    rs.close();
    ps.close();
    conn.close();
  }
}
copy

Results of the:

WeiyiGeek.

2.C3P0 (used more)

Description: C3P0 is an open source JDBC connection pool, which implements data source and JNDI binding, and supports JDBC3 specification and JDBC2 standard extension. Currently, open source projects using it include Hibernate, Spring, etc.

download link: https://sourceforge.net/projects/c3p0/

After the download is completed, import it into the project (Build Path) as above. When we learn a new technology, it is best to look at some of his help documents;

Code example (1): Manual configuration

import top.weiyigeek.Util.db;
import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0Demo1 {
  @Test
  public void Demo1() throws PropertyVetoException,Exception {
    //1. Create DataSource
    ComboPooledDataSource cpds = new ComboPooledDataSource();
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    
    //2. Set database connection information
    cpds.setDriverClass("org.mariadb.jdbc.Driver");
    cpds.setJdbcUrl("jdbc:mariadb://127.0.0.1:3306/student");
    cpds.setUser("root");
    cpds.setPassword("");

    //3. Get the connection object
    conn = cpds.getConnection();
    ps = conn.prepareStatement("SELECT * FROM user LIMIT 0,?");
    ps.setInt(1, 20);

    //4. Execute the SQL query
    rs = ps.executeQuery();
    System.out.println("serial number - Name - grade");
    while (rs.next()) {
      System.out.println(rs.getInt("id") + " - " + rs.getString("name") + " - " + rs.getShort("grade"));
    }
    
    //5. Close the connection
    db.release(conn, ps, rs);
  }
}
copy

WeiyiGeek.

Code example (2): The configuration file is configured by c3p0-config.xml by default, bytecode is used to read the file;

<c3p0-config>
  <default-config>
    <property name="driverClass">org.mariadb.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mariadb://127.0.0.1:3306/student</property>
    <property name="user">root</property>
    <property name="password"></property>

    <property name="automaticTestTable">con_test</property>
    <property name="checkoutTimeout">30000</property>
    <property name="idleConnectionTestPeriod">30</property>
    <property name="initialPoolSize">10</property>
    <property name="maxIdleTime">30</property>
    <property name="maxPoolSize">100</property>
    <property name="minPoolSize">10</property>
    <property name="maxStatements">200</property>

    <user-overrides user="test-user">
      <property name="maxPoolSize">10</property>
      <property name="minPoolSize">1</property>
      <property name="maxStatements">0</property>
    </user-overrides>
  </default-config>
<!--Multiple databases can be configured-->
  <named-config name="mysql">
    <property name="driverClass">org.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/student</property>
    <property name="user">root</property>
    <property name="password"></property>
  </named-config>

<!--Multiple databases can be configured-->
  <named-config name="oracle">
    <property name="driverClass">oracle.jdbc.driver.OracleDriver</property>
    <property name="jdbcUrl">jdbc:oracle:thin:@192.168.10.25:1521:student</property>
    <property name="user">master</property>
    <property name="password"></property>
  </named-config>
</c3p0-config>
copy

Demo code:

package top.weiyigeek.C3P0;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.junit.Test;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import top.weiyigeek.Util.db;
/**
 * 
 * @Desc: C3P0 Read database connection and connection pool configuration through xml file configuration (actually similar to Demo1)
 * @author WeiyiGeek
 * @CreatTime 2:38:55 PM
 */
public class C3P0Demo2 {
  
  @Test
  public void demo2() throws SQLException {
    //1. Create a DataSource and read and set the database connection information (c3p0-config.xml)
    ComboPooledDataSource cpds = new ComboPooledDataSource();
    //ComboPooledDataSource cpds = new ComboPooledDataSource("oracle"); //You can connect to the specified database in the configuration file
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;

    //2. Get the connection object
    conn = cpds.getConnection();
    ps = conn.prepareStatement("SELECT count(*) FROM user");

    //3. Execute the SQL query
    rs = ps.executeQuery();
    System.out.println("serial number - Name - grade");
    while (rs.next()) {
      System.out.println("total number of students: "+rs.getInt(1) );
    }
    //4. Close the connection
    db.release(conn, ps, rs);
  }
}
copy

Results of the:

March 19, 2020 2:44:34 afternoon com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource getPoolManager
 information: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> con_test, breakAfterAcquireFailure -> false, checkoutTimeout -> 30000, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 1hgevwha81v0n140s2kcd6|33b37288, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> org.mariadb.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1hgevwha81v0n140s2kcd6|33b37288, idleConnectionTestPeriod -> 30, initialPoolSize -> 10, jdbcUrl -> jdbc:mariadb://127.0.0.1:3306/student, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 30, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 100, maxStatements -> 200, maxStatementsPerConnection -> 0, minPoolSize -> 10, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
serial number - Name - grade
 total number of students: 84
 Database connection closed and resources released
copy

Using C3P0 greatly simplifies the Tools class we wrote ourselves:

import com.mchange.v2.c3p0.ComboPooledDataSource;
/***
 * @Desc: The database connection tool class adopts the c3p0 method (simplifies our code)
 * @author WeiyiGeek
 * @CreatTime 1:28:57 PM
 */
public class db1 {
  static ComboPooledDataSource cpds = null;
  static {
    cpds = new ComboPooledDataSource();
  }
  
  /**
	 * Get link object in C3P0
	 * @return Connection
	 */
  public static Connection getConn() throws SQLException {
    return cpds.getConnection();
  }
  
  /**
	 * Fun:Close the database connection and release resources (note: the order of closing)
	 * @param conn
	 * @param st
	 * @param rs
	 */
  public static void release(Connection conn,Statement st, ResultSet rs) {
    closeRs(rs);
    closeSt(st);
    closeConn(conn);
    System.out.println("\n Database connection closed and resources released\n");
  }
  
  //Private static method - release query result set
  private static void closeRs(ResultSet rs) {
      try {
        if(rs != null)
          rs.close();
      } catch (SQLException e) {
        e.printStackTrace();
      } finally {
        rs = null;
      }
    }
    
  //Private static method - release the statement object
  private static void closeSt(Statement st) {
      try {
        if(st != null)
          st.close();
      } catch (SQLException e) {
        e.printStackTrace();
      } finally {
        st = null;
      }
    }
    
  //Private static method - closes database connection
  private static void closeConn(Connection conn) {
    try {
      if(conn != null)
        conn.close();
    } catch (SQLException e) {
      e.printStackTrace();
    } finally {
      conn = null;
    }
  }
}
copy
3.DBUtils (emphasis)

What is DBUtils? Answer: Commons DbUtils is an open source tool class library provided by the Apache organization that simply encapsulates JDBC. Using it can simplify the development of JDBC applications without affecting the performance of the program;

To put it simply: DBUtils just simplifies the CRUD code for us, creating a database connection is not within his scope of consideration, you only need to pass in the DataSource connection object to him;

Official website download address: http://commons.apache.org/dbutils/download_dbutils.cgi

Additional instructions:

1. Obtain an instance of the class through the bytecode of the class (implemented through reflection)

Account a = new Account();
Account al = Account.class.newInstance();
copy

2.ResultSetHandler interface implementation class

BeanHander - encapsulates a single queried data into an object (commonly used);

BeanListHander - encapsulates multiple queried data into a List collection with objects (commonly used);

Person res = qr.query("select * from person", new BeanHandler<Person>(Person.class));    
List<Person> lp = runner.query("SELECT * FROM person ", new BeanListHandler<Person>(Person.class));
copy

ArrayHandler - encapsulates a single queried data into an array

ArrayListHandler - encapsulates multiple queried data into a collection where the elements are arrays

MapHandler - encapsulates a single queried data into a Map

MapListHandler - encapsulates multiple queried data into a collection. The elements in it are Map

ColumnsListHandler (not commonly used)

KeyedHander (not commonly used) - Multiple records are encapsulated into a Map collection of Map collections. And the outer Map collection can be specified (specify one of the attribute names of the inner Map).

ScalarHander (not commonly used) - encapsulate a single value, such as select count (*), to find the number of pieces of content

Long count = (Long)runner.query("SELECT COUNT(*) FROM person",new ScalarHandler());
return count.intValue();
copy

WeiyiGeek.

Query queries the class where the result is stored: (1) The class that stores the query data

package top.weiyigeek.DBUtil;
public class Account {
  private int id;
  private String name;
  private float money;
  public int getId() {
    return id;
  }
  public void setId(int id) {
    this.id = id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public float getMoney() {
    return money;
  }
  public void setMoney(float money) {
    this.money = money;
  }
  
  @Override
  public String toString() {
    return "Account [serial number=" + id + ", Name=" + name + ", salary=" + money + "]";
  } 
}
copy

(2) Classes implemented by DBUtil

package top.weiyigeek.DBUtil;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import javax.sql.DataSource;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import com.mchange.v2.c3p0.ComboPooledDataSource;

/*
 * Function: DButil CURD Case realization 
 */
public class DBUtilDemo1 {
  //call implementation
  public static void main(String[] args) throws SQLException {
    ComboPooledDataSource cpds = new ComboPooledDataSource();
    //Example 1. Insertion test
    if(insert(cpds)>0) {
      System.out.println("Inserted successfully");
    }else {
      System.err.println("Insert failed");
    }
    
    //Example 2. Test deletion
    if(delete(cpds)>0) {
      System.out.println("successfully deleted");
    }else {
      System.err.println("failed to delete");
    }
    
    //Example 3. Test update
    if(update(cpds)>0) {
      System.out.println("update completed");
    }else {
      System.err.println("update failed");
    }
    
    
    //Example 4. Test anonymous implementation class to return query results
    Account account = queryone(cpds);
    System.out.println(account.toString());
    
    
    //Example 5. Return the result by implementing the interface of ResultSetHandler in DBuntil;
    List<Account> ls = querytwo(cpds);
    for(Account worker:ls) {
      System.out.println(worker.toString());
    }

  }
  
  //(1) Function: Test SQL insert statement such as method name
  public static int insert(DataSource cpds) throws SQLException {
    QueryRunner qr = new QueryRunner(cpds);
    int flag = qr.update("INSERT INTO account VALUES (null,?,?)","Zhang Wei",1024);
    return flag;
  }
  
  //(2) Function: Test SQL delete statement such as method name
  public static int delete(DataSource cpds) throws SQLException {
    QueryRunner qr = new QueryRunner(cpds);
    int flag = qr.update("DELETE FROM account where name = ?","Zhang Wei");
    return flag;
  }
  
  //(3) Function: Test SQL update statement such as method name
  public static int update(DataSource cpds) throws SQLException {
    QueryRunner qr = new QueryRunner(cpds);
    int flag = qr.update("UPDATE account SET money=money+100 where id = ? and name = ?",1,"WeiyiGeek");
    return flag;
  }
  
  //(4) Function: Test SQL query statement such as method name
  public static Account queryone(DataSource cpds) throws SQLException {
    QueryRunner qr = new QueryRunner(cpds);
    //Anonymous class implementation
    Account account = qr.query("SELECT * FROM account WHERE id = ?", new ResultSetHandler<Account>() {
      @Override
      public Account handle(ResultSet rs) throws SQLException {
        // TODO Auto-generated method stub
        Account account = new Account();
        while(rs.next()) {
          int id = rs.getInt("id");
          String name = rs.getString("name");
          float money = (float)rs.getInt("money");
          account.setId(id);
          account.setName(name);
          account.setMoney(money);
        }
        return account;
      }
    }, 1);
    
    return account;
  }
  
  //(5) In ResultSetHandler, resultset is converted into other objects for implementation.
  public static List<Account> querytwo(DataSource cpds) throws SQLException {
    QueryRunner qr = new QueryRunner(cpds);
    //- BeanHander - returns an object;
    //- BeanListHander - returns a collection with objects;
    List<Account> list= qr.query("SELECT * FROM account", new BeanListHandler<Account>(Account.class));
    return list;
  }
}
copy

WeiyiGeek.

Tags: Database SQL Java Android JDBC

Posted by hellouthere on Thu, 29 Sep 2022 11:39:49 +0530