在springboot框架下使用mybatis開發遇到的一些問題以及解決方案(postgresql資料庫)
[email protected]和@mapperscan。二者的作用是一樣的,如果不想在每個mapper上加@mapper,就直接使用@mapperscan。
2.使用mybatis自動生成mapper時,如果生成多次,會直接在mapper.xml檔案裡面追加重複程式碼。
3.mybatis自動生成的mapper.xml檔案需要配置一下,才能正確載入:
mybatis.mapper-locations=classpath:/mapper/*.xml(寫在application.properties裡面)
否則會報錯:Invalid bound statement (not found)
4.mybatis自動生的example是和model一個名稱空間下的,它們實際上並不是一個東西,需要分開。但是mybatis目前還不支援分開,需要手動把example放到其他名稱空間下。注意修改一下自動生成的mapper.xml檔案,不然會報錯,因為ide不提示這裡面的錯誤。
5.mybatis不支援uuid,而且程式碼中習慣將uuid當作string型別來處理,這個時候需要在mybatis自動生成的時候重寫一下uuid型別的列。方法如下:
<table schema="public" tableName="block"> <columnOverride column="id" javaType="java.lang.String" jdbcType="VARCHAR"/> </table>
同時針對這種mybatis不支援的資料型別,你還需要用到typehandler,具體typehandler可參考官方文件,這裡提供一個我寫好的將postgresql裡面的uuid[]轉換成java裡面的list<string>的handler寫法,其中mybatis是VARCHAR型別的:
@MappedJdbcTypes(JdbcType.VARCHAR) public class ArrayToStringListHandler extends BaseTypeHandler<List<String>> { @Override public void setNonNullParameter(PreparedStatement preparedStatement, int i, List<String> strings, JdbcType jdbcType) throws SQLException { String str=String.join(",",strings); //postgresql陣列是以{}開始的 str="{"+str+"}"; preparedStatement.setString(i, str); } @Override public List<String> getNullableResult(ResultSet resultSet, int i) throws SQLException { return null; } @Override public List<String> getNullableResult(ResultSet resultSet, String s) throws SQLException { java.sql.Array array= resultSet.getArray(s); if (array==null) return null; List<String> list=new LinkedList<>(); for (Object o:(Object[]) array.getArray()) { list.add(o.toString()); } return list; } @Override public List<String> getNullableResult(CallableStatement callableStatement, int i) throws SQLException { return null; } }
8.mybatis裡面example型別強制轉換的問題,有些欄位的型別比較特殊,比如id經常用uuid,這個時候生成的sql語句要加上::uuid才行(postgresql語法),但是example不直接支援增加型別強制轉換,這裡我想到的解決辦法是修改mybatis自動生成的原始碼,方法如下:
a)首先要在example檔案裡面找到Criterion這個類,在裡面加上一個屬性property,這個是用來標識你的屬性名的,根據你的屬性名來判斷哪些地方需要加上強制轉換。
private String property;
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
b)然後修改特殊型別欄位條件方法,比如我用的資料庫是postgresql,而我的id是uuid型別的。最初mybatis生成的方法是這樣的
public Criteria andIdNotEqualTo(String value) {
addCriterion("id <>", value, "id");
return (Criteria) this;
}
這個方法是給sql加上id不等於某個值的條件,因為我傳進去的是字串,如果不加::uuid轉換,資料庫會報錯。既然我們已經把屬性名id傳進去了,所以我們修改一下addCriterion這個方法,它原本是這樣的
protected void addCriterion(String condition, Object value, String property) {
if (value == null) {
throw new RuntimeException("Value for " + property + " cannot be null");
}
Criterion criterion=new Criterion(condition, value);
criteria.add(criterion);
}
我們加一句: criterion.setProperty(property);讓它變成這樣
protected void addCriterion(String condition, Object value, String property) {
if (value == null) {
throw new RuntimeException("Value for " + property + " cannot be null");
}
Criterion criterion=new Criterion(condition, value);
criterion.setProperty(property);
criteria.add(criterion);
}
c)這個時候,傳給mapper.xml的Criterion 就帶有屬性名了,然後把mapper.xml裡面的Example_Where_Clause,或者其他用到example的語句改一下,改成這樣
<when test="criterion.singleValue and criterion.property=='id'">
and ${criterion.condition} #{criterion.value}::uuid
</when>
<when test="criterion.singleValue and criterion.property!='id'">
and ${criterion.condition} #{criterion.value}
</when>
你會注意到我這裡面加了一個判斷:and criterion.property=='id',也就是說當屬性是id的時候,我要給語句sql上加一個::uuid的轉換。其他的則不加。這樣,就解決了example特殊型別轉換的問題了。當然,更復雜的邏輯,可能xml裡面的條件判斷要複雜一些。然後你再使用example的時候還像原來那樣正常使用就可以了:
UserExample example = new UserExample();
example.createCriteria().andIdNotEqualTo(user.getId());
int count = userMapper.countByExample(example);
9.restful風格的介面,目前很多專案都在用,但是這種風格的介面有一個問題,我們知道當用戶不傳某個引數的時候,這個時候這個引數實際上是為null的,mybatis會通過判斷某個引數是否null來決定要不要更新該欄位,假如使用者就是想讓某個欄位為null,傳了一個null值進來,這樣就和restful介面衝突了,針對這個問題,很多人都是特殊欄位特殊處理,比如,整型欄位,不允許為null,如果想為null,就用-1代替,但我始終覺得這不算一個好的解決方案。實際上使用者傳和不傳,通過map<string,object>來接收使用者推送的body,是有區別的,當用戶傳了這個引數,不管傳什麼值,這個map就包含該鍵值對,如果不傳,就不包含。而如果用一個實體類來接收使用者推送的body,當用戶傳遞某個引數的值為null時,和不傳這個引數是完全一樣的結果。這會引起最終介面執行結果出錯。我目前尚且不知道該怎麼很好的解決這個問題,後面有時間再慢慢研究。