该系列博客主要记录笔者的开发过程,参考B站系列视频:【SpringBoot项目实战完整版】SpringBoot+MyBatis+MySQL电脑商城项目实战_哔哩哔哩_bilibili
所用的一些版本信息:
IDEA开发、JDK1.8版本以上、maven3.61版本以上,springboot,DataGrip管理数据库
新增收获地址:
0 前置准备
0.1 功能规划
当前收货地址功能模块:列表的展示、修改、删除、设置默认、新增地址。
开发顺序:新增收获地址,列表展示,设置默认收获地址、删除收获地址、修改收获地址。
0.2 创建实体类
在 store\src\main\java\com\cy\store\entity 目录下创建实体类:
package com.cy.store.entity;
import java.io.Serializable;
/** 收货地址数据的实体类 */
public class Address extends BaseEntity implements Serializable {
private Integer aid;
private Integer uid;
private String name;
private String provinceName;
private String provinceCode;
private String cityName;
private String cityCode;
private String areaName;
private String areaCode;
private String zip;
private String address;
private String phone;
private String tel;
private String tag;
private Integer isDefault;
public Integer getAid() {
return aid;
}
public void setAid(Integer aid) {
this.aid = aid;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getProvinceName() {
return provinceName;
}
public void setProvinceName(String provinceName) {
this.provinceName = provinceName;
}
public String getProvinceCode() {
return provinceCode;
}
public void setProvinceCode(String provinceCode) {
this.provinceCode = provinceCode;
}
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
public String getCityCode() {
return cityCode;
}
public void setCityCode(String cityCode) {
this.cityCode = cityCode;
}
public String getAreaName() {
return areaName;
}
public void setAreaName(String areaName) {
this.areaName = areaName;
}
public String getAreaCode() {
return areaCode;
}
public void setAreaCode(String areaCode) {
this.areaCode = areaCode;
}
public String getZip() {
return zip;
}
public void setZip(String zip) {
this.zip = zip;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
public Integer getIsDefault() {
return isDefault;
}
public void setIsDefault(Integer isDefault) {
this.isDefault = isDefault;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Address)) return false;
Address address1 = (Address) o;
if (getAid() != null ? !getAid().equals(address1.getAid()) : address1.getAid() != null) return false;
if (getUid() != null ? !getUid().equals(address1.getUid()) : address1.getUid() != null) return false;
if (getName() != null ? !getName().equals(address1.getName()) : address1.getName() != null) return false;
if (getProvinceName() != null ? !getProvinceName().equals(address1.getProvinceName()) : address1.getProvinceName() != null)
return false;
if (getProvinceCode() != null ? !getProvinceCode().equals(address1.getProvinceCode()) : address1.getProvinceCode() != null)
return false;
if (getCityName() != null ? !getCityName().equals(address1.getCityName()) : address1.getCityName() != null)
return false;
if (getCityCode() != null ? !getCityCode().equals(address1.getCityCode()) : address1.getCityCode() != null)
return false;
if (getAreaName() != null ? !getAreaName().equals(address1.getAreaName()) : address1.getAreaName() != null)
return false;
if (getAreaCode() != null ? !getAreaCode().equals(address1.getAreaCode()) : address1.getAreaCode() != null)
return false;
if (getZip() != null ? !getZip().equals(address1.getZip()) : address1.getZip() != null) return false;
if (getAddress() != null ? !getAddress().equals(address1.getAddress()) : address1.getAddress() != null)
return false;
if (getPhone() != null ? !getPhone().equals(address1.getPhone()) : address1.getPhone() != null) return false;
if (getTel() != null ? !getTel().equals(address1.getTel()) : address1.getTel() != null) return false;
if (getTag() != null ? !getTag().equals(address1.getTag()) : address1.getTag() != null) return false;
return getIsDefault() != null ? getIsDefault().equals(address1.getIsDefault()) : address1.getIsDefault() == null;
}
@Override
public int hashCode() {
int result = getAid() != null ? getAid().hashCode() : 0;
result = 31 * result + (getUid() != null ? getUid().hashCode() : 0);
result = 31 * result + (getName() != null ? getName().hashCode() : 0);
result = 31 * result + (getProvinceName() != null ? getProvinceName().hashCode() : 0);
result = 31 * result + (getProvinceCode() != null ? getProvinceCode().hashCode() : 0);
result = 31 * result + (getCityName() != null ? getCityName().hashCode() : 0);
result = 31 * result + (getCityCode() != null ? getCityCode().hashCode() : 0);
result = 31 * result + (getAreaName() != null ? getAreaName().hashCode() : 0);
result = 31 * result + (getAreaCode() != null ? getAreaCode().hashCode() : 0);
result = 31 * result + (getZip() != null ? getZip().hashCode() : 0);
result = 31 * result + (getAddress() != null ? getAddress().hashCode() : 0);
result = 31 * result + (getPhone() != null ? getPhone().hashCode() : 0);
result = 31 * result + (getTel() != null ? getTel().hashCode() : 0);
result = 31 * result + (getTag() != null ? getTag().hashCode() : 0);
result = 31 * result + (getIsDefault() != null ? getIsDefault().hashCode() : 0);
return result;
}
@Override
public String toString() {
return "Address{" +
"aid=" + aid +
", uid=" + uid +
", name='" + name + '\'' +
", provinceName='" + provinceName + '\'' +
", provinceCode='" + provinceCode + '\'' +
", cityName='" + cityName + '\'' +
", cityCode='" + cityCode + '\'' +
", areaName='" + areaName + '\'' +
", areaCode='" + areaCode + '\'' +
", zip='" + zip + '\'' +
", address='" + address + '\'' +
", phone='" + phone + '\'' +
", tel='" + tel + '\'' +
", tag='" + tag + '\'' +
", isDefault=" + isDefault +
"} " + super.toString();
}
}
1 持久层
1.0 构建数据表
在利用dataGrip新建一个底层的数据表:
CREATE TABLE t_address (
aid INT AUTO_INCREMENT COMMENT '收货地址id',
uid INT COMMENT '归属的用户id',
name VARCHAR(20) COMMENT '收货人姓名',
province_name VARCHAR(15) COMMENT '省-名称',
province_code CHAR(6) COMMENT '省-行政代号',
city_name VARCHAR(15) COMMENT '市-名称',
city_code CHAR(6) COMMENT '市-行政代号',
area_name VARCHAR(15) COMMENT '区-名称',
area_code CHAR(6) COMMENT '区-行政代号',
zip CHAR(6) COMMENT '邮政编码',
address VARCHAR(50) COMMENT '详细地址',
phone VARCHAR(20) COMMENT '手机',
tel VARCHAR(20) COMMENT '固话',
tag VARCHAR(6) COMMENT '标签',
is_default INT COMMENT '是否默认:0-不默认,1-默认',
created_user VARCHAR(20) COMMENT '创建人',
created_time DATETIME COMMENT '创建时间',
modified_user VARCHAR(20) COMMENT '修改人',
modified_time DATETIME COMMENT '修改时间',
PRIMARY KEY (aid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
1.1 规划SQL语句
1.插入数据语句。
2.用户收获地址最多20条数据相应,在插入用户数据前做查询操作。存在一个逻辑控制的异常
1.2 接口与抽象方法
在store\src\main\java\com\cy\store\mapper下创建AddressMapper
package com.cy.store.mapper;
import com.cy.store.entity.Address;
public interface AddressMapper {
/**
* 插入地址数据
* @param address 地址
* @return 影响行数
*/
Integer insert(Address address);
/**
* 根据用户ID统计收获地址数量
* @param uid 用户id
* @return 数量
*/
Integer countByUid(Integer uid);
}
1.3 配置SQL映射
在 store\src\main\resources\mapper 下新建地址的映射文件AddressMapper:
<?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需要映射到java中的接口,指定路径-->
<mapper namespace="com.cy.store.mapper.AddressMapper">
<resultMap id="AddressEntityMap" type="com.cy.store.entity.Address">
<id column="aid" property="aid"/>
<result column="province_code" property="provinceCode"/>
<result column="province_name" property="provinceName"/>
<result column="city_code" property="cityCode"/>
<result column="city_name" property="cityName"/>
<result column="area_code" property="areaCode"/>
<result column="area_name" property="areaName"/>
<result column="is_default" property="isDefault"/>
<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="aid">
INSERT INTO t_address(
uid, name, province_name, province_code, city_name, city_code, area_name, area_code, zip,
address, phone, tel, tag, is_default, created_user, created_time, modified_user, modified_time
)VALUE (
#{uid}, #{name}, #{provinceName}, #{provinceCode}, #{cityName}, #{cityCode}, #{areaName},
#{areaCode}, #{zip}, #{address}, #{phone}, #{tel}, #{tag}, #{isDefault}, #{createdUser},
#{createdTime}, #{modifiedUser}, #{modifiedTime}
)
</insert>
<select id="countByUid" resultMap="java.lang.Integer">
SELECT count(*) FROM t_address WHERE uid = #{uid}
</select>
</mapper>
1.4 单元测试
新建:store\src\test\java\com\cy\store\mapper\AddressMapperTests.java
编写测试函数:
package com.cy.store.mapper;
import com.cy.store.entity.Address;
import com.cy.store.entity.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.Date;
//@SpringBootTest:该注解表示标注当前类是个测试类,测试类不会随着项目一块打包发送
@SpringBootTest
//@RunWith:表示启动这个单元测试类(单元测试类时不能够运行的),需传递一个参数,必须是SpringRunner的实例类型
@RunWith(SpringRunner.class)
public class AddressMapperTests {
//接口不能创建bean对象,但是mybatis创建了动态代理实现类帮我们进行解决
@Resource
private AddressMapper addressMapper;
/**
* 单元测试方法(可以独立运行,不启动整个项目):
* 1.必须被@Test修饰
* 2.返回值类型必须是void
* 3.方法的参数列表不能指定任何类型
* 4.方法的访问修饰符必须是public
*/
@Test
public void insert(){
Address address = new Address();
address.setUid(12);
address.setPhone("123123412");
address.setName("女朋友");
Integer rows = addressMapper.insert(address);
System.out.println(rows);
}
@Test
public void contByUid(){
System.out.println(addressMapper.countByUid(12));
}
}
2 业务层
2.0 规划业务:
1.首次插入,需要将该地址作为默认收获地址。
2.1 规划异常
1.查询结果大于等于20了,抛出AddressCountLimitException
新建:store\src\main\java\com\cy\store\service\ex\AddressCountLimitException.java
package com.cy.store.service.ex;
/** 收获地址超出限制 */
public class AddressCountLimitException extends ServiceException{
public AddressCountLimitException() {
super();
}
public AddressCountLimitException(String message) {
super(message);
}
public AddressCountLimitException(String message, Throwable cause) {
super(message, cause);
}
public AddressCountLimitException(Throwable cause) {
super(cause);
}
protected AddressCountLimitException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
2.插入异常,已经存在
2.2 接口与抽象方法
创建接口:
F:\projects\store\src\main\java\com\cy\store\service\IAddressService.java
package com.cy.store.service;
import com.cy.store.entity.Address;
/** 收获地址业务层接口 */
public interface IAddressService {
/**
* 新增收获地址
* @param uid
* @param username
* @param address
*/
void addNewAddress(Integer uid, String username, Address address);
}
2.3 实现抽象方法
在配置文件store\src\main\resources\application.yml中配置最大收获地址:
# 自己的配置
user:
address:
max-count = 20
实现方法store\src\main\java\com\cy\store\service\impl\AddressService.java:
package com.cy.store.service.impl;
import com.cy.store.entity.Address;
import com.cy.store.mapper.AddressMapper;
import com.cy.store.service.IAddressService;
import com.cy.store.service.ex.AddressCountLimitException;
import com.cy.store.service.ex.InsertException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import java.util.Date;
/** 收获地址的实现类 */
public class AddressService implements IAddressService {
@Autowired
private AddressMapper addressMapper;
// 收获地址最大数量
@Value("${user.address.max-count}")
public Integer maxCount;
@Override
public void addNewAddress(Integer uid, String username, Address address) {
//查看收获地址是否达到上限
Integer count = addressMapper.countByUid(uid);
if(count >= maxCount){
throw new AddressCountLimitException("收获地址超出上限");
}
//补全字段
address.setUid(uid);
address.setIsDefault(count==0?1:0);
//补全四项日志
address.setCreatedTime(new Date());
address.setCreatedUser(username);
address.setModifiedTime(new Date());
address.setModifiedUser(username);
//插入
Integer rows = addressMapper.insert(address);
if(rows!=1){
throw new InsertException("插入时异常");
}
}
}
2.4 单元测试
新建:store\src\test\java\com\cy\store\service\AddressServiceTests.java
package com.cy.store.service;
import com.cy.store.entity.Address;
import com.cy.store.entity.User;
import com.cy.store.service.ex.ServiceException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
//@SpringBootTest:该注解表示标注当前类是个测试类,测试类不会随着项目一块打包发送
@SpringBootTest
//@RunWith:表示启动这个单元测试类(单元测试类时不能够运行的),需传递一个参数,必须是SpringRunner的实例类型
@RunWith(SpringRunner.class)
public class AddressServiceImplTests {
//接口不能创建bean对象,但是mybatis创建了动态代理实现类帮我们进行解决
@Resource
private IAddressService addressService;
@Test
public void addNewAddress(){
Address address = new Address();
address.setPhone("123123412");
address.setName("男");
addressService.addNewAddress(24, "管理员",address);
}
}
3 控制层
3.1 处理异常
在store\src\main\java\com\cy\store\controller\BaseController.java中添加:
3.2 设计请求
url:/addresses/add_new_address
请求方式:post
请求参数:Address address, HttpSession session
返回数据:JsonResult<Void>
3.3 处理请求
创建文件:store\src\main\java\com\cy\store\controller\AddressController.java
package com.cy.store.controller;
import com.cy.store.entity.Address;
import com.cy.store.service.IAddressService;
import com.cy.store.util.JsonResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
//@Controller
@RestController //该注解等于@Controller + 方法上的@ResponseBody
@RequestMapping("addresses")
public class AddressController extends BaseController{
@Resource
private IAddressService addressService;
@RequestMapping("add_new_address")
//@ResponseBody //此方法响应结果以json格式进行数据的响应给前端
public JsonResult<Void> addNewAddress(Address address, HttpSession session){
Integer uid = getuidFromSession(session);
String username = getusernameFromSession(session);
addressService.addNewAddress(uid, username, address);
return new JsonResult<Void>(OK);
}
}
3.4 测试
登录后测试:
http://localhost:8081/addresses/add_new_address?name=tom
4 前端页面
在store\src\main\resources\static\web\address.html中添加ajax请求
<script type="text/javascript">
$("#btn-add-new-address").click(function(){
$.ajax({
url:"/addresses/add_new_address",
type: "POST",
data: $('#form-add-new-address').serialize(),
dataType:"JSON",
success:function (json){
console.log(json)
if(json.state == 200){
alert("新增地址成功");
}else{
alert("新增地址失败" + json.message);
}
},
error: function(xhr){
alert("新增地址时产生未知异常" + xhr.message)
}
});
});
</script>