在springboot框架下使用mybatis开发遇到的一些问题以及解决方案(postgresql数据库)

1.@mapper和@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时,和不传这个参数是完全一样的结果。这会引起最终接口执行结果出错。我目前尚且不知道该怎么很好的解决这个问题,后面有时间再慢慢研究。

猜你喜欢

转载自blog.csdn.net/dap769815768/article/details/82744546