需求:
对商品秒杀,每个用户只能对一个商品买一次
实现思路:
设计三个表,商品详情表,商品总数表,用户订单表
插入订单表和更新总数表放到一个事务里边,更新j库存总数时,受影响行数小于等于0的时候,事务回滚;
对订单表的(user_id,goods_id)加唯一索引,一个用户只能买一笔商品,如果插入的时候报错,事务回滚;
//开启事务
//更新库存数量
update goods set goods_num=goods_num-1 where goods_id=$goods_id and goods_num>0 ;if(受影响行数<=0) 抛异常,商品秒完了,回滚。。。
//插入订单insert into goods_order。。。if(唯一索引报错?) 抛异常,已经秒过了,回滚。。。//最后提交事务
数据库设计:
商品详情表:
DROP TABLE IF EXISTS `goods`;CREATE TABLE `goods` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键', `goods_name` varchar(50) NOT NULL, `add_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='商品详情表';-- ------------------------------ Records of goods-- ----------------------------INSERT INTO `goods` VALUES ('1', '笔记本', '2016-04-27 10:24:48');
商品总数表:
DROP TABLE IF EXISTS `goods_num`;CREATE TABLE `goods_num` ( `id` int(11) NOT NULL AUTO_INCREMENT, `goods_id` int(11) NOT NULL COMMENT '货品ID', `goods_num` int(11) NOT NULL COMMENT '商品总数', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='商品总数表';-- ------------------------------ Records of goods_num-- ----------------------------INSERT INTO `goods_num` VALUES ('1', '1', '3');
用户订单表:
DROP TABLE IF EXISTS `goods_order`;CREATE TABLE `goods_order` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '订单ID', `goods_id` int(11) NOT NULL COMMENT '商品ID', `user_id` int(11) NOT NULL COMMENT '用户ID', PRIMARY KEY (`id`), UNIQUE KEY `good_user` (`goods_id`,`user_id`)) ENGINE=InnoDB AUTO_INCREMENT=409 DEFAULT CHARSET=utf8 COMMENT='订单表';
具体实现代码:
package class10;import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.SQLException;import java.sql.Statement;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class SnapUp { /** * * 功能说明:数据库连接 * * @return * */ private static Connection getConn() { String driver = "com.mysql.jdbc.Driver"; String url = "jdbc:mysql://localhost:3306/study"; String username = "root"; String password = "123456"; Connection conn = null; try { Class.forName(driver); //classLoader,加载对应驱动 conn = (Connection) DriverManager.getConnection(url, username, password); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } return conn; } /** * 功能说明:秒杀抢购 * @param goodsId 商品ID * @param userId 用户ID * */ public static void snapUp(int goodsId, int userId) { Connection conn = getConn(); Statement stmt = null; PreparedStatement pst = null; try { stmt = conn.createStatement(); //首先,.设置事务的提交方式为非自动提交 conn.setAutoCommit(false); //更新库存 String sqlUpdate = "update goods_num set goods_num=goods_num-1 where goods_id=" + goodsId + " and goods_num>0"; pst = conn.prepareStatement(sqlUpdate); int count = pst.executeUpdate(); if (count <= 0) { System.out.println("货物已经被抢空了,明天再来吧"); throw new RuntimeException(); } // 插入订单 String sqlOrder = "insert into goods_order(goods_id,user_id)values(?,?)"; pst = conn.prepareStatement(sqlOrder); pst.setInt(1, goodsId); pst.setInt(2, userId); pst.executeUpdate(); System.out.println("用户:" + userId + "抢购货物:" + goodsId + "成功!"); conn.commit(); } catch (Exception e) { System.out.println("用户:" + userId + "抢购货物:" + goodsId + "失败!"); try { //.在catch块内添加回滚事务,表示操作出现异常,撤销事务: conn.rollback(); } catch (SQLException e1) { //e1.printStackTrace(); } //e.printStackTrace(); } finally { try { //设置事务提交方式为自动提交 conn.setAutoCommit(true); stmt.close(); conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } public static void main(String[] args) { // 线程池 ExecutorService exec = Executors.newCachedThreadPool(); // 模拟50个客户端访问 for (int index = 0; index < 50; index++) { final int userId = index / 9; Runnable run = new Runnable() { public void run() { snapUp(1, userId); } }; exec.execute(run); } // 退出线程池 exec.shutdown(); }}
结果:
用户:4抢购货物:1成功!用户:3抢购货物:1成功!用户:1抢购货物:1成功!用户:5抢购货物:1失败!用户:5抢购货物:1失败!用户:3抢购货物:1失败!用户:1抢购货物:1失败!用户:1抢购货物:1失败!用户:4抢购货物:1失败!用户:3抢购货物:1失败!用户:1抢购货物:1失败!用户:1抢购货物:1失败!用户:1抢购货物:1失败!用户:3抢购货物:1失败!用户:1抢购货物:1失败!用户:1抢购货物:1失败!用户:3抢购货物:1失败!用户:3抢购货物:1失败!用户:3抢购货物:1失败!用户:4抢购货物:1失败!用户:1抢购货物:1失败!用户:4抢购货物:1失败!用户:3抢购货物:1失败!用户:4抢购货物:1失败!用户:3抢购货物:1失败!用户:4抢购货物:1失败!用户:4抢购货物:1失败!用户:4抢购货物:1失败!用户:0抢购货物:1失败!用户:4抢购货物:1失败!用户:2抢购货物:1失败!用户:2抢购货物:1失败!用户:0抢购货物:1失败!用户:2抢购货物:1失败!用户:0抢购货物:1失败!用户:2抢购货物:1失败!用户:0抢购货物:1失败!用户:2抢购货物:1失败!用户:0抢购货物:1失败!用户:0抢购货物:1失败!用户:2抢购货物:1失败!用户:0抢购货物:1失败!用户:0抢购货物:1失败!用户:0抢购货物:1失败!用户:2抢购货物:1失败!用户:2抢购货物:1失败!用户:2抢购货物:1失败!用户:5抢购货物:1失败!用户:5抢购货物:1失败!用户:5抢购货物:1失败!
最后查看goods_num表,总数变为0, goods_order里边有3条不重复的用户订单记录,说明只有独立的三个人抢购成功。
借鉴了http://www.tuicool.com/articles/Bfa63e6