自己想做个网站 费用,网站域名 格式,wordpress输出标签文章,wordpress related#x1f9f8;安清h#xff1a;个人主页 #x1f3a5;个人专栏#xff1a;【Spring篇】【计算机网络】【Mybatis篇】
#x1f6a6;作者简介#xff1a;一个有趣爱睡觉的intp#xff0c;期待和更多人分享自己所学知识的真诚大学生。 目录
#x1f680;1.上传头像 -持久… 安清h个人主页 个人专栏【Spring篇】【计算机网络】【Mybatis篇】
作者简介一个有趣爱睡觉的intp期待和更多人分享自己所学知识的真诚大学生。 目录
1.上传头像 -持久层
✨1.1规划SQL语句
✨1.2设计接口和抽象方法
✨1.3 接口的映射
✨1.4单元测试
2.上传头像 -业务层
✨2.1规划异常
✨2.2设计接口和抽象方法
✨2.3抽象方法实现
3.上传头像 -控制层
✨3.1规划异常
✨3.2处理异常
✨3.3设计请求
✨3.4实现请求
4.上传头像 -前端页面
5.解决BUG
✨5.1更改默认的大小限制
✨5.2显示头像
✨5.3登录后显示头像
1.新增收货地址-数据表创建 2.新增收货地址-创建实体类
3.新增收货地址-持久层
✨1.1各功能的开发顺序
✨1.2规划需要执行的SQL语句
✨1.3接口与抽象方法
✨1.4配置SQL映射
4.新增收货地址-业务层
✨4.1规划异常
✨4.2接口与抽象方法
✨4.3实现抽象方法
5.新增收货地址-控制层
✨5.1处理异常
✨5.2设计请求
✨5.3处理请求
6.新增收货地址-前端页面 1.上传头像 -持久层
✨1.1规划SQL语句
将对应的文件保存在操作系统上然后再把这个文件路径给记录下来因为记录路径是非常方便和便捷的将来如果要打开这个文件可以依据这个路径去找到这个文件。在数据库中需要保存这个文件的路径即可。将所有的静态资源图片文件其他放到某台电脑上再把这台电脑作为一台单独的服务器使用。
对应的是一个更新用户avatar字段的sql语句。
update t_user set avatar?,modified_user?,modified_time?
where uid?
✨1.2设计接口和抽象方法 UserMapper接口中来定义个抽象方法用于修改用户的头像。 /*** Param(SQL映射文件中#{}占位符的变量名)解决的问题* 当SQL语句的占位符和映射接口方法参数名不一致时需要将某个参数强行注入到某个占位符变量上时* 可以使用Param这个注解来标注映射关系*也就是说Param(avatar)就相当于mapper.xml中的#{avatar}** 根据用户的uid修改用户的头像* param uid* param avatar* param modifiedUser* param modifiedTime* return 返回值为收影响的行数* */Integer updateAvatarByUid(Param(uid) Integer uid,Param(avatar) String avatar,Param(modifiedUser) String modifiedUser,Param(modifiedTime) Date modifiedTime);
✨1.3 接口的映射
UserMapper.xml文件中编写映射的SQL语句。 update idupdateAvatarByUidupdate t_user set avatar#{avatar},modified_user#{modifiedUser},modified_time#{modifiedTime}where uid#{uid}/update
✨1.4单元测试
在test-mapper包下的UserMapperTests类中编写测试方法updateAvatarByUid代码如下 Testpublic void updateAvatarByUid(){userMapper.updateAvatarByUid(7,/upload/avatar.png,管理员,new Date());}
2.上传头像 -业务层
✨2.1规划异常
1.用户数据不存在找不到对应的用户数据。
2.更新的时候有未知异常的产生。 前面已开发完成无需重新开发。 ✨2.2设计接口和抽象方法
注释的快捷方法/**enter /*** 修改用户的头像* param uid 用户id* param avatar 用户的头像* param username 用户的名称*/void changeAvatar(Integer uid,String avatar,String username);
✨2.3抽象方法实现
编写业务层的更新头像的方法。 Overridepublic void changeAvatar(Integer uid, String avatar, String username) {User result userMapper.findByUid(uid);if(result null || result.getIsDelete() 1){throw new UserNotFoundException(用户数据不存在);}Integer rows userMapper.updateAvatarByUid(uid,avatar, username, new Date());if(rows ! 1){throw new UpdateException(更新时数据产生未知的异常);}}
测试业务层方法的执行。 Testpublic void changeAvatar(){userService.changeAvatar(7,/upload/test.png,haha);}
3.上传头像 -控制层
✨3.1规划异常 文件异常的父类 FileUploadException 泛指文件上传的异常。父类继承RuntimeException 父类是FileUploadException FileEmptyException 文件为空的异常。 FileSizeException 文件大小超出限制的异常。 FileStateException 文件状态产生异常即文件在打开状态时无法上传。 FileTypeException 文件类型异常。 FileUploadIOException 文件读写的异常。 五个构造方法显式声明出来再去继承相关的父类。
在controller包下新建包ex在ex包里新建以上六个类。其中父类FileUploadException继承RuntimeException其余五个类继承父类FileUploadException在此不再做过多代码描述。
✨3.2处理异常
在基类BaseController类中进行编写和统一处理。
else if (e instanceof FileEmptyException) {result.setState(6000);} else if (e instanceof FileSizeException) {result.setState(6001);} else if (e instanceof FileTypeException) {result.setState(6002);} else if (e instanceof FileStateException) {result.setState(6003);} else if (e instanceof FileUploadIOException) {result.setState(6004);} 在异常统一处理方法的参数列表上增加新的异常处理作为它的参数。
✨3.3设计请求 请求路径/users/change_avatar 请求方式POSTget请求提交数据2KB不够清晰 请求数据HttpSession sessionMutipartFile file 响应结果JsonResultString记录图片的路径图片路径是一个串即用String如果不记录下来再切换到其他页面就展示不出来了 ✨3.4实现请求
MultipartFile接口是SpringMVC提供一个接口这个接口为我们包装了获取文件类型的数据任何类型的file都可以接受SpringBoot它又整合了SpringMVC只需要在处理请求的方法参数列表上声明一个参数类型为MultipartFile然后SpringBoot会自动将传递给服务的文件数据赋值给这个参数。
RequestParam表示请求中的参数将请求中的参数注入请求处理方法的某个参数上如果名称不一致则可以使用RequestParam注解进行标记和映射。
/**** param session* param file* return*/RequestMapping(change_avatar)public JsonResultString changeAvatar(HttpSession session,RequestParam(file) MultipartFile file) {
// 判断文件是否为空if(file.isEmpty()){throw new FileEmptyException(文件为空);}
// 判断文件大小是否超出限制if(file.getSize() AVATAR_MAX_SIZE){throw new FileSizeException(文件超出限制);}
// 判断文件的类型是否为我们规定的和后缀类型String contentType file.getContentType();if(!AVATAR_TYPE.contains(contentType)){throw new FileTypeException(文件类型不支持);}
// 上传的文件.../upload/文件.pngString parent session.getServletContext().getRealPath(upload);
// File对象指向这个路径File是否存在File dir new File(parent);if(!dir.exists()){dir.mkdir(); //创建当前的目录}
// 获取到这个文件名称UUID工具来将生成一个新的字符串作为文件名
// 返回的只是文件名不包含目录结构.例如avatar01.png。
// 在后续中前面的avatar01即名称部分可以根据uid来生成一个随机的大小写字符串保存下来
// 后缀需要保存下来String originalFilename file.getOriginalFilename();System.out.println(OriginalFilenameoriginalFilename);int index originalFilename.lastIndexOf(.); //查找字符串中最后一次出现的点号.的位置。String suffix originalFilename.substring(index); //从字符串的指定索引位置开始截取字符串直到字符串的末尾。String filename UUID.randomUUID().toString().toUpperCase() suffix;File dest new File(dir,filename); //在dir下创建一个叫filename的文件此时它是一个空文件
// 将参数file中的数据写入到空文件当中try{file.transferTo(dest); //将file中的数据写入到dest文件中}catch (FileStateException e){throw new FileStateException(文件状态异常);}catch (IOException e){throw new FileUploadIOException(文件读写异常);}Integer uid getuidFromSession(session);String username getUsernameFromSession(session);
// 返回头像路径/upload/test.png相对路径String avatar /uploadfilename;userService.changeAvatar(uid,avatar,username);
// 返回用户头像的路径给前端页面将来用于头像的展示使用return new JsonResult(OK,avatar);}
这里没有办法测试数据类型模拟不出来file每一种类型的文件编码都不一样。
4.上传头像 -前端页面
在upload页面中编写上传头像的代码。.ajax()请求它可以把一个数据解析成一个大串来拼接在参数名的后边但file可以直接把一个文件作为一个整体提交。表单中action可以整体提交。
说明如果直接使用表单进行文件的上传需要给表单显示的添加一个属性
enctypemultipart/form-data;声明出来不会将目标文件的数据结构做修改再上传不同字符串。
5.解决BUG
✨5.1更改默认的大小限制
SpringMVC默认为1MB文件可以进行上传手动的去修改SpringMVC默认上传文件爱的大小。
方式一直接可以在配置文件中进行配置
spring.servlet.multipart.max-file-size10MB
spring.servlet.multipart.max-request-size15MB
✨5.2显示头像
页面中通过ajax的请求来提交文件提交完成后返回了json串解析出data中的数据设置到img头像标签的src属性上就可以了。
1.在这里把submit改成button因为submit不能够添加点击事件。
input idbtn-change-avatar typebutton classbtn btn-primary value上传 / 2.这里原来的serialize提交的是一个串的类型不能够解析文件所以要换成类似一下方式
serialize()可以将表单数据自动拼接成keyvalue的结构进行提交给服务器一般提交是普通控件类型中的数据(text/password/radio/checkbox)。FromData类将表单中的数据保持原有的结构进行提交。new FromData($(#form)[0]);将form表单中某一个元素的整体值作为FromData()创建对象的数据所以这个对象的格式就放在了这个对象当中这样格式就不会被看做其他的对象被更改或调整。 ajax默认处理数据时按照字符串的形式进行处理以及默认会采用字符串的形式进行提交数据。关闭这两个默认的功能。
new FromData($(#form)[0])
✨5.3登录后显示头像
可以更新头像成功后将服务器返回的头像路径保存在客户端的cookie对象中然后每次检测到用户打开上传头像页面在这个页面中通过ready()方法来自动监测去读取cookie中的头像并设到src上。
1.设置cookie中的值
导入cookie.js的文件
script src../bootstrap3/js/jquery.cookie.js typetext/javascript charsetutf-8/scriptscript src../js/autoLogin.js typetext/javascript/script
调用cookie的方法
$.cookie(key,value,time); //单位天存活时间
2.在upload.html页面先引入cookie.js文件
script src../bootstrap3/js/jquery.cookie.js typetext/javascript charsetutf-8/scriptscript src../js/autoLogin.js typetext/javascript/script
3.在upload.html页面通过ready()自动读取cookie中的数据。
$(document).ready(function (){let avatar $.cookie(avatar);// 将cookie中的值获取出来设置到头像src属性上$(#img-avatar).attr(src,avatar);});
5.4显示最新头像
在更改完头像后将最新的头像地址再次保存到cookie同名保存会覆盖原有cookie中的值。
$.cookie(avatar,json.data,{expires: 7});
1.新增收货地址-数据表创建
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)
) ENGINEINNODB DEFAULT CHARSETutf8;2.新增收货地址-创建实体类
创建一个类Address类在类中定义表的相关字段采用驼峰命名方式最后再去继承BaseEntity类。
//收货地址数据的实体类
public class Address extends BaseEntity {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;
}
3.新增收货地址-持久层
✨1.1各功能的开发顺序
当前收货地址功能模块列表的展示、修改、删除、设置默认、新增收货地址。开发顺序新增收货地址-列表展示-设置默认收货地址-删除收货地址-修改收货地址。
✨1.2规划需要执行的SQL语句
1.对应的插入语句
insert into t_address(除aid外的字段列表) values(字段值列表)
2. 一个用户的收货地址最多只能有20条数据对应。在插入用户数据之前先做查询操作。收货地址逻辑控制方面的一个异常。
select count(*) from t_address where uid?
✨1.3接口与抽象方法
1.创建一个新的接口Address在这个接口中来定义上面两个SQL语句抽象方法定义。
/*** 插入用户的收货地址* param address 收货地址数据* return 受影响的行数*/Integer insert(Address address);/*** 根据用户的id统计收货地址的数量* param uid 用户的id* return 当前用户的收货地址总数*/Integer count(Integer uid);
✨1.4配置SQL映射
?xml version1.0 encodingUTF-8 ?
!DOCTYPE mapperPUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespacecom.cy.store.mapper.AddressMapperresultMap idAddressEntityMap typecom.cy.store.entity.Addressid columnaid propertyaid/result columnprovince_code propertyprovinceCode/result columnprovince_name propertyprovinceName/result columncity_code propertycityCode/result columncity_name propertycityName/result columnarea_code propertyareaCode/result columnarea_name propertyareaName/result columnis_delete propertyisDelete/resultresult columncreated_user propertycreatedUser/resultresult columncreated_time propertycreatedTime/resultresult columnmodified_user propertymodifiedUser/resultresult columnmodified_time propertymodifiedTime/result/resultMapinsert idinsert useGeneratedKeystrue keyPropertyaidinsert 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) values (#{uid}, #{name}, #{provinceName}, #{provinceCode}, #{cityName}, #{cityCode}, #{areaName},#{areaCode}, #{zip}, #{address}, #{phone}, #{tel}, #{tag}, #{isDefault}, #{createdUser},#{createdTime}, #{modifiedUser}, #{modifiedTime})/insertselect idcountByUid resultTypejava.lang.Integerselect count(*) from t_address where uid#{uid}/select
/mapper
2.在test下的mapper文件夹下创建一个AddressMapperTests的测试类。
Testpublic void insert(){Address address new Address();address.setUid(6);address.setPhone(15374583927);address.setName(备备);addressMapper.insert(address);}Testpublic void countByUid(){Integer count addressMapper.countByUid(6);System.out.println(count);}
4.新增收货地址-业务层
✨4.1规划异常
1.如果用户是第一次插入收货地址规则当用户插入的地址是第一条时需要将当前地址作为默认收货地址如果查询到统计总数为0则将当前地址的is_default设置为1。查询统计的结果为0不代表异常。
查询到的结果大于20这时候需要抛出业务控制的异常AddressCountLimitException异常。自行创建这个异常。
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.插入数据时产生异常。
✨4.2接口与抽象方法
1.创建一个IAddressService接口在里面定义业务的抽象方法。 void addNewAddress(Integer uid,String username,Address address);
2.创建一个AddressServiceImpl实现类去实现接口中的抽象方法。
在配置文件中定义数据
#Spring读取配置文件中数据Value(${user.address.max-count})
user.address.max-count20
✨4.3实现抽象方法
在实现类中实现业务控制 。
//新增收货地址的实现类
Service
public class AddressServiceImpl implements IAddressService {Autowiredprivate AddressMapper addressMapper;Value(${user.address.max-count})private Integer count;Overridepublic void addNewAddress(Integer uid, String username, Address address) {
// 调用收货地址统计的方法Integer count addressMapper.countByUid(uid);if(count 20){throw new AddressCountLimitException(用户收货地址超出上限);}// uid、isDeleteaddress.setUid(uid);Integer isDelete count 0?1:0; //1表示默认0表示不默认address.setIsDefault(isDelete);
// 补全4项日志address.setCreatedUser(username);address.setCreatedTime(new Date());address.setModifiedUser(username);address.setModifiedTime(new Date());// 插入收货地址的方法Integer rows addressMapper.insert(address);if(rows ! 1){throw new InsertException(插入用户地址时产生未知异常);}}}3.测试业务层功能是否正常。AddressServiceTests测试来测试业务功能。 Testpublic void addNewAddress(){Address address new Address();address.setPhone(11874583927);address.setName(备备);addressService.addNewAddress(6,小明,address);}
5.新增收货地址-控制层
✨5.1处理异常
业务层抛出了收货地址总数超标的异常在BaseController中进行处理。
else if(e instanceof AddressCountLimitException) {result.setState(4003);result.setMessage(用户收货地址超出上限的异常);}
✨5.2设计请求 请求路径/addresses/add_new_address 请求方式POST 请求数据Address addressHttpSession session 响应结果JsonResultVoid ✨5.3处理请求
在控制层创建AddressController来处理用户收货地址的请求和响应。 RequestMapping(add_new_address)public JsonResultVoid addNewAddress(Address address, HttpSession session){Integer uid getuidFromSession(session);String username getUsernameFromSession(session);addressService.addNewAddress(uid,username,address);return new JsonResult(OK);}
先登录用户然后再访问http://localhost:8080/address/add_new_address?nametomphone1826478328 进行测试。
6.新增收货地址-前端页面
script$(#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){if(json.state200){alert(新增收货地址成功);}else{alert(新增收货地址失败);}},error:function (xhr){alert(新增收货地址时产生未知的异常xhr.message);}});});/script