20231219 Spring Boot 2.7.11加入Mybatis Starter XML讀取不到踩坑實錄🫠
TL:DR
且這篇內容超腦霧,就是個八八坑道(??),我也不知道為什麼會失常成這樣,搏君一笑,單純做個紀錄.....
前情提要
我按照一個教學(不過這邊有個巨大的過失,就是我看到文末才發現作者寫了"本教學內容已更新到Spring Boot 3.0",我知道八成會出問題了=-=...),大概做了這些事情:
- 加入mybatis-spring-boot-starter依賴(一開始是加3.0.0,一直出錯,按照官方建議先降到2.3.0)
- 寫了一個XML config並在
application.yaml
指定路徑 - 開好目錄結構放入相應的內容物,並在
application.yaml
指定mapper XML路徑(⚠️注意:這邊路徑是包版後jar/war檔內的路徑)
UserMapper.xml
<?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="org.example.mybatis.UserMapper" > <resultMap id="BaseResultMap" type="org.example.mybatis.vo.User" > <id column="username" property="username" jdbcType="VARCHAR" /> <result column="password" property="password" jdbcType="VARCHAR" /> <result column="name" property="name" jdbcType="VARCHAR" /> </resultMap> <sql id="Base_Column_List" > username, password, name </sql> <select id="getAll" resultMap="BaseResultMap" > SELECT <include refid="Base_Column_List" /> FROM "user" </select> </mapper>
vo
用途等價於entity的物件,Mybatis生成工具預設值產生的放置目錄即叫做vo
package org.example.mybatis.vo; import lombok.Data; import org.apache.ibatis.type.Alias; @Data @Alias("User") // 注意這邊,為了避免造成更多路徑問題,我沒有在application.yaml寫type-aliases-package: org.example.mybatis.vo讓他自己去掃描,而是用註釋手動註冊,可以依個人習慣調整 public class User{ String username; String password; String name; }
dao
如果用生成器預設會放在名為dao的目錄內,因為我是手寫,在不會影響讀取運作,就放得隨意一點了
package org.example.mybatis; import org.apache.ibatis.annotations.Mapper; import org.example.mybatis.vo.User; import java.util.List; @Mapper // 注意這邊,也是初期為了避免造成更多路徑問題,選擇手動註釋,而非在Spring應用程式的Main方法上方加入@MapperScan自動掃描,可以依個人習慣調整 public interface UserMapper { List<User> getAll(); // 以下是之後預計加入的query // User getOne(Long id); // void insert(User user); // void update(User user); // void delete(Long id); }
4.然後很簡略、沒有把業務邏輯放在Service地,直接在Controller弄個API(幾乎是搬運之前JPA練習的),再以Postman訪問
package org.example.controller; import java.util.ArrayList; import java.util.List; import org.example.model.response.UserListModel; import org.example.mybatis.UserMapper; import org.example.mybatis.vo.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @RestController public class MybatisTestController { @Autowired UserMapper userMapper; @GetMapping(value = "getall", produces = MediaType.APPLICATION_JSON_VALUE) public String userList() throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); //TODO這東西賊耗能的應該做成一個單例:( UserListModel userListModel = new UserListModel(); // 這是之前給API用的傳輸物件(response model),跟Mybatis無關 ArrayList<UserListModel.user> userList = new ArrayList<>(); // 那個傳輸物件有inner class List<User> userEntities = userMapper.getAll(); // 這邊才是呼叫Mybatis DAO查詢結果 userEntities.forEach(node -> { // 查詢結果裝起來送給client UserListModel.user user = new UserListModel.user(); user.setUsername(node.getUsername()); user.setName(node.getName()); userList.add(user); }); userListModel.setUserList(userList); return objectMapper.writeValueAsString(userListModel); // 其實不用這樣也能序列化出JSON物件,只是JPA那次寫法比較舊又是照搬沒改 } }
說好的ObjectMapper的單例ㄋ
以上都做完後,其實首先我在包版時就有遇到
mybatis: config-location: classpath:/org/example/mybatis/mybatis-config.xml # 我這裡面只放一些基本型態的對應
application.yaml
這段造成的錯誤
我昨天快下班前已經幾乎是腦霧狀態了,還以為是那篇文章寫錯屬性...我竟然給location尾巴加了個s,包版是過了,但到底在幹嘛啦,根本沒這個屬性@@,總之就是多埋一個bug,所以今天看到這篇發現就算有需要也可以全寫在application.yaml
啊(而且starter好像不用額外對應...?),就把它及yaml那條設定移除了,但要說的問題還是存在
昨天更顯著的問題是:訪問API他總是說我Invalid bound statement,對應不到我mapper.xml內的查詢方法實作,查詢網路上的討論,通常他們會要我們確定包版後的jar/war解壓縮後,xml檔案有沒有也被一併加入,沒有的話要運用一些奇巧(例如放在resource目錄或在pom.xml
內寫build規則要包含*.xml
),所以我的問題應該是跟這種方法沒關係的,但我昨天秉持著死馬當活馬醫的態度(?),我還是花時間照做了,但幾乎還是不影響=-=
昨天下班回家滿頭問號,只是離開公司後有清醒點,想起來版本對應蠻重要的,找了一篇文章存著今天來讀,今天早上打開才發現他是搬運MybatisPlus的還沒寫清楚(...
所以找到這兩篇適用Spring Boot 2.7的教學,寫得都挺好: 1 2
本來想整個砍掉重練跟著做,我就一步一步順著步驟,多得拔、少的拆,想說連目錄結構都一樣好了(謎之音:就結果論沒關係啊很迷信欸),版本也跟他們一樣好了
首先是在把Mybatis Starter降到2.2.2
pom.xml
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.2</version> </dependency>
(記得Maven reload,不放心可以先reload->clean->package)(謎之音:這什麼邪教儀式)
然後看到掘金那篇大概1/3?突然💡亮了,我應該不用整個照搬,我趕快去檢查application.yaml
,應該只是這個地方設定出問題了,那就是指定xml位置時路徑應該要是jar/war內的路徑,而非在專案內的路徑,我的情境的正確配置:
mybatis: mapper-locations: classpath:/org/example/mybatis/mapper/*.xml
然後敲API就能看到會員資料了=-=... (我有嘗試過把版本換回官方說可用的2.3.0,但又出現Invalid bound statement了)
好亂的心路歷程,稍微濃縮一下可以提供的方向
此情境故障排除的幾個方向
- 確認Spring Boot跟Mybatis starter真正對應的版本(最好用parent寫,問題就不會這麼多)
application.yaml
內指定xml檔案的路徑是否正確
要把頭埋起來了Orz|||
後日談
話說要不是我懶一直用Spring Boot 2.7那個遊樂場(?)一直試東西 直上Spring Boot三代大概不會遇到這種問題吧-.-
另外why XML?因為是老專案要升級,不過話說回來是要升到Spring Boot 3啊啊啊,但Spring Boot 3的教學資源蠻多的,應該不會翻車成這兩天這樣
20231208 Java Stream之美
- 用了Stream后,代码反而越写越丑?
- 這幾天有看到一篇可讀性的文章 突然找不到了QQ
20231208 Java Interface Q&A
- Q:負責實作的class可以有自己的method嗎? A:可以
但像下面這樣會找不到method
BeverageFactory bevergeFactory = new TestFactory();
bevergeFactory.strike();
這樣才可以
TestFactory testFactory = new TestFactory();
testFactory.strike();
- Q:一定要實作interface的所有方法嗎? A:除了default一定要,可以看下面的例子
package factory; public interface BeverageFactory { public Beverage createBeverage(); }
package factory; public class TestFactory implements BeverageFactory { // @Override // public Beverage createBeverage() { // System.out.println("橘子汁"); // return new OrangeJuice(); // } public void strike() { System.out.println("發起罷工工廠是不做飲料的"); } }
bevergeFactory = new TestFactory();
bevergeFactory.strike();
Output:
error: TestFactory is not abstract and does not override abstract method createBeverage() in BeverageFactory
20231122列出目錄及下方資料夾所有檔案清單批次檔v1
⚠️已知問題:
web.config
這種檔案不知道為甚麼顯示不出來如果有個要從專案根目錄排除的目錄名稱,跟想要保留子目錄下的目錄名稱雷同時,其政則寫法要再考究
1.製作prodFileList.bat
@echo off dir /s /b /a-d /on | findstr /v /i /c:".git" /c:"vendor" /c:"storage" > fileList.txt
/c:"目錄名稱"
把目錄名稱替換成你要排除的目錄
2.把這個bat放到要產清單的資料夾下方點兩下,取得你的檔案清單
20231115 VSCode Java相關的重構功能
常用 | 效果 |
---|---|
對類/方法/變數名稱按f2 |
批量改名 |
對類/方法/變數名稱按f12 |
查看定義(哪裡有出現) |