买书的例子 程序应该将图书数量的操作和更新account用户余额的操作作为一个事务来处理,只有这两个操作都完成的情况下,才能提交事务,否则就回滚事务。
1 buy.html
1 <html> 2 <head> 3 <title>购买图书</title> 4 </head> 5 <body> 6 购买《Servlet/JSP深入详解》<p> 7 <form action="trade" method="post"> 8 输入用户名: <input type="text" name="userid"><br> 9 输入购买数量:<input type="text" name="quantity"><p> 10 <input type="reset" value="重填"> 11 <input type="submit" value="购买"> 12 </form> 13 </body> 14 </html>
2 TradeServlet.java
1 package servlet; 2 import javax.servlet.*; 3 import java.io.*; 4 import javax.servlet.http.*; 5 import java.sql.*; 6 public class TradeServlet extends HttpServlet 7 { 8 private String url; 9 private String user; 10 private String password; 11 12 public void init() throws ServletException 13 { 14 ServletContext sc=getServletContext(); 15 String driverClass=sc.getInitParameter("driverClass"); 16 url=sc.getInitParameter("url"); 17 user=sc.getInitParameter("user"); 18 password=sc.getInitParameter("password"); 19 try 20 { 21 Class.forName(driverClass); 22 } 23 catch(ClassNotFoundException ce) 24 { 25 throw new ServletException("加载数据库驱动失败!"); 26 } 27 } 28 29 public void doGet(HttpServletRequest req, HttpServletResponse resp) 30 throws ServletException,IOException 31 { 32 Connection conn=null; 33 Statement stmt=null; 34 PreparedStatement pstmt=null; 35 ResultSet rs=null; 36 37 resp.setContentType("text/html;charset=gb2312"); 38 PrintWriter out=resp.getWriter(); 39 40 req.setCharacterEncoding("gb2312"); 41 42 String userid=req.getParameter("userid"); 43 String quantity=req.getParameter("quantity"); 44 45 if(null==userid || userid.equals("") || 46 null==quantity || quantity.equals("")) 47 { 48 49 out.println("错误的请求参数"); 50 out.close(); 51 } 52 else 53 { 54 try 55 { 56 conn=DriverManager.getConnection(url,user,password); 57 58 conn.setAutoCommit(false); 59 conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); 60 61 stmt=conn.createStatement(); 62 rs=stmt.executeQuery("select price,amount from bookinfo where id=3"); 63 rs.next(); 64 float price=rs.getFloat(1); 65 int amount=rs.getInt(2); 66 67 int num=Integer.parseInt(quantity); 68 if(amount>=num) 69 { 70 pstmt=conn.prepareStatement("update bookinfo set amount = ? where id = 3"); 71 pstmt.setInt(1,amount-num); 72 pstmt.executeUpdate(); 73 } 74 else 75 { 76 out.println("您所购买的图书库存数量不足。"); 77 out.close(); 78 return; 79 } 80 pstmt=conn.prepareStatement("select balance from account where userid = ?"); 81 pstmt.setString(1,userid); 82 rs=pstmt.executeQuery(); 83 84 rs.next(); 85 float balance=rs.getFloat(1); 86 87 float totalPrice=price*num; 88 89 if(balance>=totalPrice) 90 { 91 pstmt=conn.prepareStatement("update account set balance = ? where userid = ?"); 92 pstmt.setFloat(1,balance-totalPrice); 93 pstmt.setString(2,userid); 94 pstmt.executeUpdate(); 95 } 96 else 97 { 98 conn.rollback(); 99 out.println("您的余额不足。"); 100 out.close(); 101 return; 102 } 103 conn.commit(); 104 out.println("交易成功!"); 105 out.close(); 106 } 107 catch(SQLException se) 108 { 109 if(conn!=null) 110 { 111 try 112 { 113 conn.rollback(); 114 } 115 catch(SQLException ***) 116 { 117 ***.printStackTrace(); 118 } 119 } 120 se.printStackTrace(); 121 } 122 finally 123 { 124 if(rs!=null) 125 { 126 try 127 { 128 rs.close(); 129 } 130 catch(SQLException se) 131 { 132 se.printStackTrace(); 133 } 134 rs=null; 135 } 136 if(stmt!=null) 137 { 138 try 139 { 140 stmt.close(); 141 } 142 catch(SQLException se) 143 { 144 se.printStackTrace(); 145 } 146 stmt=null; 147 } 148 if(pstmt!=null) 149 { 150 try 151 { 152 pstmt.close(); 153 } 154 catch(SQLException se) 155 { 156 se.printStackTrace(); 157 } 158 pstmt=null; 159 } 160 if(conn!=null) 161 { 162 try 163 { 164 conn.close(); 165 } 166 catch(SQLException se) 167 { 168 se.printStackTrace(); 169 } 170 conn=null; 171 } 172 } 173 } 174 } 175 176 public void doPost(HttpServletRequest req, HttpServletResponse resp) 177 throws ServletException,IOException 178 { 179 doGet(req,resp); 180 } 181 }
1 44、45行 调用请求对象的getParameter()方法得到用户名和购买图书的数量
2 60行 调用Connection对象的setAutoCommit()方法 传递 false参数 取消自动提交
3 61行 调用Connection对象的setTransactionIsolation()方法设置事务的隔离等级为Repeatable Read
4 99行 如果用户的余额不足 那么这次交易失败 调用Connection的rollback()方法,回到交易开始之前的状态,也就是回到bookinfo表中书的书目没发生改变的时候
注意: 如果在调用rollback()方法之前调用了commit()方法,那么只能回滚到上一次调用commit()方法之后所作的改变
5 104行 若果所有的操作都成功了 调用Connection对象的commit()方法提交事务,也就是向数据库提交所有的改变
6 在交易过程中,若果发生了异常 那么就在114行 调用Connection对象的rollback()方法回滚所有的改变
上面这个servlet用到了两种方式保证交易的正常进行
1 利用异常处理机制 一旦交易过程发生异常 就取消所有的改变
2 在交易的业务逻辑中进行判断 当用户的账户金额小于购买金额的时候 就取消所作的改变