1. 程式人生 > >在springboot框架下使用mybatis開發遇到的一些問題以及解決方案(postgresql資料庫)

在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時,和不傳這個引數是完全一樣的結果。這會引起最終介面執行結果出錯。我目前尚且不知道該怎麼很好的解決這個問題,後面有時間再慢慢研究。