1.创建数据表
1.使用use命令先选中store数据库
USE store;
2.在store数据库中创建t_cart用户数据表
CREATE TABLE t_cart (
cid INT AUTO_INCREMENT COMMENT '购物车数据id',
uid INT NOT NULL COMMENT '用户id',
pid INT NOT NULL COMMENT '商品id',
price BIGINT COMMENT '加入时商品单价',
num INT COMMENT '商品数量',
created_user VARCHAR(20) COMMENT '创建人',
created_time DATETIME COMMENT '创建时间',
modified_user VARCHAR(20) COMMENT '修改人',
modified_time DATETIME COMMENT '修改时间',
PRIMARY KEY (cid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2.创建购物车的实体类
在entity包下创建购物车的Cart实体类并使其继承BaseEntity
/**购物车数据的实体类*/
public class Cart extends BaseEntity {
private Integer cid;
private Integer uid;
private Integer pid;
private Long price;
private Integer num;
/**
* get,set
* equals和hashCode
* toString
*/
}
3.持久层[Mapper]
规划需要执行的SQL语句
1.向购物车表中插入商品数据的SQL语句
insert into t_cart (除了cid以外的所有字段) values (匹配的值列表);
2.如果当前商品已经在购物车存在,则直接更新商品即可
update t_cart set num=? where cid=?
3.在插入或者更新具体执行哪个语句,取决于数据库中是否有当前的这个购物车商品的数据,需要查询语句才能确定
select * from t_cart where uid=? and pid=?
2设计接口和抽象方法
在mapper包下创建CartMapper接口,并添加抽象方法
public interface CartMapper {
/**
* 插入购物车数据
* @param cart 购物车数据
* @return 受影响的行数
*/
Integer insert(Cart cart);
/**
* 修改购物车数据中商品的数量
* @param cid 购物车数据的id
* @param num 新的数量
* @param modifiedUser 修改执行人
* @param modifiedTime 修改时间
* @return 受影响的行数
*/
Integer updateNumByCid(
@Param("cid") Integer cid,
@Param("num") Integer num,
@Param("modifiedUser") String modifiedUser,
@Param("modifiedTime") Date modifiedTime);
/**
* 根据用户id和商品id查询购物车中的数据
* @param uid 用户id
* @param pid 商品id
* @return 匹配的购物车数据,如果该用户的购物车中并没有该商品,则返回null
*/
Cart findByUidAndPid(
@Param("uid") Integer uid,
@Param("pid") Integer pid);
}
3编写映射
在resources.mapper文件夹下创建CartMapper.xml文件,并在文件中配置以上三个方法的映射
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cy.store.mapper.CartMapper">
<resultMap id="CartEntityMap" type="com.cy.store.entity.Cart">
<id column="cid" property="cid"/>
<result column="created_user" property="createdUser"/>
<result column="created_time" property="createdTime"/>
<result column="modified_user" property="modifiedUser"/>
<result column="modified_time" property="modifiedTime"/>
</resultMap>
<!-- 插入购物车数据-->
<insert id="insert" useGeneratedKeys="true" keyProperty="cid">
insert into t_cart (uid, pid, price, num, created_user, created_time, modified_user, modified_time)
values (#{uid}, #{pid}, #{price}, #{num}, #{createdUser}, #{createdTime}, #{modifiedUser}, #{modifiedTime})
</insert>
<!-- 修改购物车数据中商品的数量-->
<update id="updateNumByCid">
update t_cart set
num=#{num},
modified_user=#{modifiedUser},
modified_time=#{modifiedTime}
where cid=#{cid}
</update>
<!-- 根据用户id和商品id查询购物车中的数据-->
<select id="findByUidAndPid" resultMap="CartEntityMap">
select * from t_cart where uid=#{uid} AND pid=#{pid}
</select>
</mapper>
4单元测试
创建CartMapperTests测试类进行测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class CartMapperTests {
@Autowired
private CartMapper cartMapper;
@Test
public void insert() {
Cart cart = new Cart();
cart.setUid(11);
cart.setPid(10000001);
cart.setNum(3);
cart.setPrice(4L);//长整型
cartMapper.insert(cart);
}
@Test
public void updateNumByCid() {
cartMapper.updateNumByCid(1, 4, "张三", new Date());
}
@Test
public void findByUidAndPid() {
Cart cart = cartMapper.findByUidAndPid(11, 10000001);
System.out.println(cart);
}
}
4.业务层[Service]
1规划异常
在插入数据时,可能抛出InsertException异常;在修改数据时,可能抛出UpdateException异常.这两个异常已开发
2设计接口和抽象方法及实现
1.在com.cy.store.service包下创建ICartService接口,并添加抽象方法
该抽象方法都需要哪些参数呢,还是依据持久层,看持久层三条sql语句的实现需要什么参数:
findByUidAndPid:查询购物车数据,参数是uid,pid
insert:插入购物车数据,参数是cart对象(属性有cid,uid,pid,price,num)
updateNumByCid:修改购物车中商品数量,参数是cid,num,modifiedUser,modifiedTime
price可以通过业务层中调用ProductMapper接口的findById获取,modifiedTime在业务层实现类的内部创建,所以需要的参数是uid,pid,num,username
经过这次分析结合以前给业务层方法声明参数,可以发现即使持久层的方法参数是实体类对象,业务层的方法参数也大多不是实体类对象,因为实体类的部分属性是可以在业务层进行拼接然后封装到实体类对象中,再传给持久层(比如这里的price),这样的话就降低了前端传递数据的压力,如果该对象的所有方法都必须由前端传递过来,那么业务层方法参数可以是实体类对象(如注册用户时业务层的方法参数就是User对象)
public interface ICartService {
/**
* 将商品添加到购物车
* @param uid 当前登录用户的id
* @param pid 商品的id
* @param amount 增加的数量
* @param username 当前登录的用户名
*/
void addToCart(Integer uid, Integer pid, Integer amount, String username);
}
2.创建CartServiceImpl类,并实现ICartService接口.在类中声明CartMapper持久层对象和IProductService处理商品数据的业务对象,并实现业务层的抽象方法
@Service
public class ICartServiceImpl implements ICartService {
/*
由于购物车的业务依赖于购物车和商品的持久层,若通过session获取数据
*/
@Autowired
private CartMapper cartMapper;
@Autowired
private ProductMapper productMapper;
@Override
public void addToCart(Integer uid, Integer pid, Integer amount, String username) {
//查询购物车是否已经存在【查看用户之前是否将该商品加入过购物车】
Cart result = cartMapper.findByUidAndPid(uid, pid);
Date date = new Date();
if(result == null) {//新增商品
Cart cart = new Cart();
//封装数据:uid,pid,amount
cart.setUid(uid);
cart.setPid(pid);
cart.setNum(amount);
//查询商品数据,得到商品价格进行封装
Product product = productMapper.findById(pid);
cart.setPrice(product.getPrice());
//封装数据:4个日志
cart.setCreatedUser(username);
cart.setCreatedTime(date);
cart.setModifiedUser(username);
cart.setModifiedTime(date);
Integer rows = cartMapper.insert(cart);
if(rows != 1) {
throw new InsertException("未知异常 在 插入数据");
}
} else {//更新num值
//从查询结果中取出原数量,与参数amount相加,得到新的数量
Integer num = result.getNum() + amount;//加入购物车时只会有+不可能有-
Integer rows = cartMapper.updateNumByCid(result.getCid(), num, username, date);
if(rows != 1) {
throw new UpdateException("未知异常 在 更新数据");
}
}
}
}
3单元测试
创建测试类CartServiceTests并编写测试方法。
@RunWith(SpringRunner.class)
@SpringBootTest
public class CartServiceTests {
@Autowired
private ICartService cartService;
@Test
public void addToCart() {
cartService.addToCart(11, 10000002, 5, "Tom");
}
}
5.控制层[Contrroller]
1处理异常
InsertException异常和UpdateException异常都已经设置到BaseController类中了,这里无需重复开发
2设计请求
- /carts/add_to_cart
- post
- Integer pid, Integer amount, HttpSession session
- JsonResult<Void>
3处理请求
在controller包下创建CartController类并继承BaseController类,在类中添加处理请求的addToCart()方法
@RestController
@RequestMapping("carts")
public class CartController extends BaseController {
@Autowired
private ICartService cartService;
@RequestMapping("add_to_cart")
public JsonResult<Void> addToCart(Integer pid, Integer amount, HttpSession session) {
cartService.addToCart(
getUidFromSession(session),
pid,
amount,
getUsernameFromSession(session));
return new JsonResult<Void>(OK);
}
}
6.前端页面
在product.html页面中的body标签内的script标签里为“加入购物车”按钮添加点击事件
回顾一下在ajax函数中data参数的数据设置的方式
- data:$(“选择的form表单”).serialize()。当需要提交的参数过多并且在同一个表单中时使用
- data:new FormData($(“选择的form表单”)[0])。只适用提交文件
- data:“username=TOM”。手动拼接,适合参数值固定并且参数值列表有限.等同于
var user = "控件某属性值或控件文本内容或自己声明的值"
data: "username="+user
- 使用JSON格式提交数据
data: {
"username": "Tom",
"age": 18
}
使用RestFul风格不属于前端给后端传参数
这里表单里面有很多无用参数,所以不使用表单提交
$("#btn-add-to-cart").click(function() {
$.ajax({
url: "/carts/add_to_cart",
type: "POST",
data: {
"pid": id,
"amount": $("#num").val()
},
dataType: "JSON",
success: function(json) {
if (json.state == 200) {
alert("增加成功!");
} else {
alert("增加失败!" + json.message);
}
},
error: function(xhr) {
alert("您的登录信息已经过期,请重新登录!HTTP响应码:" + xhr.status);
location.href = "login.html";
}
});
});
点击"加入购物车"按钮后页面跳转的实现:product.html导入的product.js文件里面实现了点击后跳转