jdbc(增刪改查,批處理,javabean,連接池,事務)


JDBC 常見知識點(一)

JDBC基本知識點(二)

JDBC常見知識點(三)

1,JDBC (JAVA DataBase Connectivity :   java 數據庫連接)

2,如何連接數據庫?

需要有一個 對應數據庫的驅動jar 文件,實現Driver 接口注冊,再通過DiverManger 類來獲取數據庫的連接。

比如:

數據庫的驅動jar 包 (Mysql 對應: Mysql-connector-java-5.17.jar,不同的數據庫對應不用的jar 包)放入到 web root/WEB-INF/lib/ 目錄下面

3,DOS開啟數據庫服務的方法:

1,點擊windos 鍵,在彈出的搜索框中,輸入cmd,右鍵 “管理員身份運行” 打開
2,輸入 net start mysql ,這樣就能開啟mysql 服務了。

4,注冊加載數據庫驅動有下面三種方式

顯示注冊:
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
還可以使用這兩種方式:
Class.forName("com.mysql.jdbc.Driver").newInstance();
Class.forName("com.mysql.jdbc.Driver");  // 我們查看源碼會發現,執行forName 會調用newInstance() ,所以跟上面的方法一樣。

5,對數據庫的 增 刪 改 查

當我們使用Statement (或者 PreparedStatement)對數據庫的增刪改查 ,
其中 增加,刪除,修改 都是執行executeUpdate()方法 操作,
查詢是 executeQuary() 方法的操作,
並且查詢操作會返回一個結果集(ResultSet)。

6,Statement  和 PreparedStatement 的區別:

我們可以利用 Statement 來實現對數據庫的增刪改查,我們只需要組織出正確的sql 語句,就可以實現。
但是我們所寫的sql 語句不可以使用參數代替。也就是(?)代替。 PreparedStatement 繼承於 Statement 重寫了Statement 的所有方法。 PreparedStatement 允許使用不完整的 sql 語句,空缺的值使用(?)代替,直到執行的時候,再輸入進去就可以了。
使用 Statement :
Statement createStatement = conn.createStatement();
int executeUpdate2 = createStatement.executeUpdate("insert into user(name,password) values('1','2')");  
使用 preparedStatement:
//創建預編譯語句對象 pstmt = conn.prepareStatement("insert into user(name,password) values(?,?)"); //給4個占位符賦值 pstmt.setString(1, "ww"); pstmt.setString(2, "789"); //執行SQL語句(增加記錄) int n = pstmt.executeUpdate();

7,實現對數據庫的操作。

package demo;

import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Ex01 {
    /**
     * 演示:JDBC訪問數據庫的操作步驟  查詢
     */
    public static void main(String[] args) {

      Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            //1、加載驅動程序
            Class.forName("com.mysql.jdbc.Driver").newInstance();
            // 2、通過DriverManager建立數據庫連接 Driver 驅動
            String url = "jdbc:mysql://127.0.0.1:3306/test";
            String user = "root";
            String password = "111";
            conn = DriverManager.getConnection(url, user, password);
            // 3.通過Connection獲取語句對象Statement (聲明)
            stmt = conn.createStatement();
            // 4.使用語句對象執行SQL語句
            rs = stmt.executeQuery("select * from user");
            // 5.遍歷結果集
            while (rs.next()) {
                // 取出當前行各個字段的值
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String pwd = rs.getString("password");

                System.out.println(id + " " + name + " " + pwd);
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {// 一定會執行的代碼塊,無論是否發生異常
            // 6.釋放相關資源
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                rs = null;
            }
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                stmt = null;
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                conn = null;
            }
        }
    }
}
package demo;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;

public class Ex02 {
    /**
     * 演示:預編譯語句對象PreparedStatemet的使用
     */
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            String url = "jdbc:mysql://127.0.0.1:3306/test";
            String user = "root";
            String password = "111";
            conn = DriverManager.getConnection(url, user, password);
            pstmt = conn
                    .prepareStatement("insert into user(name,password,email,birthday) values(?,?,?,?)");
            // 給4個占位符賦值
            pstmt.setString(1, "ww");
            pstmt.setString(2, "789");
            pstmt.setString(3, "ww@qq.com");
            pstmt.setDate(4, Date.valueOf("2016-01-01"));
            // 執行SQL語句(增加記錄)
            int n = pstmt.executeUpdate();
            if (n > 0) {
                System.out.println("增加記錄成功");
            }
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SQLException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();
        } finally {

            if (pstmt != null) {

                try {

                    pstmt.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

                pstmt = null;            }
            if (conn != null) 
                try {
                    conn.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}
            pstmt = conn.prepareStatement("delete from user where id=?");
            pstmt.setInt(1, id);
            int n = pstmt.executeUpdate();
            if(n>0){
                return true; // 操作成功
            }else{
                return false;  // 操作石板
            }
            pstmt = conn.prepareStatement("update user set name=?,password=?,email=?,birthday=? where id=?");
            pstmt.setString(1, user.getName());
            pstmt.setString(2, user.getPassword());
            pstmt.setString(3, user.getEmail());
            pstmt.setDate(4, user.getBirthday());
            pstmt.setInt(5, user.getId());
            int n = pstmt.executeUpdate();
            if(n>0){
                return true;
            }else{
                return false;
            }

8,JDBC 批處理SQL  

可以使用 Statement  或者 PreparedStatement 對象來實現。
注意:
能夠批量處理執行的SQL 必須是 INSTER UPDATE DELETE  等返回 int 類型的SQL。
不能批量執行 SELECT 語句,會進行報錯。另外批處理需要數據庫的支持,可能有些數據庫不支持。
例子一:使用Statement 批處理
package cn.edu.aynu.sushe.utils;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
public class PichuliStatement {
    private static Connection conntection;
    private static Statement statement;
    public static void main(String[] args) {
        try {
            conntection = JDBCUtils.getConntection();
            statement = conntection.createStatement();
            for (int i = 0; i < 10; i++) {
                String sql = "insert into user(name,password) values('a" + i
                        + "','aaa" + i + "')";
                statement.addBatch(sql); // batch 批量
            }
            // 批量執行將每句sql 執行的結果返回為 int【】 數組
            int[] executeBatch = statement.executeBatch(); 
            for (int i = 0; i < executeBatch.length; i++) {
                System.out.println(executeBatch[i] + "");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 關閉流
            if (conntection != null) {
                try {
                    conntection.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            // 關閉流
            if (statement != null) {
                try {
                    statement.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }        }
    }
}

數據庫:

輸出:

1
1
1
1
1
1
1
1
1
1

這個返回的int[]  當數組中的數據出現 大於 0 就代表這條sql 語句被執行成功了。如果小於0 就意味着sql 語句沒有執行成功。

例子二:使用PreparedStatement 批處理  

package cn.edu.aynu.sushe.utils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class pichuliPreparedStatement { 
    
    public static void main(String[] args) {        
    try {
            Connection conntection = JDBCUtils.getConntection();
            String sql = "insert into user(name,password) values(?,?)";
            PreparedStatement prepareStatement = conntection.prepareStatement(sql);
            
            for (int i = 0; i < 10; i++) {
                int index = 1 ;
                prepareStatement.setString(1, "aa"+i);    
                prepareStatement.setString(2, "aa"+i); 
                prepareStatement.addBatch();
                
            }
            int[] executeBatch = prepareStatement.executeBatch();
            for (int i = 0; i < executeBatch.length; i++) {
                System.out.println(i+"haha");
            }                        
        } catch (Exception e) {
            e.printStackTrace();
        }                
    }
}

數據庫輸出:

                

處理結果集(針對的就是執行select 語句拿到的ResultSet 對象)

package cn.edu.aynu.sushe.utils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class ResultSetDemo01 {
    /**
     * @param args
     */
    public static void main(String[] args) {
        Connection conntection;
        try {
            conntection = JDBCUtils.getConntection();
            Statement createStatement = conntection.createStatement(
                    ResultSet.TYPE_SCROLL_INSENSITIVE,
                    ResultSet.CONCUR_UPDATABLE);
            ResultSet rs = createStatement.executeQuery("select * from user");
            // 光標指向第2行
            rs.absolute(2);
            System.out.println(rs.getInt("id"));
            // 光標向上移動1行
            rs.previous();
            System.out.println(rs.getInt("id"));
            // 光標相對向下移動2行
            rs.relative(2);
            System.out.println(rs.getInt("id"));
            // 光標相對向上移動2行
            rs.relative(-2);
            System.out.println(rs.getInt("id"));
            // 向下移動一行
            rs.next();
            System.out.println(rs.getInt("id"));
        } catch (Exception e) {
            e.printStackTrace();
        }    }
}

 輸出:

9,分頁顯示功能

10,JDBC 高級應用

 DAO 模式  和 JavaBean

DAO (數據庫操作對象 DataBase Access  Object)是JDBC 下常用的模式。
保存數據時,將java  bean 的屬性拆分成sql語句,並保存到數據庫中。
讀取數據時,將數據從數據庫讀出來,通過setXXX方法設置到javabean 中。
看來代碼怎么組成:
javabean  對象: (省略  get  set 方法)
package cn.edu.aynu.shuse.bean;
public class Users {
    private int id; // 用戶的id,自動增長,作為 linkman 的外鍵
    private String username; // 用戶的姓名
    private String password; // 用戶的密碼
            getXXX
            setxxx

Dao 類:

package cn.edu.aynu.shuse.dao;

import java.sql.SQLException;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import cn.edu.aynu.shuse.bean.Users;
import cn.edu.aynu.shuse.utils.JDBCUtils;
/**
 * 對user表的數據操作,兩方面{ (1)根據賬戶名,去查找密碼 (2)注冊添加用戶的信息 }
 * 
 * @author Administrator
 * 
 */
public class UsersDao {
    /**
     * QueryRunner類簡化了執行SQL語句的代碼,它與ResultSetHandler組合在一起就能完成大部分的數據庫操作,大大減少編碼量。
     * 針對不同的數據庫操作,QueryRunner類提供的不同的方法。
     */
    QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
    /**
     * (1)根據賬戶名,去查找密碼 
     * 
     * @throws SQLException
     */
    public Users select(String name) throws SQLException {
        String sql = "select * from user where username=?";
        Users list = qr.query(sql, new BeanHandler<Users>(Users.class), name);
        return list;
    }
    /**
     * (2)注冊添加用戶的信息
     * 
     * @throws SQLException
     */
    public void add(String name, String password) throws SQLException {
        String sql = "insert into user(username,password) values(?,?) ";
        qr.update(sql, name, password);
    }
}

11,JDBC 數據庫連接池

為什么需要使用數據庫連接池?

在JDBC編程中,每次創建和斷開Connection對象都會消耗一定的時間和IO資源
頻繁地創建、斷開數據庫連接勢必會影響數據庫的訪問效率,甚至導致數據庫崩潰。
為了避免頻繁的創建數據庫連接,工程師們提出了數據庫連接池技術。

什么是數據庫連接池?

數據庫連接池負責分配、管理和釋放數據庫連接,它允許應用程序重復使用現有的數據庫連接,而不是重新建立。

怎么使用數據庫連接池?

【1】獲取數據庫連接(Connection)

既然要連接數據庫就應該獲取數據庫連接對象(Connection),那么我們通過使用JDBC提供了javax.sql.DataSource接口
(它負責與數據庫建立連接,並定義了返回值為Connection對象的方法) Connection getConnection() Connection getConnection(String username,String password) 來拿到我們的數據庫連接對象

【2】獲取數據庫連接池對象(DataSource)

我們習慣性的把實現了javax.sql.DataSource接口的類稱為數據源,顧名思義,數據源即數據的來源

在數據源中存儲了所有建立數據庫連接的信息

常用的數據源分為兩種 :
1,DBCP  
2,C3P0
DBCP數據庫連接池(DataBase Connection Pool)的簡稱,是Apache組織下的開源連接池實現,也是Tomcat服務器使用的連接池組件單獨使用DBCP數據源時,需要在應用程序中導入兩個jar包。commons-pool.jar包  commons-dbcp.jar包
(commons-dbcp.jar包中包含兩個核心類,分別是BasicDataSourceFactoryBasicDataSource,它們都包含獲取DBCP數據源對象的方法。)

C3P0是目前最流行的開源數據庫連接池之一,它實現了DataSource數據源接口,支持JDBC2和JDBC3的標准規范,易於擴展並且性能優越,
著名的開源框架Hibernate和 Spring使用的都是該數據源。 需要了解C3P0中DataSource接口的實現類ComboPooledDataSource,它是C3P0的核心類,提供了數據源對象的相關方法

【3】使用DBCP 數據源 獲取數據庫的連接

當使用DBCP數據源時,首先得創建數據源對象,數據源對象的創建方式有兩種

第一種通過BasicDataSource直接創建數據源對象手動給數據源對象設置屬性值然后獲取數據庫連接對象第二種通過讀取配置文件創建數據源對象使用BasicDataSourceFactory工廠類讀取配置文件創建數據源對象然后獲取數據庫連接對象。(推薦使用)

第一種方式:類創建

package cn.edu.aynu.DBCP;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
public class leichuangjian {
        public static DataSource  ds = null ;
    static {
        // 獲取DBCP的數據源對象
        BasicDataSource basicDataSource = new BasicDataSource();
        /*
         * 【1】設置數據庫的配置信息
         */
        // 設置連接數據庫的驅動名稱        
basicDataSource.setDriverClassName("com.mysql.jdbc.Driver"); // 設置連接數據庫的路徑 basicDataSource.setUrl("jdbc:mysql://localhost:3306/test"); // 設置數據庫的登陸賬號 basicDataSource.setUsername("root"); // 設置數據庫的登錄密碼 basicDataSource.setPassword("111"); /* * 【2】設置數據庫連接池的信息 * */ //設置數據庫連接池初始化的連接數目 basicDataSource.setInitialSize(10); //設置數據庫連接池最大活躍的連接數目 basicDataSource.setMaxActive(5); //設置數據庫連接池最小閑置的連接數目 basicDataSource.setMinIdle(2); ds = basicDataSource; } /** * @param args * @throws SQLException */ public static void main(String[] args) throws SQLException { // 導入jar包 // 從數據庫獲取一個連接 Connection connection = ds.getConnection(); System.out.println(connection); } }
第二種:使用配置文件進行創建

注意:這個配置文件 后綴必須是 : xxx.properties 否者創建的就是錯誤的

package cn.edu.aynu.DBCP;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
public class peizhichuangjian {
    /**
     * @param args
     * @throws Exception
     */
    @SuppressWarnings("static-access")
    public static void main(String[] args) throws Exception {
        // 通過配置文件來返回數據庫的連接
        Properties properties = new Properties();
        // 通過類加載器拿到配置信息
        InputStream inputStream = new peizhichuangjian()//
                .getClass()//
                .getClassLoader()//
                .getSystemResourceAsStream("ds.properties");
        // 加載拿到的流
        properties.load(inputStream);
        // 通過工廠類創建DataSourse
        DataSource createDataSource = BasicDataSourceFactory
                .createDataSource(properties);
        Connection connection = createDataSource.getConnection();
        System.out.println(connection);
    }
}

【4】使用C3P0 數據源 獲取數據庫的連接

 當使用C3P0數據源時,首先得創建數據源對象,數據源對象的創建方式有兩種。

第一種通過ComboPooledDataSource類直接創建數據源對象手動給數據源對象設置屬性值然后獲取數據庫連接對象第二種通過讀取配置文件創建數據源對象使用ComboPooledDataSource讀取配置文件,創建數據源對象,然后獲取數據庫連接對象。(推薦使用)
其中讀取配置文件,有兩種方式一種是默認的一種是自定義的詳情看下面)

一,類創建:

package cn.edu.aynu.C3P0;

import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class leichuangjian {
    public static DataSource ds = null;
    static {
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
        // 【1】配置數據源的信息
        try {
            comboPooledDataSource.setDriverClass("com.mysql.jdbc.Driver");
            comboPooledDataSource
                    .setJdbcUrl("jdbc:mysql://127.0.0.1:3306/test");
            comboPooledDataSource.setUser("root");
            comboPooledDataSource.setPassword("111");
            // 設置連接池信息
            comboPooledDataSource.setInitialPoolSize(10);
            comboPooledDataSource.setMaxPoolSize(100);
            ds = comboPooledDataSource;
        } catch (PropertyVetoException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    /**
     * @param args
     * @throws SQLException 
     */
    public static void main(String[] args) throws SQLException {
        Connection connection = ds.getConnection();
        System.out.println(connection);        
    }
}

二,使用配置文件進行創建:

以xml 的方式進行的存儲 這個文件存放在 src 目錄下

 

其中的配置代碼:

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <!-- 默認配置,當使用ComboPooledDataSource無參構造器時,使用的就是這個配置 -->
    <default-config>
        <!-- 基本配置 -->
        <property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/test</property>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="user">root</property>
        <property name="password">111</property>
        <!-- 每次增量,當需要創建Connection對象時,一次創建幾個 -->
        <property name="acquireIncrement">3</property>
        <!-- 當創建池對象后,池中應該有幾個Connection對象 -->
        <property name="initialPoolSize">10</property>
        <!-- 池中最少Connection個數,如果少於這個值,就會創建Connection -->
        <property name="minPoolSize">2</property>
        <!-- 池中最大連接個數 -->
        <property name="maxPoolSize">10</property>
    </default-config>
    <!-- 命名配置,new ComboPooledDataSource("oralce-config")時,使用的就是這個配置 -->
    <named-config name="mysql-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">111</property>
        <property name="acquireIncrement">3</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">2</property>
        <property name="maxPoolSize">10</property>
    </named-config>
</c3p0-config>

獲取數據源:

package cn.edu.aynu.C3P0;

import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class morenpeizhi {
    public static DataSource ds = null;
    
    static{
        // 使用默認的配置
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
        ds =comboPooledDataSource;
        // 使用mysql-config 命名的文件創建
        ComboPooledDataSource comboPooledDataSource  = new ComboPooledDataSource("mysql-config");
        ds =comboPooledDataSource ;
    }    
    /**
     * @param args
     * @throws SQLException 
     */
    public static void main(String[] args) throws SQLException {
        Connection connection = ds.getConnection();  // 拿到連接
        System.out.println(connection);        
    }
}

12,事務實例: 銀行轉賬(經典題目)

如何使用JDBC處理事務?

1)在數據庫操作中,一項事務由一條或多條操作數據庫的SQL語句組成的一個不可分割的工作單元2)只有當事務中的所有操作都正常完成整個事務才能被提交到數據庫中,如果有一項操作沒有完成,則整個事務會被撤銷
 例如 銀行的轉賬業務 

通過 Connection 接口中提供了三個方法來針對 JDBC 處理事物。

其中有三個方法:
setAutoCommit(boolean autoCommit)// 設置自動提交 我們需要將這個方法設置為 false 不自動提交
//開啟事務
    public static void startTransaction(){
        try {
            //獲取連接
            Connection conn = getConnection();
            //開啟事務
            conn.setAutoCommit(false);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }        
    }
commit() // 提交   當我們的事務完成的時候,提交
//提交事務
    public static void commit(){
        try {
            //從集合tc中得到一個連接
            Connection conn = tc.get();
            if(conn != null){
                conn.commit();
            }            
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

rollback()  // 回滾  當我們的事務出現異常的時候,回滾

//回滾事務
    public static void rollback(){
        try {
            //從集合tc中得到一個連接
            Connection conn = tc.get();
            if(conn != null){
                conn.rollback();
            }
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

銀行轉賬的業務邏輯

用戶 zs 要向 ls 的賬戶上轉200 元錢,該轉賬業務需要分成兩步:
第一步:zs 的賬戶上需要減少 200 元錢
第二步:ls 的賬戶上需要增加 200 元錢
這兩步操作需要都成功,事務才成功。也就是說,要么都執行成功,要么都沒有執行成功。 那么就會出現兩種結果,一種是commit() 執行成功,要么就需要執行 rollback () ;

13,介紹ThreadLocaL類

為什么使用ThreadLocaL 類?
Web應用程序,多用戶並發訪問。
一個客戶端訪問時,服務器啟動一個線程,比如轉賬,在這個線程中獨立完成這個事務操作,
另外一個客戶端也來訪問時,服務器也啟動另外一個線程,在另外一個線程中獨立完成轉賬事務操作。 要想多線程中每個事務執行成功必須保證每個線程有獨立的Connection對象
ThreadLocaL類介紹:
ThreadLocaL類:在一個線程中記錄本地變量。
我們可以將一個Connection對象放到這個ThreadLocaL集合中,只要是這個線程中的對象都可以共享這個Connection對象線程結束后刪除這個Connection對象
這樣保證:一個事務一個連接。

TreadLoacl本質上是一個Map,鍵是當前線程對象,值可以是我們的當前連接對象。

    //創建一個ThreadLoacl對象,用當前線程作為key
    private static ThreadLocal<Connection> tc = new ThreadLocal<Connection>();

14,實例

在使用數據源(DataSource )時,如果是業務處理,使用C3P0 時,不需要拿數據源對象。

Java Web應用開發的三層架構:
1、表示層(Web層):jsp和servlet
2、業務邏輯層(Service層):XXXService,事務操作是在service層完成的。
3、數據訪問層(dao層):XXXDao
按照編寫正常代碼演示:
bean-->Utils ---> Dao--->service--->Servlet
先看下數據庫表:
test數據庫中 account 表三個字段:  id 主鍵自動遞增,name 姓名 , money 賬戶的金額 (double 類型)

 Account.java

package cn.edu.aynu.rjxy.bean;
public class Account {

    private int id;
    private String name;
    private double 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 double getMoney() {
        return money;
    }
    public void setMoney(double money) {
        this.money = money;
    }
}

JDBCUtils.java

package cn.edu.aynu.rjxy.utils;

import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class JDBCUtils {
    //創建一個ThreadLoacl對象,用當前線程作為key
    private static ThreadLocal<Connection> tc = new ThreadLocal<Connection>();
    //從c3p0-config.xml文件中讀取默認配置創建數據庫連接池對象
    private static DataSource ds = new ComboPooledDataSource();

    //得到數據庫連接池對象
    public static DataSource getDataSource(){
        return ds;
    }
    
    //從連接池中獲取連接
    public static Connection getConnection() throws Exception{
        //從集合tc中獲取一個Connection
        Connection conn = tc.get();
        if(conn == null){
            conn = ds.getConnection();
            //將conn存放到集合tc中
            tc.set(conn);
            System.out.println("首次創建連接:"+conn);
        }
        return conn;
    }

    //開啟事務
    public static void startTransaction(){
        try {
            //獲取連接
            Connection conn = getConnection();
            //開啟事務
            conn.setAutoCommit(false);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    
    }
    //提交事務
    public static void commit(){
        try {
            //從集合tc中得到一個連接
            Connection conn = tc.get();
            if(conn != null){
                conn.commit();
            }            
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }        
    }
    
    //回滾事務
    public static void rollback(){
        try {
            //從集合tc中得到一個連接
            Connection conn = tc.get();
            if(conn != null){
                conn.rollback();

            }
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    //關閉連接釋放資源
    public static void close(){
        //從集合tc中得到一個連接
        Connection conn = tc.get();
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }finally{
                //從集合tc中移除連接對象
                tc.remove();
                conn = null;
            }
        }
    }
}

AccountDao,java

package cn.edu.aynu.rjxy.dao;

import java.sql.Connection;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import cn.edu.aynu.rjxy.bean.Account;
import cn.edu.aynu.rjxy.utils.JDBCUtils;

public class AccountDao {
    //根據賬戶名查詢賬戶信息
    public Account find(String name) throws Exception{
        QueryRunner run = new QueryRunner();   // 處理事務,不需要傳 數據源對象
        Connection conn = JDBCUtils.getConnection();
        System.out.println("find()得到連接:"+conn);
        String sql = "select * from account where name=?";
        Account account  = (Account)run.query(conn, sql, new BeanHandler(Account.class), new Object[]{name});    
        return account;        
    }
    
    //轉賬(可能轉入也可能轉出)
    public void update(Account account) throws Exception {
        QueryRunner run = new QueryRunner();
        Connection conn = JDBCUtils.getConnection();
        System.out.println("update()得到連接:"+conn);
        String sql = "update account set money=? where name=?";
        run.update(conn, sql, new Object[]{account.getMoney(),account.getName()});
    }
}

AccountService.java  轉賬的業務邏輯

package cn.edu.aynu.rjxy.service;
import cn.edu.aynu.rjxy.bean.Account;
import cn.edu.aynu.rjxy.dao.AccountDao;
import cn.edu.aynu.rjxy.utils.JDBCUtils;
public class AccountService {
    //轉賬事務
    public static void transfer(String fromName,String toName,double balance){
        try {
            //開啟事務
            JDBCUtils.startTransaction();
            AccountDao dao = new AccountDao();
            //查詢兩個賬戶的金額
            Account accountFrom = dao.find(fromName);
            Account accountTo = dao.find(toName);
            //判斷是否可以轉賬
            if(balance<accountFrom.getMoney()){
                //可以轉賬
                //設置轉出賬戶的金額
                accountFrom.setMoney(accountFrom.getMoney()-balance);
                //設置轉入賬戶的金額
                accountTo.setMoney(accountTo.getMoney()+balance);
                //執行數據庫更改操作
                dao.update(accountFrom); 
                dao.update(accountTo);
                //提交事務
                JDBCUtils.commit();
                System.out.println("事務提交成功");
            }else{
                System.out.println("轉出賬戶金額不足");
            }
            
        } catch (Exception e) {
            //回滾事務
            JDBCUtils.rollback();
            System.out.println("事務提交失敗");
            e.printStackTrace();
        }finally{
            //釋放資源
            JDBCUtils.close();
        }
    }
}
測試轉賬是否能夠完成
import cn.edu.aynu.rjxy.service.AccountService;
public class TestTransfer {
    /**
     * @param args
     */
    public static void main(String[] args) {
        //實現從zs賬戶上轉賬200到ls賬戶上
        AccountService.transfer("zs", "ls", 200);
    }
}
源碼:https://github.com/gengzi/files/tree/master/%E9%93%B6%E8%A1%8C%E8%BD%AC%E8%B4%A6%EF%BC%88%E6%A1%88%E4%BE%8B%EF%BC%89
數據庫:https://github.com/gengzi/files/tree/master/%E9%93%B6%E8%A1%8C%E8%BD%AC%E8%B4%A6%EF%BC%88%E6%A1%88%E4%BE%8B%EF%BC%89

 

 
            

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM