利用spring boot和mybatis構建介面
想做一個心情收納箱的軟體,幫助我在心情低落的時候找找合適的排憂途徑。人嘛,總有低落的時候,只有在低落的時候能夠快速恢復,才能不會更低落
注:因為這是真實專案下來的,所以我把import去掉了
1、首先應該弄明白 mapper.xml 和 mapper 之間是如何聯絡起來的
看一個例子
<?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="xxxx.xxx.xxx.mapper.ChangeUserInfoMapper">
<resultMap id="changeUserInfoMap" type="ChangeUserInfoDto">
<result column="user_name" property="userName"/>
<result column="email" property="email" />
<result column="org_name" property ="orgName"/>
<result column="org_web_site" property="orgWenSite" />
<result column="image" property="image"/>
</resultMap>
<select id="queryById" parameterType="Long" resultMap="changeUserInfoMap">
SELECT
A.user_name
FROM t_user AS A
WHERE A.uid = #{id}
</ select>
<update id="updateUserInfo">
UPDATE t_user SET
<if test="orgName != null">
org_name = #{orgName}
</if>
<if test="orgWebSite != null">
org_web_site = #{orgWebSite}
</if>
<if test="image != null">
image = #{image}
</if>
WHERE uid = #{uid}
</update>
</mapper>
注意:如果select的返回值是Integer等型別,應該寫成resultType而不是resultMap
namespace 就是該mapper.xml對應的實體類mapper
@Mapper
@Component(value = "changeUserInfoMapper")
public interface ChangeUserInfoMapper {
ChangeUserInfoDto queryById(long id);
Integer updateUserInfo(@Param("uid") long uid, @Param("orgName") String orgName, @Param("orgWebSite") String orgWebSite,@Param("image") byte[] image);
}
實體mapper中的方法名就是mapper.xml中的id,這是一一對照關係。在mapper.java中方法中的引數,如果大於一個,要使用@Param註解,否則mybatis找不到對應的引數,@Param中的名字應該與mapper.xml中的變數名保持一致。如果只有一個引數,@Param就無所謂要不要了。方法的返回型別與.xml中的resultMap型別一致。可以自定義resultMap型別,自定義的resultMap的type最好使用通用的資料傳輸Dto,這樣使得資料傳輸具有統一性,方便程式碼的編寫和清晰。
以上是基於spring boot開發情況下,spring boot會自動對mapper.xml和mapper進行配置,如果非spring boot環境,需要自己手動寫。
2、然後要知道做一個數據交換的 DTO 的必要性
資料傳輸物件,把在一個介面中需要的所有必要引數全部放到一個POJO中,包括從資料庫中獲取的和從使用者處得到的。從開始接收使用者資料開始,一直都使用這一個資料傳輸物件,可以有效避免程式碼引數過多變得特別凌亂不好維護的場景。同時,寫成dto後可以有效的把邏輯層剝離出來。
public class ChangeUserInfoDto {
private long uid;
private String userName;
private String email;
private String orgName;
private String orgWebSite;
private byte[] image;
private boolean validate;
public long getUid() {
return uid;
}
public void setUid(long uid) {
this.uid = uid;
}
public String getUserName() {
return userName;
}
public String getEmail() {
return email;
}
public String getOrgName() {
return orgName;
}
public String getOrgWebSite() {
return orgWebSite;
}
public byte[] getImage() {
return image;
}
public void setValidate(boolean validate) {
this.validate = validate;
}
public void setUserName(String userName) {
this.userName = userName;
}
public void setEmail(String email) {
this.email = email;
}
public void setImage(byte[] image) {
this.image = image;
}
public void setOrgName(String orgName) {
this.orgName = orgName;
}
public void setOrgWebSite(String orgWebSite) {
this.orgWebSite = orgWebSite;
}
public boolean isValidate() {
return validate;
}
}
3、service 如何與mapper聯絡起來
service層一般是處理的核心,在處理業務邏輯的時候,可能會需要持久化資料進行支援,這時候只需要呼叫相應的mapper就可以了。所以,在service中採用構造器注入的方式將mapper注入到service中。
@Service
public class ChangeUserInfoServiceImpl implements ChangeUserInfoService {
private ChangeUserInfoMapper changeUserInfoMapper;
public ChangeUserInfoServiceImpl(ChangeUserInfoMapper mapper){ this.changeUserInfoMapper = mapper;}
@Override
public ChangeUserInfoDto checkUser(long id) {
ChangeUserInfoDto dto = changeUserInfoMapper.queryById(id);
if (Objects.nonNull(dto)){
if (dto.getUserName() != null){
dto.setValidate(true);
dto.setUserName("");
dto.setUid(id);
}
}
return dto;
}
@Override
public Integer updateUserInfo(ChangeUserInfoDto dto) {
int ans = changeUserInfoMapper.updateUserInfo(dto.getUid(), dto.getOrgName(), dto.getOrgWebSite(), dto.getImage());
return ans;
}
}
這裡要說明的是,將service定義成介面,然後使用類來實現service的介面式程式設計,有助於控制器中對service的自動注入,自動注入會找到實現該service介面的Impl,然後自動注入到程式碼中。如果同時有多個類實現了該service,自動注入的時候就會報錯,得在優先順序最高的Impl上面加上@primary的註釋,這樣就會自動注入該Impl了。
public interface ChangeUserInfoService {
ChangeUserInfoDto checkUser(long id);
Integer updateUserInfo(ChangeUserInfoDto dto);
}
4、建立rest ful api 要會的東西
支撐的東西研究完了,應該開始寫接收請求了,控制器。為了能夠更好的解釋上面說的內容,這裡如何進行呼叫的我就沒有模糊化
首先,
@Controller告訴spring boot這是一個控制器
然後通過@Autowired自動注入需要的service
@RequestMapping裡面主要有倆引數,value代表請求的路徑,可以是一個列表,即多個路徑用同一個方法處理。method指的是請求的方法
@ResponseBody告訴spring boot返回的結果就是返回值,如果不加這個註釋,spring boot會到模板中去找對應的頁面,如果要返回一個鍵值對,可以使用Map<String, Object>的方式,這裡,我只返回了一個數字。
如果傳進來的是一個檔案,使用類似
@RequestParam(“userImage”) final MultipartFile file的方法就可以了
其中括號中的變數名應該與傳遞的引數的名字保持一致,後面的變數名是一個別名。
@Controller
@SessionAttributes(Constant.USER_SESSION_NAME)
public class UserInfoController {
@Autowired
ChangeUserInfoService changeUserInfoService;
@RequestMapping(value = Path.CHANGE_PROPERTY, method = RequestMethod.POST)
@ResponseBody
public Integer changeProperty(@ModelAttribute(Constant.USER_SESSION_NAME) User user,
@RequestParam("newinfo") final String newinfo,
@RequestParam("flag") final int flag){
long uid = user.getUserId();
ChangeUserInfoDto dto = changeUserInfoService.checkUser(uid);
if (Objects.nonNull(dto) && dto.isValidate()){
// TODO: 2018/12/13 執行更新操作
if (flag == 1){
dto.setOrgName(newinfo);
}
int flagdo = 0;
if (flag == 2){
boolean url = WebSiteUtil.checkUrl(newinfo);
if (url){
dto.setOrgWebSite(newinfo);
}else {
flagdo = 1;
}
}
if (flagdo == 0){
int check = changeUserInfoService.updateUserInfo(dto);
if (check > 0){
return 1;
}else {
return 0;
}
}
}
return 0;
}
@RequestMapping(value = Path.ChANGE_USER_IMAGE, method = RequestMethod.POST)
@ResponseBody
public Integer changeUserImage(@ModelAttribute(Constant.USER_SESSION_NAME) User user,
@RequestParam("userImage") final MultipartFile file) throws IOException {
if (!file.isEmpty()){
long uid =user.getUserId();
ChangeUserInfoDto dto = changeUserInfoService.checkUser(uid);
if (Objects.nonNull(dto) && dto.isValidate()){
dto.setImage(file.getBytes());
int check = changeUserInfoService.updateUserInfo(dto);
if (check > 0){
return 1;
}
}
return 0;
}else {
return 0;
}
}
}
5、把一連串操作串聯起來就成啦
根據上面的討論流程,我們可以發現執行流程是這樣的 請求->Controller->service->mapper->mapper.xml->mapper->service->Controller->響應這樣的。
6、介面開發的真實開發流程
根據執行流程,可以發現其實我們的開發流程也應該是先寫Controller,然後寫需要完成什麼樣的服務,需要進行哪些資料傳輸互動,然後設計DTO,資料庫需要做哪些支援,然後設計mapper,編寫程式碼就可以了。