使用SpringBoot+MyBatis+MySQL完成加入购物车
创建数据表
在数据中执行:
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;
创建实体类
在entity下创建Cart.java实体类文件。
public class Cart extends BaseEntity{
private Integer cid;
private Integer uid;
private Integer pid;
private Long price;
private Integer num;
// get、set、hashCode()、equals()和toString()方法
}
加入购物车-持久层
规划需要执行的SQL语句
① 向购物车表中插入数据;
insert into t_cart (cid除外) values(值列表)
② 当前的商品已经在购物车中存在,则直接更新num的数量;
update t_cart set num=?, created_user=?, created_time=? where cid=?
③ 在插入或者更新具体执行哪个语句,取决于数据库中是否有当前的这个购物车商品的数据,根据查询结果确定。
select * from t_cart where pid=? and uid=?
设计接口和抽象方法
创建一个CartMapper的接口文件。
package com.cy.store.mapper;
import com.cy.store.entity.Cart;
public interface CartMapper {
/**
* 插入购物车数据
* @param cart 购物车数据
* @return 受影响的行数
*/
Integer insert(Cart cart);
/**
* 更新购物车某件商品的数量
* @param cid 购物车数据id
* @param num 更新的商品数量
* @param modifiedUser 修改者时间
* @return 受影响的行数
*/
Integer updateNumByCid(Integer cid,
Integer num,
String modifiedUser,
String modififedTime);
/**
* 根据用户id和商品id查询购物车中的数据
* @param uid 用户id
* @param pid 商品id
* @return 商品信息
*/
Cart findByUidAndPid(Integer uid, Integer pid);
}
SQL映射
创建一个CartMapper.xml文件,添加以上三个抽象方法的SQL语句映射。
<?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">
<!-- namespace属性:用于指定当前的映射文件和哪个接口进行映射,需要指定接口的文件路径,需要标注包的完整路径 -->
<mapper namespace="com.cy.store.mapper.CartMapper">
<resultMap id="CartEntityMap" type="com.cy.store.entity.Cart">
<id column="id" property="id"/>
<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>
<select id="findByUidAndPid" resultMap="CartEntityMap">
SELECT *
FROM t_cart
WHERE uid=#{uid} AND pid=#{pid}
</select>
<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},
created_user=#{createdUser},
created_time=#{createdTime}
WHERE
cid=#{cid}
</update>
</mapper>
单元测试
在单元测试方法中编写测试用例。
@RunWith(SpringRunner.class)
@SpringBootTest
public class CartMapperTests {
@Autowired
private CartMapper cartMapper;
@Test
public void insert() {
Cart cart = new Cart();
cart.setUid(14);
cart.setPid(100000425);
cart.setNum(2);
cart.setPrice(1000L);
cartMapper.insert(cart);
}
@Test
public void updateNumByCid() {
cartMapper.updateNumByCid(1, 5, "test002", new Date());
}
@Test
public void findByUidAndPid() {
System.out.println(cartMapper.findByUidAndPid(14,100000425));
}
}
加入购物车-业务层
规划异常
- 插入数据时可能产生的异常:InsertException;
- 更新数据时可能产生的异常:UpdateException。
接口和抽象方法
创建一个ICartService接口文件,在这个接口文件中设计抽象方法。
public interface ICartService {
/**
* 将商品添加到购物车中
* @param uid 用户id
* @param pid 商品id
* @param amount 新增数量
* @param username 用户名(修改者)
*/
void addToCart(Integer uid, Integer pid, Integer amount, String username);
}
实现接口
创建CartServiceImpl类,并实现ICartService接口,在类的定义前添加@Service注解。在类中声明CartMapper持久层对象和IProductService处理商品数据的业务对象,并都添加@Autowired注解修饰。
@Service
public class CartServiceImpl implements ICartService {
/** 购物车的业务层依赖于购物车的持久层和商品的持久层 */
@Autowired
private CartMapper cartMapper;
@Autowired
private IProductService productService;
@Override
public void addToCart(Integer uid, Integer pid, Integer amount, String username) {
// 查询当前要添加到购物车的商品是否在表中已存在
Cart result = cartMapper.findByUidAndPid(uid, pid);
Date now = new Date();
if(result == null) { // 表示商品从来没有被添加到购物车中,则进行新增操作
// 创建一个cart对象
Cart cart = new Cart();
// 补全数据:参数传递的数据
cart.setUid(uid);
cart.setPid(pid);
cart.setNum(amount);
// 补全价格,来自商品中的数据
Product product = productService.findById(pid);
cart.setPrice(product.getPrice());
// 补全4个日志
cart.setCreatedUser(username);
cart.setCreatedTime(now);
cart.setModifiedUser(username);
cart.setModifiedTime(now);
// 执行数据的插入操作
Integer rows = cartMapper.insert(cart);
if (rows != null) {
throw new InsertException("插入数据时产生未知的异常");
}
} else { // 表示当前的商品在购物车中已经存在,则更新这条数据的num值
Integer num = result.getNum() + amount;
Integer rows = cartMapper.updateNumByCid(
result.getCid(),
num,
username,
now);
if (rows != 1) {
throw new UpdateException("更新数据时产生未知的异常");
}
}
}
}
单元测试
在service包下创建测试类CartServiceTests,并编写测试方法。
@SpringBootTest
@RunWith(SpringRunner.class)
public class CartServiceTests {
@Autowired
private ICartService cartService;
@Test
public void addToCart() {
cartService.addToCart(14, 100000425, 3, "test005");
}
}
加入购物车-控制层
规划异常
无需要处理的异常。
设计请求
请求路径:/carts/add_to_cart
请求方法:GET
请求数据:Integer pid, Interger amount, Httpsession session
响应结果:JsonResult<Void>
编写请求处理方法
创建一个CartController类。
@RequestMapping("carts")
@RestController
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<>(OK);
}
}
启动项目后先登录再访问,在浏览器输入url地址进行测试。
加入购物车-前端页面
在product.html页面给[加入购物车]按钮添加点击事件,并发送ajax请求。
$("#btn-add-to-cart").click(function () {
$.ajax({
url: "/carts/add_to_cart",
type: "GET",
data: {
"pid": id,
"amount": $("#num").val()
},
dataType: "JSON",
success: function (json) {
if (json.state == 200) {
alert("加入购物车成功");
} else {
alert("加入购物车失败");
}
},
error: function (xhr) {
alert("加入购物车时产生未知的异常" + xhr.message);
}
});
});
在Ajax函数中添加数据的方式
在ajax函数中data参数的数据设置的方式:
第一种方式:
$("form表单选择").serialize()。当参数过多并且在同一个表单中,字符串的提交等。
第二种方式:
new FormData($("form表单选择")[0]),只适用于提交文件
第三种方式: data: “username=Tom”,适合参数值固定并且参数值列表有限,可以进行手动拼接
let user = "Tom"
data: "username" + user
第四种方式:适用于JSON格式提交数据:
data: {
"username": "Tom",
"age": 19,
"sex": 0
}