網(wǎng)站備案查詢不到說明啥短網(wǎng)址生成網(wǎng)站
??? ? 🧸安清h:個人主頁?
? ?🎥個人專欄:【Spring篇】【計算機網(wǎng)絡(luò)】【Mybatis篇】
🚦作者簡介:一個有趣愛睡覺的intp,期待和更多人分享自己所學(xué)知識的真誠大學(xué)生。
目錄
🚀1.上傳頭像 -持久層
?1.1規(guī)劃SQL語句
?1.2設(shè)計接口和抽象方法
?1.3 接口的映射
?1.4單元測試
🚀2.上傳頭像 -業(yè)務(wù)層
?2.1規(guī)劃異常
?2.2設(shè)計接口和抽象方法
?2.3抽象方法實現(xiàn)
🚀3.上傳頭像 -控制層
?3.1規(guī)劃異常
?3.2處理異常
?3.3設(shè)計請求
?3.4實現(xiàn)請求
🚀4.上傳頭像 -前端頁面
🚀5.解決BUG
?5.1更改默認(rèn)的大小限制
?5.2顯示頭像
?5.3登錄后顯示頭像
🎃1.新增收貨地址-數(shù)據(jù)表創(chuàng)建
?🎃2.新增收貨地址-創(chuàng)建實體類
🎃3.新增收貨地址-持久層
?1.1各功能的開發(fā)順序
?1.2規(guī)劃需要執(zhí)行的SQL語句
?1.3接口與抽象方法
?1.4配置SQL映射
🎃4.新增收貨地址-業(yè)務(wù)層
?4.1規(guī)劃異常
?4.2接口與抽象方法
?4.3實現(xiàn)抽象方法?
🎃5.新增收貨地址-控制層
?5.1處理異常
?5.2設(shè)計請求
?5.3處理請求
🎃6.新增收貨地址-前端頁面
🚀1.上傳頭像 -持久層
?1.1規(guī)劃SQL語句
將對應(yīng)的文件保存在操作系統(tǒng)上,然后再把這個文件路徑給記錄下來,因為記錄路徑是非常方便和便捷的,將來如果要打開這個文件,可以依據(jù)這個路徑去找到這個文件。在數(shù)據(jù)庫中需要保存這個文件的路徑即可。將所有的靜態(tài)資源(圖片,文件,其他)放到某臺電腦上,再把這臺電腦作為一臺單獨的服務(wù)器使用。
對應(yīng)的是一個更新用戶avatar字段的sql語句。
update t_user set avatar=?,modified_user=?,modified_time=?
where uid=?
?1.2設(shè)計接口和抽象方法
?UserMapper接口中來定義個抽象方法用于修改用戶的頭像。
/*** @Param("SQL映射文件中#{}占位符的變量名"):解決的問題,* 當(dāng)SQL語句的占位符和映射接口方法參數(shù)名不一致時,需要將某個參數(shù)強行注入到某個占位符變量上時,* 可以使用@Param這個注解來標(biāo)注映射關(guān)系*也就是說@Param("avatar")就相當(dāng)于mapper.xml中的#{avatar}** 根據(jù)用戶的uid修改用戶的頭像* @param uid* @param avatar* @param modifiedUser* @param modifiedTime* @return 返回值為收影響的行數(shù)* */Integer updateAvatarByUid(@Param("uid") Integer uid,@Param("avatar") String avatar,@Param("modifiedUser") String modifiedUser,@Param("modifiedTime") Date modifiedTime);
?1.3 接口的映射
UserMapper.xml文件中編寫映射的SQL語句。
<update id="updateAvatarByUid">update 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.上傳頭像 -業(yè)務(wù)層
?2.1規(guī)劃異常
1.用戶數(shù)據(jù)不存在,找不到對應(yīng)的用戶數(shù)據(jù)。
2.更新的時候,有未知異常的產(chǎn)生。
前面已開發(fā)完成,無需重新開發(fā)。?
?2.2設(shè)計接口和抽象方法
注釋的快捷方法:/**+enter
/*** 修改用戶的頭像* @param uid 用戶id* @param avatar 用戶的頭像* @param username 用戶的名稱*/void changeAvatar(Integer uid,String avatar,String username);
?2.3抽象方法實現(xiàn)
編寫業(yè)務(wù)層的更新頭像的方法。
@Overridepublic void changeAvatar(Integer uid, String avatar, String username) {User result = userMapper.findByUid(uid);if(result == null || result.getIsDelete() == 1){throw new UserNotFoundException("用戶數(shù)據(jù)不存在");}Integer rows = userMapper.updateAvatarByUid(uid,avatar, username, new Date());if(rows != 1){throw new UpdateException("更新時數(shù)據(jù)產(chǎn)生未知的異常");}}
測試業(yè)務(wù)層方法的執(zhí)行。
@Testpublic void changeAvatar(){userService.changeAvatar(7,"/upload/test.png","haha");}
🚀3.上傳頭像 -控制層
?3.1規(guī)劃異常
文件異常的父類:
????????FileUploadException 泛指文件上傳的異常。(父類)繼承RuntimeException
父類是:FileUploadException
????????FileEmptyException 文件為空的異常。
????????FileSizeException 文件大小超出限制的異常。
????????FileStateException 文件狀態(tài)產(chǎn)生異常,即文件在打開狀態(tài)時無法上傳。
????????FileTypeException 文件類型異常。
????????FileUploadIOException 文件讀寫的異常。
?五個構(gòu)造方法顯式聲明出來,再去繼承相關(guān)的父類。
在controller包下新建包ex,在ex包里新建以上六個類。其中父類FileUploadException繼承RuntimeException,其余五個類繼承父類FileUploadException,在此不再做過多代碼描述。
?3.2處理異常
在基類BaseController類中進行編寫和統(tǒng)一處理。
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);}
?在異常統(tǒng)一處理方法的參數(shù)列表上增加新的異常處理作為它的參數(shù)。
?3.3設(shè)計請求
請求路徑:/users/change_avatar
請求方式:POST(get請求提交數(shù)據(jù)2KB,不夠清晰)
請求數(shù)據(jù):HttpSession session,MutipartFile file
響應(yīng)結(jié)果:JsonResult<String>(記錄圖片的路徑,圖片路徑是一個串,即用String,如果不記錄下來,再切換到其他頁面就展示不出來了)
?3.4實現(xiàn)請求
MultipartFile接口是SpringMVC提供一個接口,這個接口為我們包裝了獲取文件類型的數(shù)據(jù)(任何類型的file都可以接受),SpringBoot它又整合了SpringMVC,只需要在處理請求的方法參數(shù)列表上聲明一個參數(shù)類型為MultipartFile,然后SpringBoot會自動將傳遞給服務(wù)的文件數(shù)據(jù)賦值給這個參數(shù)。
@RequestParam:表示請求中的參數(shù),將請求中的參數(shù)注入請求處理方法的某個參數(shù)上,如果名稱不一致則可以使用@RequestParam注解進行標(biāo)記和映射。
/**** @param session* @param file* @return*/@RequestMapping("change_avatar")public JsonResult<String> changeAvatar(HttpSession session,@RequestParam("file") MultipartFile file) {
// 判斷文件是否為空if(file.isEmpty()){throw new FileEmptyException("文件為空");}
// 判斷文件大小是否超出限制if(file.getSize() > AVATAR_MAX_SIZE){throw new FileSizeException("文件超出限制");}
// 判斷文件的類型是否為我們規(guī)定的和后綴類型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(); //創(chuàng)建當(dāng)前的目錄}
// 獲取到這個文件名稱,UUID工具來將生成一個新的字符串作為文件名
// 返回的只是文件名,不包含目錄結(jié)構(gòu).例如:avatar01.png。
// 在后續(xù)中,前面的avatar01即名稱部分可以根據(jù)uid來生成一個隨機的大小寫字符串保存下來,
// 后綴需要保存下來String originalFilename =file.getOriginalFilename();System.out.println("OriginalFilename"+originalFilename);int index = originalFilename.lastIndexOf("."); //查找字符串中最后一次出現(xiàn)的點號(.)的位置。String suffix = originalFilename.substring(index); //從字符串的指定索引位置開始截取字符串,直到字符串的末尾。String filename = UUID.randomUUID().toString().toUpperCase() + suffix;File dest = new File(dir,filename); //在dir下創(chuàng)建一個叫filename的文件,此時它是一個空文件
// 將參數(shù)file中的數(shù)據(jù)寫入到空文件當(dāng)中try{file.transferTo(dest); //將file中的數(shù)據(jù)寫入到dest文件中}catch (FileStateException e){throw new FileStateException("文件狀態(tài)異常");}catch (IOException e){throw new FileUploadIOException("文件讀寫異常");}Integer uid = getuidFromSession(session);String username = getUsernameFromSession(session);
// 返回頭像路徑/upload/test.png,相對路徑String avatar = "/upload"+filename;userService.changeAvatar(uid,avatar,username);
// 返回用戶頭像的路徑給前端頁面,將來用于頭像的展示使用return new JsonResult<>(OK,avatar);}
這里沒有辦法測試,數(shù)據(jù)類型模擬不出來,file每一種類型的文件編碼都不一樣。?
🚀4.上傳頭像 -前端頁面
在upload頁面中編寫上傳頭像的代碼。.ajax()請求它可以把一個數(shù)據(jù)解析成一個大串,來拼接在參數(shù)名的后邊,但file可以直接把一個文件作為一個整體提交。表單中action可以整體提交。
說明:如果直接使用表單進行文件的上傳,需要給表單顯示的添加一個屬性:
enctype="multipart/form-data";聲明出來,不會將目標(biāo)文件的數(shù)據(jù)結(jié)構(gòu)做修改再上傳,不同字符串。
🚀5.解決BUG
?5.1更改默認(rèn)的大小限制
SpringMVC默認(rèn)為1MB文件可以進行上傳,手動的去修改SpringMVC默認(rèn)上傳文件愛的大小。
方式一:直接可以在配置文件中進行配置:
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=15MB
?5.2顯示頭像
頁面中通過ajax的請求來提交文件,提交完成后返回了json串,解析出data中的數(shù)據(jù),設(shè)置到img頭像標(biāo)簽的src屬性上就可以了。
1.在這里把submit改成button,因為submit不能夠添加點擊事件。
<input id="btn-change-avatar" type="button" class="btn btn-primary" value="上傳" />
2.這里原來的serialize提交的是一個串的類型,不能夠解析文件,所以要換成類似一下方式:
- serialize():可以將表單數(shù)據(jù)自動拼接成key=value的結(jié)構(gòu)進行提交給服務(wù)器,一般提交是普通控件類型中的數(shù)據(jù)(text/password/radio/checkbox)。
- FromData類:將表單中的數(shù)據(jù)保持原有的結(jié)構(gòu)進行提交。new FromData($("#form")[0]);將form表單中某一個元素的整體值作為FromData()創(chuàng)建對象的數(shù)據(jù),所以這個對象的格式就放在了這個對象當(dāng)中,這樣格式就不會被看做其他的對象被更改或調(diào)整。
- ?ajax默認(rèn)處理數(shù)據(jù)時按照字符串的形式進行處理,以及默認(rèn)會采用字符串的形式進行提交數(shù)據(jù)。關(guān)閉這兩個默認(rèn)的功能。
new FromData($("#form")[0])
?5.3登錄后顯示頭像
可以更新頭像成功后,將服務(wù)器返回的頭像路徑保存在客戶端的cookie對象中,然后每次檢測到用戶打開上傳頭像頁面,在這個頁面中通過ready()方法來自動監(jiān)測去讀取cookie中的頭像并設(shè)到src上。
1.設(shè)置cookie中的值:
導(dǎo)入cookie.js的文件:
<script src="../bootstrap3/js/jquery.cookie.js" type="text/javascript" charset="utf-8"></script><script src="../js/autoLogin.js" type="text/javascript"></script>
調(diào)用cookie的方法:
$.cookie(key,value,time); //單位:天(存活時間)
2.在upload.html頁面先引入cookie.js文件
<script src="../bootstrap3/js/jquery.cookie.js" type="text/javascript" charset="utf-8"></script><script src="../js/autoLogin.js" type="text/javascript"></script>
3.在upload.html頁面通過ready()自動讀取cookie中的數(shù)據(jù)。
$(document).ready(function (){let avatar = $.cookie("avatar");// 將cookie中的值獲取出來設(shè)置到頭像src屬性上$("#img-avatar").attr("src",avatar);});
5.4顯示最新頭像
在更改完頭像后,將最新的頭像地址,再次保存到cookie,同名保存會覆蓋原有cookie中的值。
$.cookie("avatar",json.data,{expires: 7});
🎃1.新增收貨地址-數(shù)據(jù)表創(chuàng)建
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 '區(qū)-名稱',area_code CHAR(6) COMMENT '區(qū)-行政代號',zip CHAR(6) COMMENT '郵政編碼',address VARCHAR(50) COMMENT '詳細地址',phone VARCHAR(20) COMMENT '手機',tel VARCHAR(20) COMMENT '固話',tag VARCHAR(6) COMMENT '標(biāo)簽',is_default INT COMMENT '是否默認(rèn):0-不默認(rèn),1-默認(rèn)',created_user VARCHAR(20) COMMENT '創(chuàng)建人',created_time DATETIME COMMENT '創(chuàng)建時間',modified_user VARCHAR(20) COMMENT '修改人',modified_time DATETIME COMMENT '修改時間',PRIMARY KEY (aid)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
?🎃2.新增收貨地址-創(chuàng)建實體類
創(chuàng)建一個類Address類,在類中定義表的相關(guān)字段,采用駝峰命名方式,最后再去繼承BaseEntity類。
//收貨地址數(shù)據(jù)的實體類
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各功能的開發(fā)順序
當(dāng)前收貨地址功能模塊:列表的展示、修改、刪除、設(shè)置默認(rèn)、新增收貨地址。開發(fā)順序:新增收貨地址->列表展示->設(shè)置默認(rèn)收貨地址->刪除收貨地址->修改收貨地址。
?1.2規(guī)劃需要執(zhí)行的SQL語句
1.對應(yīng)的插入語句:
insert into t_address(除aid外的字段列表) values(字段值列表)
2. 一個用戶的收貨地址最多只能有20條數(shù)據(jù)對應(yīng)。在插入用戶數(shù)據(jù)之前先做查詢操作。收貨地址邏輯控制方面的一個異常。
select count(*) from t_address where uid=?
?1.3接口與抽象方法
1.創(chuàng)建一個新的接口Address,在這個接口中來定義上面兩個SQL語句抽象方法定義。
/*** 插入用戶的收貨地址* @param address 收貨地址數(shù)據(jù)* @return 受影響的行數(shù)*/Integer insert(Address address);/*** 根據(jù)用戶的id統(tǒng)計收貨地址的數(shù)量* @param uid 用戶的id* @return 當(dāng)前用戶的收貨地址總數(shù)*/Integer count(Integer uid);
?1.4配置SQL映射
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<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_delete" property="isDelete"></result><result column="created_user" property="createdUser"></result><result column="created_time" property="createdTime"></result><result column="modified_user" property="modifiedUser"></result><result column="modified_time" property="modifiedTime"></result></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) values (#{uid}, #{name}, #{provinceName}, #{provinceCode}, #{cityName}, #{cityCode}, #{areaName},#{areaCode}, #{zip}, #{address}, #{phone}, #{tel}, #{tag}, #{isDefault}, #{createdUser},#{createdTime}, #{modifiedUser}, #{modifiedTime})</insert><select id="countByUid" resultType="java.lang.Integer">select count(*) from t_address where uid=#{uid}</select>
</mapper>
2.在test下的mapper文件夾下創(chuàng)建一個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.新增收貨地址-業(yè)務(wù)層
?4.1規(guī)劃異常
1.如果用戶是第一次插入收貨地址,規(guī)則:當(dāng)用戶插入的地址是第一條時,需要將當(dāng)前地址作為默認(rèn)收貨地址,如果查詢到統(tǒng)計總數(shù)為0,則將當(dāng)前地址的is_default設(shè)置為1。查詢統(tǒng)計的結(jié)果為0不代表異常。
查詢到的結(jié)果大于20,這時候需要拋出業(yè)務(wù)控制的異常AddressCountLimitException異常。自行創(chuàng)建這個異常。
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.插入數(shù)據(jù)時產(chǎn)生異常。?
?4.2接口與抽象方法
1.創(chuàng)建一個IAddressService接口,在里面定義業(yè)務(wù)的抽象方法。
void addNewAddress(Integer uid,String username,Address address);
2.創(chuàng)建一個AddressServiceImpl實現(xiàn)類,去實現(xiàn)接口中的抽象方法。
在配置文件中定義數(shù)據(jù)
#Spring讀取配置文件中數(shù)據(jù):@Value(${user.address.max-count})
user.address.max-count=20
?4.3實現(xiàn)抽象方法?
在實現(xiàn)類中實現(xiàn)業(yè)務(wù)控制 。
//新增收貨地址的實現(xiàn)類
@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) {
// 調(diào)用收貨地址統(tǒng)計的方法Integer count = addressMapper.countByUid(uid);if(count >= 20){throw new AddressCountLimitException("用戶收貨地址超出上限");}// uid、isDeleteaddress.setUid(uid);Integer isDelete = count == 0?1:0; //1表示默認(rèn),0表示不默認(rèn)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("插入用戶地址時產(chǎn)生未知異常");}}}
3.測試業(yè)務(wù)層功能是否正常。AddressServiceTests測試來測試業(yè)務(wù)功能。
@Testpublic void addNewAddress(){Address address = new Address();address.setPhone("11874583927");address.setName("備備");addressService.addNewAddress(6,"小明",address);}
🎃5.新增收貨地址-控制層
?5.1處理異常
業(yè)務(wù)層拋出了收貨地址總數(shù)超標(biāo)的異常,在BaseController中進行處理。
else if(e instanceof AddressCountLimitException) {result.setState(4003);result.setMessage("用戶收貨地址超出上限的異常");}
?5.2設(shè)計請求
請求路徑:/addresses/add_new_address
請求方式:POST
請求數(shù)據(jù):Address address,HttpSession session
響應(yīng)結(jié)果:JsonResult<Void>
?5.3處理請求
在控制層創(chuàng)建AddressController來處理用戶收貨地址的請求和響應(yīng)。
@RequestMapping("add_new_address")public JsonResult<Void> 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?name=tom&phone=1826478328?進行測試。
🎃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.state==200){alert("新增收貨地址成功");}else{alert("新增收貨地址失敗");}},error:function (xhr){alert("新增收貨地址時產(chǎn)生未知的異常"+xhr.message);}});});</script>