银行转账(跨库事务)
题目:
数据库事务,银行转账的例子。
a和b不在同一个数据库里(跨数据库)的事务设计。如何保证两个或多个数据库之间转账事务的执行和回滚。
MySQL怎么做。自己写代码怎么实现。
【如何实现跨库的事务操作】(跨数据库怎样保证事务执行和回滚)
【两段式提交】
两个事务,在保证一个"中心"事务是完整的情况下,如果这个事务提交,将另一个事务加入到一个队列中去异步由另外的进程负责完成操作。
【银行转账业务场景的几种实现思路对比】
银行转账的核心业务逻辑:
- 源账户扣除转账金额,当然首先需要先判断源账户余额是否足够,如果不够,则无法转账;
- 目标账户增加转账金额;
- 为源账户生成一笔转账记录;
- 为目标账户生成一笔转账记录;
【用数据库事务控制语句模拟银行汇款过程】
Connection
PreparedStatement
ResultSet
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import jdbc.util.DBUtil; public class Trans { public static void main(String[] args) { Trans tran = new Trans(); tran.transfer( 1 , 2 , 500 ); tran.print( "select * from account" ); } /** * 汇款 * @param from 汇款人 * @param to 接受人 * @param amount 汇款金额 */ public void transfer( int from, int to, double amount) { String sql1 = "update account set balance=balance-? where id=?" ; String sql2 = "update account set balance=balance+? where id=?" ; // 在汇款以后,如果汇出帐号的余额为负数,就抛出余额不足异常,自动回滚到原始状态 String sql3 = "select balance from account where id=?" ; Connection conn = null ; try { conn = DBUtil.getConnection(); conn.setAutoCommit( false ); //开启事务 PreparedStatement ps1 = conn.prepareStatement(sql1); ps1.setDouble( 1 ,amount); ps1.setInt( 2 , from); ps1.executeUpdate(); PreparedStatement ps2 = conn.prepareStatement(sql2); ps2.setDouble( 1 ,amount); ps2.setInt( 2 , to); ps2.executeUpdate(); //检查余额 PreparedStatement ps3 = conn.prepareStatement(sql3); ps3.setInt( 1 , from); ResultSet rs = ps3.executeQuery(); double balance = 0 ; while (rs.next()){ balance = rs.getDouble( "balance" ); } if (balance< 0 ){ throw new Exception( "余额不足" ); } rs.close(); ps1.close();ps2.close();ps3.close(); conn.commit(); //提交事务 } catch (Exception e){ e.printStackTrace(); try { conn.rollback(); //回滚事务 } catch (SQLException e1) { e1.printStackTrace(); } } finally { DBUtil.close(conn); } } public void print(String sql){ Connection conn = null ; try { conn = DBUtil.getConnection(); Statement st = conn.createStatement(); ResultSet rs = st.executeQuery(sql); ResultSetMetaData meta = rs.getMetaData(); int cols = meta.getColumnCount(); for ( int i= 1 ;i<=cols;i++){ System.out.print(meta.getColumnName(i)+ " " ); } System.out.println(); while (rs.next()){ for ( int i= 1 ;i<=cols;i++){ Object data = rs.getObject(i); System.out.print(data+ " " ); } System.out.println(); } rs.close(); st.close(); } catch (SQLException e){ e.printStackTrace(); } finally { DBUtil.close(conn); } } } |
【存储过程、事务在银行转账系统的应用】http://blog.csdn.net/joetao/article/details/5757024
最近项目中涉及到多表删除的情况,为了保证数据的完整性,准备用存储过程结合事务实现该效果。但自己以前只是看过事务方面的东西,但没真正用过。在网上收索了下发现这篇文章不错!但都是图片!如是自己全部敲了一边。并附上调用存储过程的代码!这篇文章对怎样写一个复杂的存储过程还是很有借鉴的意义的!直接贴带码了。
本篇一银行的转账系统为例说明,因为这个例子贴近生活,很容易理解!
本篇一银行的转账系统为例说明,因为这个例子贴近生活,很容易理解!
use BankSystem
use master go create DataBase BankSystem go use BankSystem go create table CardInfo ( PKID int identity(1,1) primary key , CardNum nvarchar(19) not null, --卡号 Balance money default(0) --余额 ) go create table Transfer_Event ( PKID int identity(1,1) primary key, FromCardID nvarchar(19) not null, --源卡号 ToCardID nvarchar(19) not null, --目标卡号 TransMoney money, --转账金额 OccurTime datetime --发生时间 ) --插入记录 insert into CardInfo(CardNum,Balance) values('123456789',80.8) insert into CardInfo(CardNum,Balance) values('123',10.9) go --实现转账过程的存储过程实现如下(成功返回0,余额不够返回1,出现异常返回2) create procedure TranserMoney ( @fromCardNum varchar(19), --转账源卡号 @toCardNum varchar(19), --转账目标卡号 @tansMoney Money, --转账金额 @occurTime DateTime --转账时间 ) as begin declare @cardNum varchar(19) declare @balance Money --开始事务 begin transaction --定义游标 declare cursorTransfer cursor for select CardNum,Balance from CardInfo where CardNum=@fromCardNum --打开游标 open cursorTransfer --取字段值,放入@cardNum,@balance fetch next from cursorTransfer into @cardNum,@balance while @@FETCH_STATUS=0 --取游标状态(0--表示成功) begin if(@balance>=@tansMoney) --判断余额是否大于转账金额 begin update CardInfo set Balance=Balance-@tansMoney where CardNum=@fromCardNum if @@ERROR<>0 goto ERR --发生错误跳转 update CardInfo set Balance=Balance+@tansMoney where CardNum=@toCardNum if @@ERROR<>0 goto ERR insert into Transfer_Event(FromCardID,ToCardID,TransMoney,OccurTime)values(@fromCardNum,@toCardNum,@tansMoney,@occurTime) if @@ERROR<>0 goto ERR commit transaction --提交事务 close cursorTransfer --关闭游标 deallocate cursorTransfer return 0 --执行正常,返回0 end else --金额不够,返回1,回滚事务 begin rollback transaction --回滚事务 close cursorTransfer --关闭游标 deallocate cursorTransfer return 1 --执行正常,返回0 end end end ERR:--发生异常返回2 begin rollback transaction --回滚事务 close cursorTransfer deallocate cursorTransfer return 2 end go --调用存储过程 declare @fromCardNum nvarchar(19),@toCardNum nvarchar(19),@money money,@dateTime DateTime,@flag int set @fromCardNum='123456789' set @toCardNum='123' set @dateTime='2009-7-17' set @money=50 exec @flag= TranserMoney @fromCardNum,@toCardNum,@money,@dateTime select @flag select *from CardInfo; select *from Transfer_Event; |