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
能夠批量處理執行的SQL 必須是 INSTER UPDATE DELETE 等返回 int 類型的SQL。 不能批量執行 SELECT 語句,會進行報錯。另外批處理需要數據庫的支持,可能有些數據庫不支持。
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 中。
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包中包含兩個核心類,分別是BasicDataSourceFactory和BasicDataSource,它們都包含獲取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 處理事物。
//開啟事務 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(); } }
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 時,不需要拿數據源對象。
1、表示層(Web層):jsp和servlet 2、業務邏輯層(Service層):XXXService,事務操作是在service層完成的。 3、數據訪問層(dao層):XXXDao
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