java之數據庫鏈接池-dbcp&c3p0&dbutils

介紹

由於數據庫鏈接對象的建立比較消耗性能,因此能夠在應用程序啓動時就在內存中開闢一片空間(集合)存放多個數據庫鏈接對象,後面須要鏈接時直接從該空間中取而不用新建立;使用完畢後歸還鏈接(將鏈接從新放回空間),確保鏈接對象能重複使用。java

知識儲備

裝飾者模式

package com.zze.test;

public interface IWaiter {
    void service();
}
IWaiter.java
package com.zze.test;

public class Waiter implements IWaiter{
    public void service(){
        System.out.println("正在服務");
    }
}
Waiter.java
package com.zze.test;

public class WaiterWrapper implements IWaiter {
    public WaiterWrapper(Waiter waiter) {
        this.waiter = waiter;
    }

    private Waiter waiter;

    @Override
    public void service() {
        System.out.println("服務以前");
        waiter.service();
        System.out.println("服務以後");
    }
}
WaiterWrapper.java
@Test
public void wrapperTest() {
    IWaiter waiter = new WaiterWrapper(new Waiter());
    waiter.service();
    /*
    服務以前
    正在服務
    服務以後
     */
}
test

本身實現一個鏈接池

一般一個鏈接使用完畢後咱們要調用它的 close 方法關閉它,而這裏咱們是要讓它歸還到鏈接池,這裏咱們能夠經過裝飾者模式修改它的 close 方法實現:mysql

package com.zze.util;

import java.sql.*;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;

public class ConnectionWrap implements Connection {
    private Connection connection = null;
    private List<Connection> connectionList;

    /**
     * 構造函數
     * @param connection 要裝飾的鏈接
     * @param connectionList 鏈接池儲存鏈接的集合
     */
    public ConnectionWrap(Connection connection, List<Connection> connectionList) {
        super();
        this.connection = connection;
        this.connectionList = connectionList;
    }

    @Override
    public void close() throws SQLException {
        connectionList.add(connection);
    }

    @Override
    public Statement createStatement() throws SQLException {
        return connection.createStatement();
    }

    @Override
    public PreparedStatement prepareStatement(String sql) throws SQLException {
        return connection.prepareStatement(sql);
    }

    @Override
    public CallableStatement prepareCall(String sql) throws SQLException {
        return connection.prepareCall(sql);
    }

    @Override
    public String nativeSQL(String sql) throws SQLException {
        return connection.nativeSQL(sql);
    }

    @Override
    public void setAutoCommit(boolean autoCommit) throws SQLException {
        connection.setAutoCommit(autoCommit);
    }

    @Override
    public boolean getAutoCommit() throws SQLException {
        return connection.getAutoCommit();
    }

    @Override
    public void commit() throws SQLException {
        connection.commit();
    }

    @Override
    public void rollback() throws SQLException {
        connection.rollback();
    }


    @Override
    public boolean isClosed() throws SQLException {
        return connection.isClosed();
    }

    @Override
    public DatabaseMetaData getMetaData() throws SQLException {
        return connection.getMetaData();
    }

    @Override
    public void setReadOnly(boolean readOnly) throws SQLException {
        connection.setReadOnly(readOnly);
    }

    @Override
    public boolean isReadOnly() throws SQLException {
        return connection.isReadOnly();
    }

    @Override
    public void setCatalog(String catalog) throws SQLException {
        connection.setCatalog(catalog);
    }

    @Override
    public String getCatalog() throws SQLException {
        return connection.getCatalog();
    }

    @Override
    public void setTransactionIsolation(int level) throws SQLException {
        connection.setTransactionIsolation(level);
    }

    @Override
    public int getTransactionIsolation() throws SQLException {
        return connection.getTransactionIsolation();
    }

    @Override
    public SQLWarning getWarnings() throws SQLException {
        return connection.getWarnings();
    }

    @Override
    public void clearWarnings() throws SQLException {
        connection.clearWarnings();
    }

    @Override
    public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
        return connection.createStatement(resultSetType, resultSetConcurrency);
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        return connection.prepareStatement(sql, resultSetType, resultSetConcurrency);
    }

    @Override
    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        return connection.prepareCall(sql,resultSetType,resultSetConcurrency);
    }

    @Override
    public Map<String, Class<?>> getTypeMap() throws SQLException {
        return connection.getTypeMap();
    }

    @Override
    public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
        connection.setTypeMap(map);
    }

    @Override
    public void setHoldability(int holdability) throws SQLException {
        connection.setHoldability(holdability);
    }

    @Override
    public int getHoldability() throws SQLException {
        return connection.getHoldability();
    }

    @Override
    public Savepoint setSavepoint() throws SQLException {
        return connection.setSavepoint();
    }

    @Override
    public Savepoint setSavepoint(String name) throws SQLException {
        return connection.setSavepoint(name);
    }

    @Override
    public void rollback(Savepoint savepoint) throws SQLException {
        connection.rollback();
    }

    @Override
    public void releaseSavepoint(Savepoint savepoint) throws SQLException {
        connection.releaseSavepoint(savepoint);
    }

    @Override
    public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return connection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
    }

    @Override
    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return connection.prepareCall(sql,resultSetType,resultSetConcurrency,resultSetHoldability);
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
        return connection.prepareStatement(sql, autoGeneratedKeys);
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
        return connection.prepareStatement(sql, columnIndexes);
    }

    @Override
    public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
        return connection.prepareStatement(sql, columnNames);
    }

    @Override
    public Clob createClob() throws SQLException {
        return connection.createClob();
    }

    @Override
    public Blob createBlob() throws SQLException {
        return connection.createBlob();
    }

    @Override
    public NClob createNClob() throws SQLException {
        return connection.createNClob();
    }

    @Override
    public SQLXML createSQLXML() throws SQLException {
        return connection.createSQLXML();
    }

    @Override
    public boolean isValid(int timeout) throws SQLException {
        return connection.isValid(timeout);
    }

    @Override
    public void setClientInfo(String name, String value) throws SQLClientInfoException {
        connection.setClientInfo(name,value);
    }

    @Override
    public void setClientInfo(Properties properties) throws SQLClientInfoException {
        connection.setClientInfo(properties);
    }

    @Override
    public String getClientInfo(String name) throws SQLException {
        return connection.getClientInfo(name);
    }

    @Override
    public Properties getClientInfo() throws SQLException {
        return connection.getClientInfo();
    }

    @Override
    public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
        return connection.createArrayOf(typeName,elements);
    }

    @Override
    public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
        return connection.createStruct(typeName,attributes);
    }

    @Override
    public void setSchema(String schema) throws SQLException {
        connection.setSchema(schema);
    }

    @Override
    public String getSchema() throws SQLException {
        return connection.getSchema();
    }

    @Override
    public void abort(Executor executor) throws SQLException {
        connection.abort(executor);
    }

    @Override
    public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
        connection.setNetworkTimeout(executor,milliseconds);
    }

    @Override
    public int getNetworkTimeout() throws SQLException {
        return connection.getNetworkTimeout();
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return connection.unwrap(iface);
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return connection.isWrapperFor(iface);
    }
}
ConnectionWrap.java

對於鏈接池,java 已經給咱們提供了接口 javax.sql.DataSource ,咱們要作的就是實現它:sql

package com.zze.util;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

public class MyDataSource implements DataSource {
    // 定義一個集合用來存放鏈接
    private List<Connection> connections = new ArrayList<>();

    public MyDataSource() {
        // 初始化 10 個鏈接
        for (int i = 0; i < 10; i++) {
            connections.add(JDBCUtil.getConnection());
        }
    }

    /**
     * 獲取鏈接
     *
     * @return 從鏈接池取出的鏈接
     */
    @Override
    public Connection getConnection() throws SQLException {
        // 取鏈接但集合沒有鏈接時,新添加 5 個鏈接
        if (connections.size() == 0) {
            for (int i = 0; i < 5; i++) {
                connections.add(JDBCUtil.getConnection());
            }
        }
        Connection connection = connections.remove(0);
        // 返回咱們定義的鏈接包裝類
        ConnectionWrap connectionWrap = new ConnectionWrap(connection, connections);
        return connectionWrap;
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return null;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }
}
MyDataSource.java

此時就能夠經過咱們本身編寫的鏈接池獲取鏈接了:數據庫

@Test
public void getConnTest() {
    try {
        MyDataSource dataSource = new MyDataSource();
        Connection connection = dataSource.getConnection();
        System.out.println(connection); // com.zze.util.ConnectionWrap@6c629d6e
    } catch (SQLException e) {
        e.printStackTrace();
    }
}
test

點擊下載完整示例apache

第三方鏈接池使用

DBCP

DBCP (DataBase Connection Pool) 是 java 數據庫鏈接池的一種,由 Apache 開發,經過它可讓程序自動管理數據庫鏈接的釋放和斷開。緩存

依賴 jar 包下載多線程

# 鏈接設置
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=root

# 初始化鏈接
initialSize=10

# 最大鏈接數量
maxActive=50

# 最大空閒鏈接
maxIdle=20

# 最小空閒鏈接
minIdle=5

# 超時等待時間以毫秒爲單位 6000毫秒/1000等於60秒
maxWait=60000

# JDBC驅動創建鏈接時附帶的鏈接屬性屬性的格式必須爲這樣:[屬性名=property;]
# 注意:"user" 與 "password" 兩個屬性會被明確地傳遞,所以這裏不須要包含他們。
connectionProperties=useUnicode=true;characterEncoding=gbk;serverTimezone=GMT

# 指定由鏈接池所建立的鏈接的自動提交(auto-commit)狀態。
defaultAutoCommit=true

# driver default 指定由鏈接池所建立的鏈接的事務級別(TransactionIsolation)。
# 可用值爲下列之一:(詳情可見javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
src:dbcpconfig.properties
package com.zze.test;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.junit.Test;

import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

public class DemoTest {
    /**
     * 經過代碼配置建立鏈接池
     */
    @Test
    public void dbcpTest1() {
        try {
            // 構建數據源對象
            BasicDataSource dataSource = new BasicDataSource();
            // 數據庫驅動
            dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
            // 用戶名
            dataSource.setUsername("root");
            // 密碼
            dataSource.setPassword("root");
            // 數據庫連接
            dataSource.setUrl("jdbc:mysql://localhost:3306/test?serverTimezone=GMT");
            // 獲得鏈接對象
            Connection connection = dataSource.getConnection();
            System.out.println(connection.getClass());
            // class org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 經過配置文件建立鏈接池
     */
    @Test
    public void dbcpTest2() {
        try {
            Properties properties = new Properties();
            properties.load(new FileInputStream("src//dbcpconfig.properties"));
            DataSource dataSource = BasicDataSourceFactory.createDataSource(properties);
            Connection connection = dataSource.getConnection();
            System.out.println(connection.getClass());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

C3P0

C3P0 也是一個開源的 JDBC 鏈接池,它實現了數據源和 JNDI 綁定,支持 JDBC3 規範和 JDBC2 的標準擴展。目前使用它的開源項目有 Hibernate、Sping 等。app

依賴 jar 包下載異步

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <!--默認配置-->
    <default-config>
        <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>
    </default-config>

    <!--配置 mysql 鏈接池-->
    <named-config name="mysql">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/test?useUnicode=true&amp;serverTimezone=GMT&amp;characterEncoding=utf-8</property>
        <property name="user">root</property>
        <property name="password">root</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>
    </named-config>
</c3p0-config>
src:c3p0-config.xml
還可更精細的配置:
    <!--acquireIncrement:連接用完了自動增量3個。 -->
    <property name="acquireIncrement">3</property>

    <!--acquireRetryAttempts:連接失敗後從新試30次。-->
    <property name="acquireRetryAttempts">30</property>
 
    <!--acquireRetryDelay;兩次鏈接中間隔1000毫秒。 -->
    <property name="acquireRetryDelay">1000</property>
 
    <!--autoCommitOnClose:鏈接關閉時默認將全部未提交的操做回滾。 -->
    <property name="autoCommitOnClose">false</property>
 
    <!--automaticTestTable:c3p0測試表,沒什麼用。-->
    <property name="automaticTestTable">Test</property>
 
    <!--breakAfterAcquireFailure:出錯時不把正在提交的數據拋棄。-->
    <property name="breakAfterAcquireFailure">false</property>
 
    <!--checkoutTimeout:100毫秒後若是sql數據沒有執行完將會報錯,若是設置成0,那麼將會無限的等待。 --> 
    <property name="checkoutTimeout">100</property>
 
    <!--connectionTesterClassName:經過實現ConnectionTester或QueryConnectionTester的類來測試鏈接。類名需制定全路徑。Default: com.mchange.v2.c3p0.impl.DefaultConnectionTester-->
    <property name="connectionTesterClassName"></property>
 
    <!--factoryClassLocation:指定c3p0 libraries的路徑,若是(一般都是這樣)在本地便可得到那麼無需設置,默認null便可。-->
    <property name="factoryClassLocation">null</property>
 
    <!--forceIgnoreUnresolvedTransactions:做者強烈建議不使用的一個屬性。--> 
    <property name="forceIgnoreUnresolvedTransactions">false</property>
 
    <!--idleConnectionTestPeriod:每60秒檢查全部鏈接池中的空閒鏈接。--> 
    <property name="idleConnectionTestPeriod">60</property>
 
    <!--initialPoolSize:初始化時獲取三個鏈接,取值應在minPoolSize與maxPoolSize之間。 --> 
    <property name="initialPoolSize">3</property>
 
    <!--maxIdleTime:最大空閒時間,60秒內未使用則鏈接被丟棄。若爲0則永不丟棄。-->
    <property name="maxIdleTime">60</property>
 
    <!--maxPoolSize:鏈接池中保留的最大鏈接數。 -->
    <property name="maxPoolSize">15</property>
 
    <!--maxStatements:最大連接數。-->
    <property name="maxStatements">100</property>
 
    <!--maxStatementsPerConnection:定義了鏈接池內單個鏈接所擁有的最大緩存statements數。Default: 0  -->
    <property name="maxStatementsPerConnection"></property>
 
    <!--numHelperThreads:異步操做,提高性能經過多線程實現多個操做同時被執行。Default: 3--> 
    <property name="numHelperThreads">3</property>
 
    <!--overrideDefaultUser:當用戶調用getConnection()時使root用戶成爲去獲取鏈接的用戶。主要用於鏈接池鏈接非c3p0的數據源時。Default: null--> 
    <property name="overrideDefaultUser">root</property>
 
    <!--overrideDefaultPassword:與overrideDefaultUser參數對應使用的一個參數。Default: null-->
    <property name="overrideDefaultPassword">password</property>
 
    <!--password:密碼。Default: null--> 
    <property name="password"></property>
 
    <!--preferredTestQuery:定義全部鏈接測試都執行的測試語句。在使用鏈接測試的狀況下這個一顯著提升測試速度。注意: 測試的表必須在初始數據源的時候就存在。Default: null-->
    <property name="preferredTestQuery">select id from test where id=1</property>
 
    <!--propertyCycle:用戶修改系統配置參數執行前最多等待300秒。Default: 300 --> 
    <property name="propertyCycle">300</property>
 
    <!--testConnectionOnCheckout:因性能消耗大請只在須要的時候使用它。Default: false -->
    <property name="testConnectionOnCheckout">false</property>
 
    <!--testConnectionOnCheckin:若是設爲true那麼在取得鏈接的同時將校驗鏈接的有效性。Default: false -->
    <property name="testConnectionOnCheckin">true</property>
 
    <!--user:用戶名。Default: null-->
    <property name="user">root</property>
 
    <!--usesTraditionalReflectiveProxies:動態反射代理。Default: false-->
    <property name="usesTraditionalReflectiveProxies">false</property>
src:c3p0-config.xml
package com.zze.test;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.Test;

import javax.sql.DataSource;
import java.sql.Connection;

public class DemoTest {
    /**
     * 代碼配置建立鏈接池
     */
    @Test
    public void c3p0Test1() {
        try {
            ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
            comboPooledDataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
            comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test?serverTimezone=GMT");
            comboPooledDataSource.setUser("root");
            comboPooledDataSource.setPassword("root");
            Connection connection = comboPooledDataSource.getConnection();
            System.out.println(connection);
            // com.mchange.v2.c3p0.impl.NewProxyConnection@7a0ac6e3
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 配置文件建立鏈接池
     */
    @Test
    public void c3p0Test2() {
        try {
            //參數對應使用哪一個config,若是不寫,表示使用默認的config,即default-config裏的配置,不然使用參數指定的named-config裏的配置。
            DataSource ds = new ComboPooledDataSource("mysql");
            Connection connection = ds.getConnection();
            System.out.println(connection);
            // com.mchange.v2.c3p0.impl.NewProxyConnection@3796751b
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
注意:配置文件名固定爲  c3p0-config.xml 且需放在 src 根目錄。

抽取工具類

package com.zze.util;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

public class JDBCUtil {

    private static final ComboPooledDataSource DATA_SOURCE = new ComboPooledDataSource("mysql");
    private static final ThreadLocal<Connection> t = new ThreadLocal<>();

    public static Connection getConnection() {
        Connection conn = null;
        try {
            conn = t.get();
            if (conn == null) {
                conn = DATA_SOURCE.getConnection();
                t.set(conn);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return conn;
    }

    public static void beginTransaction() throws SQLException {
        Connection conn = getConnection();
        conn.setAutoCommit(false);
    }

    public static void commitTransaction() throws SQLException {
        Connection conn = getConnection();
        conn.commit();
    }

    public static DataSource getDataSource() {
        return DATA_SOURCE;
    }
}

DBUtils

使用

要使用 dbutils 須要提供一個鏈接池,這裏我使用 c3p0 。ide

依賴 jar 包下載

package com.zze.bean;

public class User {
    public User() {
    }
    public User(Integer id, String username, String password) {
        this.id = id;
        this.username = username;
        this.password = password;
    }

    private Integer id;
    private String password;
    private String username;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public String toString() {
        return String.format("id=%d,username=%s", this.id, this.username);
    }
}
com.zze.bean.User
package com.zze.test;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.zze.bean.User;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.junit.Test;

import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class DemoTest {
    /**
     * 查詢全部
     */
    @Test
    public void listAllTest() {
        try {
            DataSource ds = new ComboPooledDataSource("mysql");
            QueryRunner qr = new QueryRunner(ds);
            List<User> userList = qr.query("select * from user", new ResultSetHandler<List<User>>() {
                @Override
                public List<User> handle(ResultSet resultSet) throws SQLException {
                    List<User> users = new ArrayList<>();
                    while (resultSet.next()) {
                        int id = resultSet.getInt("id");
                        String username = resultSet.getString("username");
                        String password = resultSet.getString("password");
                        users.add(new User(id, username, password));
                    }
                    return users;
                }
            });
            System.out.println(userList);
            // [id=1,username=張三, id=2,username=李四]
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 根據 Id 查詢
     */
    @Test
    public void getByIdTest() {
        try {
            Integer id = 1;
            DataSource ds = new ComboPooledDataSource("mysql");
            QueryRunner qr = new QueryRunner(ds);
            User user = qr.query("select * from user where id=?", new ResultSetHandler<User>() {
                @Override
                public User handle(ResultSet resultSet) throws SQLException {
                    if (resultSet.next()) {
                        int id = resultSet.getInt("id");
                        String username = resultSet.getString("username");
                        String password = resultSet.getString("password");
                        return new User(id, username, password);
                    }
                    return null;
                }
            }, id);
            System.out.println(user);
            // id=1,username=張三
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 新增
     */
    @Test
    public void testAdd() {
        Integer id = 1;
        DataSource ds = new ComboPooledDataSource("mysql");
        QueryRunner qr = new QueryRunner(ds);
        try {
            int count = qr.update("insert into user (username,password) values(?,?)", "王五", "1226");
            System.out.println(count > 0 ? "success" : "failed");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 更新
     */
    @Test
    public void testUpdate() {
        DataSource ds = new ComboPooledDataSource("mysql");
        QueryRunner qr = new QueryRunner(ds);
        try {
            int count = qr.update("update user set username=? where id=?", "趙六", 3);
            System.out.println(count > 0 ? "success" : "failed");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 刪除
     */
    @Test
    public void testDelete() {
        DataSource ds = new ComboPooledDataSource("mysql");
        QueryRunner qr = new QueryRunner(ds);
        try {
            int count = qr.update("delete from user where id=?", 3);
            System.out.println(count > 0 ? "success" : "failed");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

本身實現一個QueryRunner

package com.zze.util;

import java.sql.ResultSet;

public interface ResultHandler<T> {
    T handle(ResultSet resultSet);
}
com.zze.util.ResultHandler
package com.zze.util;

import javax.sql.DataSource;
import javax.xml.crypto.Data;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class MyQueryRunner {
    public MyQueryRunner(DataSource dataSource){
        this.dataSource = dataSource;
    }
    private DataSource dataSource;

    public int update(String sql, Object... args) {
        try {
            Connection conn = dataSource.getConnection();
            PreparedStatement preparedStatement = conn.prepareStatement(sql);
            int parameterCount = preparedStatement.getParameterMetaData().getParameterCount();
            for (int i = 0; i < parameterCount; i++) {
                preparedStatement.setObject(i + 1, args[i]);
            }
            return preparedStatement.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    public <T> T query(String sql, ResultHandler<T> handler, Object... args) {
        try {
            Connection conn = dataSource.getConnection();
            PreparedStatement preparedStatement = conn.prepareStatement(sql);
            int parameterCount = preparedStatement.getParameterMetaData().getParameterCount();
            for (int i = 0; i < parameterCount; i++) {
                preparedStatement.setObject(i + 1, args[i]);
            }
            ResultSet resultSet = preparedStatement.executeQuery();
            T result = handler.handle(resultSet);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
com.zze.util.MyQueryRunner
package com.zze.test;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.zze.bean.User;
import com.zze.util.MyQueryRunner;
import com.zze.util.ResultHandler;
import org.junit.Test;

import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * 自定義的 DBUtils 測試
 */
public class MyDBUtilsTest {
    /**
     * 查詢全部
     */
    @Test
    public void listAllTest() {
        DataSource ds = new ComboPooledDataSource("mysql");
        MyQueryRunner myQueryRunner = new MyQueryRunner(ds);
        List<User> userList = myQueryRunner.query("select * from user", new ResultHandler<List<User>>() {
            @Override
            public List<User> handle(ResultSet resultSet) {
                try {
                    List<User> users = new ArrayList<>();
                    while (resultSet.next()) {
                        int id = resultSet.getInt("id");
                        String username = resultSet.getString("username");
                        String password = resultSet.getString("password");
                        users.add(new User(id, username, password));
                    }
                    return users;
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
        System.out.println(userList);
    }

    @Test
    public void getByIdTest() {
        DataSource ds = new ComboPooledDataSource("mysql");
        MyQueryRunner myQueryRunner = new MyQueryRunner(ds);
        int id = 1;
        User user = myQueryRunner.query("select * from user where id=?", new ResultHandler<User>() {
            @Override
            public User handle(ResultSet resultSet) {
                try {
                    if (resultSet.next()) {
                        int id = resultSet.getInt("id");
                        String username = resultSet.getString("username");
                        String password = resultSet.getString("password");
                        return new User(id, username, password);
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                return null;
            }
        }, id);
        System.out.println(user);
    }

    /**
     * 新增
     */
    @Test
    public void testAdd() {
        Integer id = 1;
        DataSource ds = new ComboPooledDataSource("mysql");
        MyQueryRunner qr = new MyQueryRunner(ds);
        try {
            int count = qr.update("insert into user (username,password) values(?,?)", "王五", "1226");
            System.out.println(count > 0 ? "success" : "failed");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 更新
     */
    @Test
    public void testUpdate() {
        DataSource ds = new ComboPooledDataSource("mysql");
        MyQueryRunner qr = new MyQueryRunner(ds);
        try {
            int count = qr.update("update user set username=? where id=?", "趙六", 7);
            System.out.println(count > 0 ? "success" : "failed");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 刪除
     */
    @Test
    public void testDelete() {
        DataSource ds = new ComboPooledDataSource("mysql");
        MyQueryRunner qr = new MyQueryRunner(ds);
        try {
            int count = qr.update("delete from user where id=?", 6);
            System.out.println(count > 0 ? "success" : "failed");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

DBUtils提供的結果集處理器

@Test
public void beanHandlerTest() {
    try {
        Integer id = 1;
        DataSource ds = new ComboPooledDataSource("mysql");
        QueryRunner qr = new QueryRunner(ds);
        User user = qr.query("select * from user where id=?", new BeanHandler<>(User.class), id);
        System.out.println(user);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Test
public void beanListHandlerTest() {
    try {
        DataSource ds = new ComboPooledDataSource("mysql");
        QueryRunner qr = new QueryRunner(ds);
        List<User> userList = qr.query("select * from user", new BeanListHandler<>(User.class));
        System.out.println(userList);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Test
public void mapHandlerTest() {
    try {
        DataSource ds = new ComboPooledDataSource("mysql");
        QueryRunner qr = new QueryRunner(ds);
        Map<String, Object> user = qr.query("select * from user where id=?", new MapHandler(), 2);
        System.out.println(user); // {password=1226, id=2, username=李四}
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Test
public void mapListHandlerTest() {
    try {
        DataSource ds = new ComboPooledDataSource("mysql");
        QueryRunner qr = new QueryRunner(ds);
        List<Map<String, Object>> users = qr.query("select * from user", new MapListHandler());
        System.out.println(users); // [{password=1226, id=1, username=張三}, {password=1226, id=2, username=李四}]
    } catch (Exception e) {
        e.printStackTrace();
    }
}

練習

這個練習算是對以前知識的總結,一個包含 CRUD、模糊查詢、分頁功能的簡易版學生管理系統,點擊下載

相關文章
相關標籤/搜索