实际场景
当数据库表数据为这样一个结构时(某个字段存储的值为json数组),在java代码中使用这个字段时需要借助fastjson进行手动转化,比较麻烦;
id | data |
---|---|
1 | [{"id":0,"name":"test0"},{"id":1,"name":"test1"},{"id":2,"name":"test2"},{"id":3,"name":"test3"},{"id":4,"name":"test4"}] |
2 | [{"id":0,"name":"test0"},{"id":1,"name":"test1"},{"id":2,"name":"test2"},{"id":3,"name":"test3"},{"id":4,"name":"test4"}] |
3 | [{"id":0,"name":"test0"},{"id":1,"name":"test1"},{"id":2,"name":"test2"},{"id":3,"name":"test3"},{"id":4,"name":"test4"}] |
4 | [{"id":0,"name":"test0"},{"id":1,"name":"test1"},{"id":2,"name":"test2"},{"id":3,"name":"test3"},{"id":4,"name":"test4"}] |
5 | [{"id":0,"name":"test0"},{"id":1,"name":"test1"},{"id":2,"name":"test2"},{"id":3,"name":"test3"},{"id":4,"name":"test4"}] |
解决方案
第一种:自定义TypeHandler
TypeHandler根据字面意思即为类型处理器
引用官方文档的描述: MyBatis在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型
可以让MybaitsPlus、Mybatis自动帮我们转化
@MappedTypes(List.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class JsonArrayToUserListHandler extends BaseTypeHandler<List<TestData>> {
// 执行插入数据方法 且 实体属性不为空时
// 属性为空的时候也没必要进行相应的处理
@Override
public void setNonNullParameter(PreparedStatement ps, int i, List<TestData> parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, String.valueOf(JSON.toJSON(parameter)));
}
// 根据列名获取数据
// 数据的值可能为空
@Override
public List<TestData> getNullableResult(ResultSet rs, String columnName) throws SQLException {
String data = rs.getString(columnName);
if (data != null) {
return JSON.parseArray(data, TestData.class);
}
return null;
}
// 根据列索引获取数据
// 数据的值可能为空
@Override
public List<TestData> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String data = rs.getString(columnIndex);
if (data != null) {
return JSON.parseArray(data, TestData.class);
}
return null;
}
@Override
public List<TestData> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String data = cs.getString(columnIndex);
if (data != null) {
return JSON.parseArray(data, TestData.class);
}
return null;
}
}
@Data
@TableName(autoResultMap = true) // 低版本mybatisplus 没有autoResultMap = true这个属性可以不用写
public class Test implements Serializable {
@TableId(type = IdType.AUTO)
private Long id;
@TableField(value = "data", typeHandler = JsonArrayToUserListHandler.class) // 在此处配置后使用mybatisPlus提供的方法就可以使用了
private List<TestData> data;
private static final long serialVersionUID = 1L;
}
完成以上配置后即可在mybaitsPlus提供的方法里使用了
如果需要在xml里面写sql还需要增加如下配置:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.practice.mapper.TestMapper">
<resultMap id="BaseResultMap" type="com.practice.entity.Test">
<id property="id" column="id" jdbcType="INTEGER"/>
<result property="data" column="data" typeHandler="com.practice.entity.typehandler.JsonArrayToUserListHandler"/>
</resultMap>
<select id="getList" resultMap="BaseResultMap">
select * from test
</select>
<update>
update test set data = #{data, typeHandler=com.practice.entity.typehandler.JsonArrayToUserListHandler}
</update>
</mapper>
第二种:直接使用Mybatis提供的
mybatisPlus提供现成的TypeHandler,可以直接使用
@TableField(value = "data", typeHandler = FastjsonTypeHandler.class)
private List<TestData> data;
@TableField(value = "data", typeHandler = JacksonTypeHandler.class)
private List<TestData> data;
注:使用UpdateWrapper更新时有bug无法更新
@MapKey注解
使用方式
@Mapper
public interface TestMapper extends BaseMapper<Test> {
@MapKey("id")
Map<Long, Test> getIdMapKey();
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.practice.mapper.TestMapper">
<resultMap id="BaseResultMap" type="com.practice.entity.Test">
<id property="id" column="id" jdbcType="INTEGER"/>
<result property="data" column="data" typeHandler="com.practice.entity.typehandler.JsonArrayToUserListHandler"/>
</resultMap>
<select id="getIdMapKey" resultMap="BaseResultMap">
select * from test
</select>
</mapper>
查询结果
注:key需要唯一
@TableLogic
作用
会在mybatis中的各种查询、更新、删除方法中自动拼接deleted = 0
,过滤调删除的值
字段加完@TableLogic
注解之后使用mybatisPlus提供的一些方法则无法更新该字段;
@TableLogic(value = "0", delval = "1")
private Integer deleted;
无法更新方法(@TableLogic字段)
public interface IService<T> {
boolean updateById(T entity);
@Transactional(rollbackFor = Exception.class)
default boolean updateBatchById(Collection<T> entityList) {
return updateBatchById(entityList, 1000);
}
boolean saveOrUpdate(T entity);
@Transactional(rollbackFor = Exception.class)
default boolean saveOrUpdateBatch(Collection<T> entityList) {
return saveOrUpdateBatch(entityList, 1000);
}
}
public interface BaseMapper<T> {
int updateById(@Param(Constants.ENTITY) T entity);
}
在上述方法中如果说在实体中仅填充了deleted会直接报出语法错误
就是因为直接忽略了标注@TableLogic注解的属性
可更新方法(@TableLogic字段)
- 使用UpdateWrapper并且需要调用其set方法
updateWrapper.set("deleted", 0);
public interface IService<T> {
boolean update(T entity, Wrapper<T> updateWrapper);
default boolean update(Wrapper<T> updateWrapper) {
return update(null, updateWrapper);
}
}
public interface BaseMapper<T> {
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
}
- 使用自己写sql
日期查询时精确度问题
查询时间范围
数据库中大多日期字段是DateTime类型,例 2022-04-25 09:30:00 , 查询传参时日期的精度也要求到秒,否则起止日部分数据可能会丢失。
如果不精确到时分秒,会自动填充为00:00:00
eg: 2022-04-25 -> 2022-04-25 00:00:00
select * from demo where businessDate between '2022-04-25 00:00:00' and '2022-04-25 23:23:59'
按时间排序时
要使用其他字段辅助排序
场景:按照创建日期升序,同一天创建很多条,在翻页时同一条记录在多个页中出现。
分析:在时间相同的情况下,返回数据的顺序会变化。
方案:创建时间(主排序)+ 数据ID(辅助排序),保证查询的幂等性。
Json检索
select * from a1
where 'DT004' member of (JSON_EXTRACT(js,'$[*]'))