1. 程式人生 > >jdbc連線進階_基礎_執行緒池_事務

jdbc連線進階_基礎_執行緒池_事務

目錄

  • 00 連線之基
  • 01 提取工具
  • 02 精簡冗餘程式碼·dbutils
  • 03 執行緒池·c3p0
  • 04 事務處理·service層·c3p0&ThreadLocal
  • 05 事務處理·filter層·c3p0&ThreadLocal&Filter

00 連線之基

匯入mysql-connector-java-5.1.12-bin.jar包

package d0608;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 檔案:Test.java
 * 描述:資料連線
 * 換行符:Unix
 * 字元格式:UTF-8
 */
public class Test {

    public static void main(String[] args) throws SQLException, ClassNotFoundException {

        String driver = "com.mysql.jdbc.Driver";
        String url = "jdbc:mysql://localhost:3306/test";
        String user = "root";
        String password = "root";

        Class.forName(driver);
        Connection conn = DriverManager.getConnection(url, user, password);
        PreparedStatement pst = conn.prepareStatement("select * from tree");
        ResultSet rs = pst.executeQuery();
        while(rs.next()) {
            System.out.println(rs.getString(3));
        }
        rs.close();
        pst.close();
        conn.close();   
    }

}

01 提取工具

匯入mysql-connector-java-5.1.12-bin.jar包

編寫配置檔案jdbc.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
user=root
password=root

編寫工具類JdbcUtil.java

package d0608;

import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

/**
 * 檔案:JdbcUtil.java
 * 描述:編寫工具類
 * 換行符:Unix
 * 字元格式:UTF-8
 */

public class JdbcUtil {

    private static String driver;
    private static String url;
    private static String user;
    private static String password;

    static {

        Properties prop = new Properties();
        try {
            prop.load(JdbcUtil.class.getClassLoader().getResourceAsStream("jdbc.properties"));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        driver = prop.getProperty("driver");
        url = prop.getProperty("url");
        user = prop.getProperty("user");
        password = prop.getProperty("password");

        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    public static Connection getConnection() throws SQLException {
        Connection conn = DriverManager.getConnection(url, user, password);
        return conn;
    }

    public static PreparedStatement getPrepaedStatement(Connection conn, String sql) throws SQLException {
        PreparedStatement pst = conn.prepareStatement(sql);
        return pst; 
    }

    public static void close(ResultSet rs, PreparedStatement pst, Connection conn) throws SQLException {
        if (null != rs) {
            rs.close();
        }
        if (null != pst) {
            pst.close();
        }
        if (null != conn) {
            conn.close();
        }
    }

}

測試類Test.java

package d0608;
/**
 * 工具類使用說明 
 * 換行符:Unix
 * 字元格式:UTF-8
 */
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class Test {

    public static void main(String[] args) throws SQLException {
        Connection conn = JdbcUtil.getConnection();
        PreparedStatement pst = JdbcUtil.getPrepaedStatement(conn, "select * from tree");
        ResultSet rs = pst.executeQuery();
        while(rs.next()) {
            System.out.println(rs.getString(3));
        }
        JdbcUtil.close(rs, pst, conn);
    }

}

02 精簡冗餘程式碼·dbutils

導相應的包

mysql-connector-java-5.1.12-bin.jar
commons-logging-1.1.3.jar
commons-dbutils-1.6.jar

編寫配置檔案jdbc.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
user=root
password=root

工具類編寫:

package d0608;

import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

import org.apache.commons.dbutils.QueryRunner;

/**
 * 工具類使用說明 
 * 換行符:Unix
 * 字元格式:UTF-8
 */

public class JdbcUtil {

    private static String driver;
    private static String url;
    private static String user;
    private static String password;

    static {

        Properties prop = new Properties();
        try {
            prop.load(JdbcUtil.class.getClassLoader().getResourceAsStream("jdbc.properties"));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        driver = prop.getProperty("driver");
        url = prop.getProperty("url");
        user = prop.getProperty("user");
        password = prop.getProperty("password");

        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, user, password);
    }

    public static QueryRunner getQueryRunner() {
        return new QueryRunner();
    }

}

資料庫對應pojo類:

package d0608;

public class Tree {

    private Integer id;
    private Integer pId;
    private String name;
    public Tree() {
        super();
        // TODO Auto-generated constructor stub
    }
    public Tree(Integer id, Integer pId, String name) {
        super();
        this.id = id;
        this.pId = pId;
        this.name = name;
    }
    @Override
    public String toString() {
        return "Tree [id=" + id + ", pId=" + pId + ", name=" + name + "]";
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public Integer getpId() {
        return pId;
    }
    public void setpId(Integer pId) {
        this.pId = pId;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

}

測試使用類:

package d0608;

import java.sql.SQLException;
import java.util.List;
import org.apache.commons.dbutils.handlers.ArrayHandler;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

/**
 * 測試獲取資料
 * 換行符:Unix
 * 字元格式:UTF-8
 */
public class Test {

    public static void main(String[] args) throws SQLException, ClassNotFoundException {

        /**
         * QueryRunner的一些用法
         * ArrayHandler:把結果集中的第一行資料轉成物件陣列。
         * ArrayListHandler:把結果集中的每一行資料都轉成一個數組,再存放到List中。   
         * BeanHandler:將結果集中的第一行資料封裝到一個對應的JavaBean例項中。
         * BeanListHandler:將結果集中的每一行資料都封裝到一個對應的JavaBean例項中,存放到List裡。
         * ColumnListHandler:將結果集中某一列的資料存放到List中。
         * KeyedHandler(name):將結果集中的每一行資料都封裝到一個Map裡,再把這些map再存到一個map裡,其key為指定的key。
         * MapHandler:將結果集中的第一行資料封裝到一個Map裡,key是列名,value就是對應的值。
         * MapListHandler:將結果集中的每一行資料都封裝到一個Map裡,然後再存放到List
         */

        // 返回集合
        List<Tree> list = JdbcUtil.getQueryRunner().query(JdbcUtil.getConnection(),"select * from tree", new BeanListHandler<>(Tree.class));

        for (Tree tree : list) {
            System.out.println(tree.getName());
        }

        // 返回單個物件
        Tree tree = JdbcUtil.getQueryRunner().query(JdbcUtil.getConnection(), "select * from tree where id = 1", new BeanHandler<>(Tree.class));
        System.out.println(tree.getName());

        // 返回基本的資料型別
        Object[] id = JdbcUtil.getQueryRunner().query(JdbcUtil.getConnection(), "select id from tree where id = 1", new ArrayHandler());
        System.out.println((Integer)id[0]);

        // 帶引數的修改,返回受影響的行數
        int update = JdbcUtil.getQueryRunner().update(JdbcUtil.getConnection(), "update tree set name=? where id=?", "test", 100);
        System.out.println(update);

    }

}

03 執行緒池·c3p0

如果自己寫模擬寫一下執行緒池的話,事先生成多個connection,儲存到佇列裡,然後出隊入隊,就基本是執行緒池複用資源的思想了,這裡直接使用c3p0

匯入相關的包檔案:

c3p0-0.9.1.2.jar
commons-dbutils-1.6.jar
commons-logging-1.1.3.jar
mysql-connector-java-5.1.12-bin.jar

配置檔案c3p0-config.xml:

<c3p0-config>
    <default-config>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/test
        </property>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="user">root</property>
        <property name="password">root</property>
        <property name="initialPoolSize">3</property>
        <property name="maxPoolSize">6</property>
        <property name="maxIdleTime">1000</property>
    </default-config>

    <named-config name="oracle_config">
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc_demo</property>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="user">scot</property>
        <property name="password">tiger</property>
        <property name="initialPoolSize">3</property>
        <property name="maxPoolSize">6</property>
        <property name="maxIdleTime">1000</property>
    </named-config>

</c3p0-config>

編寫工具類:

package d0608;

import org.apache.commons.dbutils.QueryRunner;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JdbcUtil {

    public static ComboPooledDataSource dataSource = new ComboPooledDataSource();

    public static QueryRunner getQueryRunner() {
        return new QueryRunner(dataSource);
    }

}

測試類:

package d0608;

import java.sql.SQLException;

import org.apache.commons.dbutils.handlers.ArrayHandler;

/**
 * 測試獲取資料
 * 換行符:Unix
 * 字元格式:UTF-8
 */
public class Test {

    public static void main(String[] args) throws SQLException, ClassNotFoundException {

        Object[] query = JdbcUtil.getQueryRunner().query("select count(*) from tree", new ArrayHandler());
        System.out.println(query[0]);

    }

}

04 事務處理·service層·c3p0&ThreadLocal

在開發中,對資料庫的多個表或者對一個表中的多條資料執行更新操作時要保證對多個更新操作要麼同時成功,要麼都不成功,這就涉及到對多個更新操作的事務管理問題了。在開發中,DAO層的職責應該只涉及到基本的CRUD,不涉及具體的業務操作,所以在開發中DAO層出現事務處理方法是一種不好的設計。故,在此提取工具類,使其方便在service層進行包裹dao服務來處理事務。

在此,使用ThreadLocal類進行改造,ThreadLocal一個容器,向這個容器儲存的物件,在當前執行緒範圍內都可以取得出來,向ThreadLocal裡面存東西就是向它裡面的Map存東西的,然後ThreadLocal把這個Map掛到當前的執行緒底下,這樣Map就只屬於這個執行緒了。

匯入相關的包檔案:

c3p0-0.9.1.2.jar
commons-dbutils-1.6.jar
commons-logging-1.1.3.jar
mysql-connector-java-5.1.12-bin.jar

配置檔案c3p0-config.xml:

<c3p0-config>
    <default-config>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/test
        </property>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="user">root</property>
        <property name="password">root</property>
        <property name="initialPoolSize">3</property>
        <property name="maxPoolSize">6</property>
        <property name="maxIdleTime">1000</property>
    </default-config>

    <named-config name="oracle_config">
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc_demo</property>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="user">scot</property>
        <property name="password">tiger</property>
        <property name="initialPoolSize">3</property>
        <property name="maxPoolSize">6</property>
        <property name="maxIdleTime">1000</property>
    </named-config>

</c3p0-config>

編寫工具類:

package d0608;

import java.sql.Connection;
import java.sql.SQLException;

import org.apache.commons.dbutils.QueryRunner;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JdbcUtil {

    public static ComboPooledDataSource dataSource = new ComboPooledDataSource();
    public static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();

    // 簡化事務方法
    public static void startTransaction(TransactionProcess transactionProcess ) {
        try {
            start();
            // 呼叫包裹事務過程的介面的process方法
            transactionProcess.process();
            commit();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            try {
                rollback();
            } catch (SQLException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            try {
                close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    // 包裹事務的過程介面
    public interface TransactionProcess{
        void process();
    }

    // 獲取查詢
    public static QueryRunner getQueryRunner() {
        return new QueryRunner();
    }

    // 獲取Connection
    public static Connection getConnection() throws SQLException {
        Connection conn = threadLocal.get();
        if (conn == null) {
            conn = dataSource.getConnection();
            threadLocal.set(conn);
        }
        return conn;
    }

    // 開啟事物
    public static void start() throws SQLException {
        getConnection().setAutoCommit(false);
    }

    // 提交事務
    public static void commit() throws SQLException {
        getConnection().commit();
    }

    // 回滾事務
    public static void rollback() throws SQLException {
        getConnection().rollback();
    }

    // 關閉資源
    public static void close() throws SQLException {
        threadLocal.remove();
    }

}

測試使用類:

package d0608;

import java.sql.SQLException;

/**
 * 測試獲取資料
 * 換行符:Unix
 * 字元格式:UTF-8
 * 使用說明:如果需要使用事務,則直接呼叫工具類中的方法,將事務的過程包裹即可
 */
public class Test {

    public static void main(String[] args) throws SQLException, ClassNotFoundException {

        JdbcUtil.startTransaction(new JdbcUtil.TransactionProcess() {
            @Override
            public void process() {
                // TODO Auto-generated method stub
                // 將需要事務處理的過程包裹在這裡
                try {
                    // 正常的插入操作
                    JdbcUtil.getQueryRunner().update(JdbcUtil.getConnection(),"insert into tree(id,pId,name) values(123,123,'123')");
                    // 模擬異常的產生
                    int a = 1/0;
                    // 正常的插入操作
                    JdbcUtil.getQueryRunner().update(JdbcUtil.getConnection(),"insert into tree(id,pId,name) values(234,234,'234')");
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        });

        // 結果,由於非零的的異常,事務進行回滾,未插入任何一條資料,符合事務的要求

    }

}

05 事務處理·filter層·c3p0&ThreadLocal&Filter

放一個網路圖(不是我畫的):

在這裡我首先說一下,為什麼要使用這麼麻煩的攔截器,而不在service層直接使用對需要處理的事物進行包裹起來處理。

第一:我的第一感覺是,我看到教學視訊上是說事務處理放在filter中,就像處理response和request的字元過濾器一樣,為什麼我覺得這樣寫的程式碼更難受啊,感覺怎想不通了,這樣每次的操作都會事務過濾一下,不是會很麻煩嘛。我很疑問為什麼不是寫在業務處理層,我給寫成了工具類,直接在業務處理層進行使用,這樣就可以該事務的時候,包裹一下事務的過程就好了。

第二:經過網路的查詢,我發現,什麼樣的都有,有dao層的,有service層的,有filter層的。然後我去問一些群裡的同學,他們說在公司中,比較多的是在service層的,但是這樣確實是可以在filter裡面做多資料來源事務切換,很方便。

匯入相關的包檔案:

c3p0-0.9.1.2.jar
commons-dbutils-1.6.jar
commons-logging-1.1.3.jar
mysql-connector-java-5.1.12-bin.jar

配置檔案c3p0-config.xml:

<c3p0-config>
    <default-config>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/test
        </property>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="user">root</property>
        <property name="password">root</property>
        <property name="initialPoolSize">3</property>
        <property name="maxPoolSize">6</property>
        <property name="maxIdleTime">1000</property>
    </default-config>

    <named-config name="oracle_config">
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc_demo</property>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="user">scot</property>
        <property name="password">tiger</property>
        <property name="initialPoolSize">3</property>
        <property name="maxPoolSize">6</property>
        <property name="maxIdleTime">1000</property>
    </named-config>

</c3p0-config>

編寫工具類:

package d0608;

import java.sql.Connection;
import java.sql.SQLException;

import org.apache.commons.dbutils.QueryRunner;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JdbcUtil {

    public static ComboPooledDataSource dataSource = new ComboPooledDataSource();
    public static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();

    // 簡化事務方法
    public static void startTransaction(TransactionProcess transactionProcess ) {
        try {
            start();
            // 呼叫包裹事務過程的介面的process方法
            transactionProcess.process();
            commit();
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            try {
                rollback();
            } catch (SQLException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            try {
                close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    // 包裹事務的過程介面
    public interface TransactionProcess{
        void process();
    }

    // 獲取查詢
    public static QueryRunner getQueryRunner() {
        return new QueryRunner();
    }

    // 獲取Connection
    public static Connection getConnection() throws SQLException {
        Connection conn = threadLocal.get();
        if (conn == null) {
            conn = dataSource.getConnection();
            threadLocal.set(conn);
        }
        return conn;
    }

    // 開啟事物
    public static void start() throws SQLException {
        getConnection().setAutoCommit(false);
    }

    // 提交事務
    public static void commit() throws SQLException {
        getConnection().commit();
    }

    // 回滾事務
    public static void rollback() throws SQLException {
        getConnection().rollback();
    }

    // 關閉資源
    public static void close() throws SQLException {
        threadLocal.remove();
    }

}

編寫過濾器filter:

package d0608;

import java.io.IOException;
import java.sql.SQLException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import d0608.JdbcUtil.TransactionProcess;

/**
 * Servlet Filter implementation class TransactionFilter
 */
public class TransactionFilter implements Filter {

    public void destroy() {
        // TODO Auto-generated method stub
        System.out.println("filter...destroy()...");
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // TODO Auto-generated method stub
        JdbcUtil.startTransaction(new TransactionProcess() {

            @Override
            public void process() {
                // TODO Auto-generated method stub
                try {
                    chain.doFilter(request, response);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (ServletException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        });

    }


    public void init(FilterConfig fConfig) throws ServletException {
        // TODO Auto-generated method stub
        System.out.println("filter...init()...");
    }

}

編寫servlet測試類:

package d0608;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class TestServlet
 */
public class TestServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        // TODO Auto-generated method stub

        try {
            JdbcUtil.getQueryRunner().update(JdbcUtil.getConnection(), "insert into tree(id,pId,name) values(123,123,'jjj')");
            //  模擬異常的操作
            int a = 1/0;
            JdbcUtil.getQueryRunner().update(JdbcUtil.getConnection(), "insert into tree(id,pId,name) values(234,234,'hhh')");
            System.out.println("testServlet...");
        } catch (Exception e) {
            // TODO Auto-generated catch block
            throw new RuntimeException("出錯了");
        }

    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }

}

編寫servlet和filter的web.xml檔案:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>d0608</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <filter>
    <display-name>TransactionFilter</display-name>
    <filter-name>TransactionFilter</filter-name>
    <filter-class>d0608.TransactionFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>TransactionFilter</filter-name>
    <!--這裡是過濾的規則-->
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <servlet>
    <description></description>
    <display-name>TestServlet</display-name>
    <servlet-name>TestServlet</servlet-name>
    <servlet-class>d0608.TestServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>TestServlet</servlet-name>
    <url-pattern>/testServlet</url-pattern>
  </servlet-mapping>
</web-app>

關於filter的過濾規則url-pattern的填寫:

過濾器的xml匹配寫法:

1.完全匹配
  <url-pattern>/test/list.do</url-pattern>  

2.路徑匹配
  <url-pattern>/*</url-pattern> 匹配根路徑下的全部請求

3.副檔名匹配
  <url-pattern>*.do</url-pattern> struts1
  <url-pattern>*.html</url-pattern> 匹配全部html結尾的請求
  <url-pattern>*</url-pattern> 不能用*,否則報錯