SpringBoot版本:2.5.4
Mybatis版本:2.2.0
Mysql版本:8.0
1.@mapper和@mapperscan。二者的作用是一样的,mapper用在单个mapper接口上,mapperscan用在程序入口类上,如果不想在每个mapper上加@mapper,就直接在程序的入口类上加@mapperscan,如下:
2.mybatis自动生成的mapper.xml文件需要在application.properties里配置一下,才能正确加载:
mybatis.mapper-locations=classpath:/mapper/*.xml(写在application.properties里面)
否则会报错:Invalid bound statement (not found)
3.mybatis有个对应的库叫mybatis-generate,它是一个自动根据数据库生成mapper的工具,它同时也会生成一个叫example的东西,这个是自动生成sql条件语句用的,这样就可以减少sql语句的编写,比较冷门。
4.mybatis如何支持json类型,如果你的实体类里面有个字段是类对象,你希望在进行数据库存储的时候,把该实体类转化为json类型存放到数据库,那么你需要提供一个typehandler:
@MappedJdbcTypes(JdbcType.JAVA_OBJECT)
public class ObjectJsonHandler extends BaseTypeHandler<DataEntity> {
Gson gson=new Gson();
@Override
public void setNonNullParameter(PreparedStatement preparedStatement,
int i, DataEntity
dataEntity, JdbcType jdbcType) throws SQLException {
//写入数据库时,转为json
preparedStatement.setObject(i,gson.toJson(dataEntity));
}
@Override
public DataEntity getNullableResult(ResultSet resultSet, String s)
throws SQLException {
//查询数据时,转为对象
String ret=resultSet.getString(s);
if (ret==null) return null;
return gson.fromJson(ret,DataEntity.class);
}
@Override
public DataEntity getNullableResult(ResultSet resultSet, int i)
throws SQLException {
return null;
}
@Override
public DataEntity getNullableResult(CallableStatement callableStatement, int i)
throws SQLException {
return null;
}
}
这里的jdbcType是JAVA_OBJECT,这个类型我看官方的文档并没有,估计是后来新增的。同时我用的mysql数据库是8.0版本的,早期的版本并不支持json,较新的版本是支持的。
写好typehandler之后,xml的resultMap要这么配置:
// 其他的配置这里省略
<resultMap id="BaseResultMap" type="com.camjy.datacloud.entity.DeviceDataEntity">
<result column="data" property="data" jdbcType="JAVA_OBJECT"
javaType="java.lang.Object" typeHandler="com.test.ObjectJsonHandler"/>
</resultMap>
sql语句这么写:
<insert id="insertData" parameterType="com.camjy.datacloud.entity.DeviceDataEntity">
insert into data_table(data)
VALUES (#{data,typeHandler=com.test.ObjectJsonHandler});
</insert>
5.mybatis-generate里面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);
6.restful风格的接口允许传null值进来的问题。这个问题的解决方式是用map接收body,而不再是实体类。如果你用实体类接收,那么你就无法区分是用户传给了你null,还是因为用户没有传导致的null值了。用map接收则没有这个问题,你可以判断map中是否包含这个key,如果包含就表示用户要修改,如果不包含,就表示用户不打算修改,至于所传的值是不是null,你则不需要关心。这样的话,mybatis的mapper应该像下面这么写:
<update id="updateTest" parameterType="Map"> update public.status <set> <if test="_parameter.containsKey('name')"> name = #{name,jdbcType=VARCHAR}, </if> <if test="_parameter.containsKey('remarks')"> remarks = #{remarks,jdbcType=VARCHAR}, </if> </set> where id = #{id,jdbcType=VARCHAR} </update>
判断是否包含该键,如果包含则更新。
7.一个xml配置里面同时执行多条语句,这个需要在application.properties里面进行配置,在数据库连接字符串上增加allowMultiQueries=true:
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?allowMultiQueries=true
然后,你xml的sql语句里面用逗号隔开不同的语句就行了,如下:
<insert id="test" parameterType="com.test.TestEntity">
update test_table set time=#{time} where id=#{id};
insert into test_table(time) VALUES(#{time});
</insert>