大数据正式25 淡淡的烟草味﹌ 2022-06-03 02:41 242阅读 0赞 # 大数据正式25 # * EasyMall商品删除模块 * 设计 1. 在商品列表界面的商品后边添加删除按钮 2. 点击删除按钮,然后访问ManageDelProdServlet 3. 调用Service删除相应的产品 4. 调用dao层真正的删除 * 可能出现的问题---【【更新丢失】】 1. 两个管理员删除同一种类的最后两个商品【商品可以删除,但是种类没删除掉】 2. 原因:分别进入的事务判断的都是对的(>1),然后各自删完商品后,种类应该随着删除掉,但是两次都是因为大于1进的,所以这个地方有纰漏 3. 问题的本质:两个并发的事务,基于同一个查询结果进行更新操作,然后出现没有更新的现象 4. 图解 * ![jjDJvtY.png][] 5. 例子 1. 秒杀抢购 * 当同一时刻有大量的用户抢购时,抢购事务将会各自执行,但是数据库的更新会出现乱子 * 图解 * ![YwpaXq2.png][] 2. 重复充值 * 网络中断或延迟:银行反复向服务器发送确认请求,当多次的请求同时开启事务时,将会发生更新丢失现象(相同的加钱操作执行了两次)。 * 图解 * ![WZjxLwB.png][] 6. 解决方案 1. 将数据库的隔离级别设置为Serializable【产生死锁-->>结束一方,执行另一方】 2. 悲观锁解决方案 1. 数据库中不存在这个锁----这是解决方案的名称 2. 每次查询认为比较“悲观”添加排他锁 # # select * from table for update;#排它锁for update 3. 特点:排斥其他事务 4. 应用场景:更新的,查询少 3. 乐观锁解决方案 1. 数据库中不存在这个锁----这是解决方案的名称 2. 乐观的认为,每次查询都不会造成更新的丢失,检测更新的丢失来进行相应的处理 # # 1. 得到预料的值:select 2. select * from table and 检测的字段=预料的值;#乐观锁(判断理想中的条件是否成立) 3. 判断是否要回滚操作 3. 特点:可能造成一个事务循环【当自己的事务进行时又来一个事务】 4. 应用场景:查询的,更新少 4. EasyMall的解决方案 1. 问题点:查询该种类的剩余商品 2. 解决:查询该种类的剩余商品的时候添加悲观锁 # # public List<Prod> findProdsByCategoryId(int category_id) { List<Prod> prod_list = new ArrayList<Prod>(); try { // 查询数据库,并把每一条记录包装成Prod对象存入prod_list列表中 conn = MySqlUtils.getConn(); stat = conn .prepareStatement("select * from prod inner join prod_category where prod.prodCategory_id=prod_category.id and prod_category.id=? for update");//悲观锁解决方案(for update) stat.setInt(1, category_id); rs = stat.executeQuery(); while (rs.next()) { Prod prod = new Prod(); prod.setId(rs.getInt(1)); prod.setName(rs.getString(2)); prod.setPnum(rs.getInt(4)); prod.setDescription(rs.getString(7)); prod.setImgurl(rs.getString(5)); prod.setPrice(rs.getDouble(3)); prod.setProdCategory_id(rs.getInt(6)); prod.setProdCategory(new ProdCategory(rs.getString(8), rs .getString(9))); prod_list.add(prod); } } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } finally { MySqlUtils.close(null, stat, rs); } return prod_list; } ### 商品删除具体实现 ### * 效果图展示 * ![6JjHXoS.png][] * 代码 * 页面 * manageProdList.jsp # # <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!DOCTYPE HTML> <html> <head> <style type="text/css"> body { text-align: center; } table { text-align: center; } th { background-color: silver; } </style> <script type="text/javascript" src="../js/jquery-1.4.2.js"></script> <script type="text/javascript"> function updateNum(obj,id){ $.get("../ManageUpdatePnumServlet", { "id": id, "pnum": obj.value }, function(data){ alert("更新商品总数成功!!现在的个数为:" + data); }); } </script> </head> <body> <h1>商品管理</h1> <a href="${pageContext.request.contextPath }/backend/manageAddProd.jsp">添加商品</a> <hr> <table align="center" bordercolor="black" border="1" width="90%" cellspacing="0px" cellpadding="5px"> <tr> <th>商品图片</th> <th>商品id</th> <th>商品名称</th> <th>商品种类</th> <th>商品单价</th> <th>库存数量</th> <th>描述信息</th> <th>删除</th> </tr> <c:forEach items="${requestScope.prod_lists }" var="prod"> <tr> <td><img width="120px" height="120px" src="../ProdImgServlet?imgurl=${prod.imgurl}" /></td> <td>${prod.id }</td> <td>${prod.name }</td> <td>${prod.prodCategory.name }</td> <td>${prod.price }</td> <td><input type="text" value="${prod.pnum }" onblur="updateNum(this,${prod.id});" /></td> <td>${prod.description }</td> <td><a href="../ManageProdDelServlet?id=${prod.id}">删除</a></td> </tr> </c:forEach> </table> </body> </html> * web * ManageProdDelServlet # # package com.easymall.web; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.easymall.factory.BasicFactory; import com.easymall.service.ProdService; @SuppressWarnings("serial") public class ManageProdDelServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 获取参数--商品的id int id = Integer.parseInt(request.getParameter("id")); // 通过id 删除对应的商品 ProdService service = BasicFactory.getFactory().getObj( ProdService.class); service.delProd(id); // 返回商品列表界面 response.getWriter().write("删除成功!即将返回。。。"); response.setHeader("refresh", "2;url=ManagerProdListServlet"); } } * service * ProdService # # package com.easymall.service; import java.util.List; import com.easymall.domain.Prod; public interface ProdService { /** * 添加一个产品 * * @param prod */ public void addProd(Prod prod); /** * 查询所有的商品 * * @return 查询所有的商品 */ public List<Prod> findProdList(); /** * 通过id更新商品的pnum * * @param id * @param pnum */ public void upDatePunm(int id, int pnum); /** * 通过id删除相应的产品 * @param id */ public void delProd(int id); } * ProdServiceImpl # # package com.easymall.service; import java.util.List; import com.easymall.dao.ProdDao; import com.easymall.domain.Prod; import com.easymall.domain.ProdCategory; import com.easymall.factory.BasicFactory; import com.easymall.utils.TransactionManager; public class ProdServiceImpl implements ProdService { private ProdDao prodDao = BasicFactory.getFactory().getObj(ProdDao.class); public void addProd(Prod prod) { try { // 开启事务 TransactionManager.StartTransaction(); String category_id = "-666"; // 检查商品种类是否存在 ProdCategory category = prodDao.findProdCategoryByCategoryName(prod .getProdCategory().getName()); if (category == null) { // 商品种类不存在--则先添加商品种类,然后添加商品 category = prodDao .addCategory(prod.getProdCategory().getName());// 返回值的作用是为了后期容易得到商品种类的ID值 } // 获取商品种类的ID category_id = category.getId(); // 商品种类存在,添加商品 prodDao.addProd(prod, category_id); // 提交事务 TransactionManager.CommitTransaction(); // 关闭连接 TransactionManager.realse(); } catch (Exception e) { e.printStackTrace(); // 事务回滚 TransactionManager.RollbackTransaction(); System.out.println("back"); throw new RuntimeException(e); } } public List<Prod> findProdList() { return prodDao.findProdList(); } public void upDatePunm(int id, int pnum) { prodDao.upDatePnum(id, pnum); } public void delProd(int id) { try { TransactionManager.StartTransaction();// 开启事务 // 根据商品信息id查找对应的产品信息 Prod prod = prodDao.findProdById(id); // 获取种类 int category_id = prod.getProdCategory_id(); // 根据种类获取该种类的剩余产品 List<Prod> list_prod = prodDao.findProdsByCategoryId(category_id); if (list_prod.size() > 1) { // 如果大于1则只删除当前商品即可 prodDao.delProdById(id); } else if (list_prod.size() == 1) { // 如果为1的话,则将该商品和该种类都删除(级联删除) prodDao.delProdById(id); prodDao.delCategoryByCategoryId(category_id); } TransactionManager.CommitTransaction();// 提交事务 } catch (Exception e) { e.printStackTrace(); TransactionManager.RollbackTransaction();// 回滚事务 throw new RuntimeException(e); } finally { // 释放资源 TransactionManager.realse(); } } } * dao * ProdDao # # package com.easymall.dao; import java.util.List; import com.easymall.domain.Prod; import com.easymall.domain.ProdCategory; public interface ProdDao { /** * 通过商品种类名称查询数据库是否有此种类 * * @param string * @return 找到了? 商品种类的对象:null; */ ProdCategory findProdCategoryByCategoryName(String string); /** * 添加一个商品种类 * * @param category * @return 添加成功对应的商品种类 */ ProdCategory addCategory(String category_name); /** * 添加商品 * * @param prod * 商品 * @param category_id * 商品种类的ID * @return void */ void addProd(Prod prod, String category_id); /** * 查询所有商品 * * @return 商品列表 */ List<Prod> findProdList(); /** * 通过id更新产品的pnum数据 * * @param id * @param pnum */ void upDatePnum(int id, int pnum); /** * 通过产品id查找对应的商品 * * @param id * @return 商品信息 */ Prod findProdById(int id); /** * 通过中了的id查找对应的所有商品 * * @param category_id * @return 该种类对应的所有商品 ---list */ List<Prod> findProdsByCategoryId(int category_id); /** * 通过商品的id删除商品 * * @param id */ void delProdById(int id); /** * 通过商品种类的id删除改种类 * * @param category_id */ void delCategoryByCategoryId(int category_id); } * ProdDaoImpl # # package com.easymall.dao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; import java.util.List; import com.easymall.domain.Prod; import com.easymall.domain.ProdCategory; import com.easymall.utils.MySqlUtils; import com.easymall.utils.TransactionManager; public class ProdDaoImpl implements ProdDao { Connection conn = null; PreparedStatement stat = null; ResultSet rs = null; public ProdCategory findProdCategoryByCategoryName(String name) { try { ProdCategory pc = null; conn = TransactionManager.getConn(); stat = conn .prepareStatement("select * from prod_category where name=?"); stat.setString(1, name); rs = stat.executeQuery(); while (rs.next()) {// 一个对象这里才对----------嘻嘻 pc = new ProdCategory(); pc.setId(rs.getString("id")); pc.setName(rs.getString("name")); } return pc; } catch (Exception e) { throw new RuntimeException(e); } finally { MySqlUtils.close(null, stat, rs); } } public ProdCategory addCategory(String category_name) { try { conn = TransactionManager.getConn(); conn.setAutoCommit(false);// 设置不自动提交 stat = conn .prepareStatement("insert into prod_category(name) values(?)"); stat.setString(1, category_name); stat.executeUpdate(); // 通过得到刚存入的数据得到相应的id来返回ProdCategory对象 ProdCategory pc = null; stat = conn .prepareStatement("select * from prod_category where name=? "); stat.setString(1, category_name); rs = stat.executeQuery(); while (rs.next()) { pc = new ProdCategory(); pc.setId(rs.getString("id")); pc.setName(rs.getString("name")); } return pc; } catch (Exception e) { throw new RuntimeException(e); } finally { MySqlUtils.close(null, stat, rs); } } public void addProd(Prod prod, String category_id) { try { conn = TransactionManager.getConn(); stat = conn .prepareStatement("insert into prod(name,price,pnum,img_url,description,prodCategory_id) values(?,?,?,?,?,?)"); stat.setString(1, prod.getName()); stat.setDouble(2, prod.getPrice()); stat.setInt(3, prod.getPnum()); stat.setString(4, prod.getImgurl()); stat.setString(5, prod.getDescription()); stat.setInt(6, Integer.parseInt(category_id)); stat.executeUpdate(); } catch (Exception e) { throw new RuntimeException(e); } finally { MySqlUtils.close(null, stat, rs); } } public List<Prod> findProdList() { List<Prod> prod_list = new ArrayList<Prod>(); try { // 查询数据库,并把每一条记录包装成Prod对象存入prod_list列表中 conn = MySqlUtils.getConn(); stat = conn .prepareStatement("select * from prod inner join prod_category where prod.prodCategory_id=prod_category.id"); rs = stat.executeQuery(); while (rs.next()) { Prod prod = new Prod(); prod.setId(rs.getInt(1)); prod.setName(rs.getString(2)); prod.setPnum(rs.getInt(4)); prod.setDescription(rs.getString(7)); prod.setImgurl(rs.getString(5)); prod.setPrice(rs.getDouble(3)); prod.setProdCategory_id(rs.getInt(6)); prod.setProdCategory(new ProdCategory(rs.getString(8), rs .getString(9))); prod_list.add(prod); } } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } finally { MySqlUtils.close(conn, stat, rs); } return prod_list; } public void upDatePnum(int id, int pnum) { try { // 通过id更新产品的pnum conn = MySqlUtils.getConn(); stat = conn.prepareStatement("update prod set pnum=? where id=? "); stat.setInt(1, pnum); stat.setInt(2, id); stat.executeUpdate(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } public Prod findProdById(int id) { try { Prod prod = null; conn = TransactionManager.getConn(); stat = conn .prepareStatement("select * from prod inner join prod_category where prod.prodCategory_id=prod_category.id and prod.id=?"); stat.setInt(1, id); rs = stat.executeQuery(); while (rs.next()) { prod = new Prod(); prod.setId(rs.getInt(1)); prod.setName(rs.getString(2)); prod.setPnum(rs.getInt(4)); prod.setDescription(rs.getString(7)); prod.setImgurl(rs.getString(5)); prod.setPrice(rs.getDouble(3)); prod.setProdCategory_id(rs.getInt(6)); prod.setProdCategory(new ProdCategory(rs.getString(8), rs .getString(9))); } return prod; } catch (Exception e) { throw new RuntimeException(e); } finally { MySqlUtils.close(null, stat, rs); } } public List<Prod> findProdsByCategoryId(int category_id) { List<Prod> prod_list = new ArrayList<Prod>(); try { // 查询数据库,并把每一条记录包装成Prod对象存入prod_list列表中 conn = MySqlUtils.getConn(); stat = conn .prepareStatement("select * from prod inner join prod_category where prod.prodCategory_id=prod_category.id and prod_category.id=? for update");// 悲观锁解决更新丢失 stat.setInt(1, category_id); rs = stat.executeQuery(); while (rs.next()) { Prod prod = new Prod(); prod.setId(rs.getInt(1)); prod.setName(rs.getString(2)); prod.setPnum(rs.getInt(4)); prod.setDescription(rs.getString(7)); prod.setImgurl(rs.getString(5)); prod.setPrice(rs.getDouble(3)); prod.setProdCategory_id(rs.getInt(6)); prod.setProdCategory(new ProdCategory(rs.getString(8), rs .getString(9))); prod_list.add(prod); } } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } finally { MySqlUtils.close(null, stat, rs); } return prod_list; } public void delProdById(int id) { try { conn = TransactionManager.getConn(); stat = conn.prepareStatement("delete from prod where id=?"); stat.setInt(1, id); stat.executeUpdate(); } catch (Exception e) { throw new RuntimeException(e); } finally { MySqlUtils.close(null, stat, rs); } } public void delCategoryByCategoryId(int category_id) { try { conn = TransactionManager.getConn(); stat = conn .prepareStatement("delete from prod_category where id=?"); stat.setInt(1, category_id); stat.executeUpdate(); } catch (Exception e) { throw new RuntimeException(e); } finally { MySqlUtils.close(null, stat, rs); } } } ### 再次谈谈缺点 ### * 异常的处理【各个地方】 * 页面的数据判别处理【特指商品添加模块】 [jjDJvtY.png]: https://i.imgur.com/jjDJvtY.png [YwpaXq2.png]: https://i.imgur.com/YwpaXq2.png [WZjxLwB.png]: https://i.imgur.com/WZjxLwB.png [6JjHXoS.png]: https://i.imgur.com/6JjHXoS.png
相关 大数据正式5 大数据正式5 常见的shell命令 管道命令 管道符| 将两个命令隔开,左边命令的输出就会作为管道右边命令的输入 连续使 旧城等待,/ 2022年06月06日 10:29/ 0 赞/ 233 阅读
相关 大数据正式2 大数据正式2 用户身份与用户组记录的文件 在Linux系统当中,默认情况下所有的系统上的账号信息都记录在/etc/passwd这个文件内(包括root用户), 快来打我*/ 2022年06月06日 08:38/ 0 赞/ 161 阅读
相关 大数据正式10 大数据正式10 jQuery 定义:jQuery是一个“写的更少”,但“做的更多”的轻量级JavaScript函数库 优势 1. 可 ゞ 浴缸里的玫瑰/ 2022年06月05日 06:24/ 0 赞/ 246 阅读
相关 大数据正式32 大数据正式32 Spring中的JDBC jar包准备 ![zW1gEQQ.png][] bean+properties普通配置 悠悠/ 2022年06月03日 08:44/ 0 赞/ 165 阅读
相关 大数据正式27 大数据正式27 Spring 先来张图简单看一下 ![oQySJMC.png][] spring框架的特点 1 悠悠/ 2022年06月03日 04:38/ 0 赞/ 143 阅读
相关 大数据正式37 大数据正式37 Maven 传统项目存在的弊端 1. 导入jar包得经验丰富 2. 传统项目打包方式不通用,不能很好的支持聚合项 左手的ㄟ右手/ 2022年06月02日 01:46/ 0 赞/ 155 阅读
相关 大数据正式36 大数据正式36 MyBatis的接口形式 注意两点 1. 接口名---namespace值对应 2. 方法名---id一致 淩亂°似流年/ 2022年06月02日 01:12/ 0 赞/ 260 阅读
相关 大数据正式34 大数据正式34 Spring+SpringMVC 小例子 效果图 ![hsIEQmd.png][] 功能说明 川长思鸟来/ 2022年06月02日 00:16/ 0 赞/ 272 阅读
还没有评论,来说两句吧...