买书的例子 程序应该将图书数量的操作和更新account用户余额的操作作为一个事务来处理,只有这两个操作都完成的情况下,才能提交事务,否则就回滚事务。
1 buy.html
<html>
<head>
<title>购买图书</title>
</head>
<body>
购买《Servlet/JSP深入详解》<p>
<formaction="trade"method="post">
输入用户名: <inputtype="text"name="userid"><br>
输入购买数量:<inputtype="text"name="quantity"><p>
<inputtype="reset"value="重填">
<inputtype="submit"value="购买">
</form>
</body>
</html>
2 TradeServlet.java
package servlet;
import javax.servlet.*;
import java.io.*;
import javax.servlet.http.*;
import java.sql.*;
public class TradeServletextends HttpServlet
{
private Stringurl;
private String user;
private String password;
public voidinit()throws ServletException
{
ServletContext sc=getServletContext();
String driverClass=sc.getInitParameter("driverClass");
url=sc.getInitParameter("url");
user=sc.getInitParameter("user");
password=sc.getInitParameter("password");
try
{
Class.forName(driverClass);
}
catch(ClassNotFoundException ce)
{
throw new ServletException("加载数据库驱动失败!");
}
}
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException,IOException
{
Connection conn=null;
Statement stmt=null;
PreparedStatement pstmt=null;
ResultSet rs=null;
resp.setContentType("text/html;charset=gb2312");
PrintWriter out=resp.getWriter();
req.setCharacterEncoding("gb2312");
String userid=req.getParameter("userid");
String quantity=req.getParameter("quantity");
if(null==userid|| userid.equals("")||
null==quantity|| quantity.equals(""))
{
out.println("错误的请求参数");
out.close();
}
else
{
try
{
conn=DriverManager.getConnection(url,user,password);
conn.setAutoCommit(false);
conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
stmt=conn.createStatement();
rs=stmt.executeQuery("select price,amount from bookinfo where id=3");
rs.next();
float price=rs.getFloat(1);
int amount=rs.getInt(2);
int num=Integer.parseInt(quantity);
if(amount>=num)
{
pstmt=conn.prepareStatement("update bookinfo set amount = ? where id = 3");
pstmt.setInt(1,amount-num);
pstmt.executeUpdate();
}
else
{
out.println("您所购买的图书库存数量不足。");
out.close();
return;
}
pstmt=conn.prepareStatement("select balance from account where userid = ?");
pstmt.setString(1,userid);
rs=pstmt.executeQuery();
rs.next();
float balance=rs.getFloat(1);
float totalPrice=price*num;
if(balance>=totalPrice)
{
pstmt=conn.prepareStatement("update account set balance = ? where userid = ?");
pstmt.setFloat(1,balance-totalPrice);
pstmt.setString(2,userid);
pstmt.executeUpdate();
}
else
{
conn.rollback();
out.println("您的余额不足。");
out.close();
return;
}
conn.commit();
out.println("交易成功!");
out.close();
}
catch(SQLException se)
{
if(conn!=null)
{
try
{
conn.rollback();
}
catch(SQLException ***)
{
***.printStackTrace();
}
}
se.printStackTrace();
}
finally
{
if(rs!=null)
{
try
{
rs.close();
}
catch(SQLException se)
{
se.printStackTrace();
}
rs=null;
}
if(stmt!=null)
{
try
{
stmt.close();
}
catch(SQLException se)
{
se.printStackTrace();
}
stmt=null;
}
if(pstmt!=null)
{
try
{
pstmt.close();
}
catch(SQLException se)
{
se.printStackTrace();
}
pstmt=null;
}
if(conn!=null)
{
try
{
conn.close();
}
catch(SQLException se)
{
se.printStackTrace();
}
conn=null;
}
}
}
}
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException,IOException
{
doGet(req,resp);
}
}
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 在交易的业务逻辑中进行判断 当用户的账户金额小于购买金额的时候 就取消所作的改变