需求:

对商品秒杀,每个用户只能对一个商品买一次

实现思路:

设计三个表,商品详情表,商品总数表,用户订单表

插入订单表和更新总数表放到一个事务里边,更新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 
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条不重复的用户订单记录,说明只有独立的三个人抢购成功。