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