我正在尝试使用dynamic-sql方法和sql-builder方法创建一个动态更新语句,但我只管理它让它适用于string
数据类型。在构造更新语句时,我不确定如何“转换”为正确的数据类型。
我想要实现的是使用Map<String, Object>
或实际的 pojo生成更新语句Post
Post
看起来像这样
public class Post {
private Integer id;
private String title;
private String body;
private LocalDateTime createdAt;
private String createdBy;
private LocalDateTime updatedAt;
private String updatedBy;
}
原因Map<String, Object>
是为了更容易遍历集合并构造语句。使用 pojo 需要我使用reflection
我尽量不使用的。
在了解我是如何做到的之前
这就是在 pojo 中使用普通更新语句时的样子
@PutMapping("/{id}")
public Post updateById(@PathVariable Integer id, @RequestBody Post post) {
return this.postService.updateById(id, post);
}
@Update("UPDATE POST SET title = #{p.title}, body = #{p.body}, createdAt = #{p.createdAt}, createdBy = #{p.createdBy}, updatedAt = #{p.updatedAt}, updatedBy = #{p.updatedBy} WHERE id = #{id}")
public boolean updateById(@Param("id") Integer id, @Param("p") Post post);
这将导致
2021-10-30 12:03:15.037 DEBUG 15988 --- [nio-8080-exec-2] c.b.s.s.post.PostMapper.updateById : ==> Preparing: UPDATE POST SET title = ?, body = ?, createdAt = ?, createdBy = ?, updatedAt = ?, updatedBy = ? WHERE id = ?
2021-10-30 12:03:15.064 DEBUG 15988 --- [nio-8080-exec-2] c.b.s.s.post.PostMapper.updateById : ==> Parameters: jsonpatch1(String), bo21(String), 2021-10-30T12:03:14.954483(LocalDateTime), stackoverflow(String), 2021-10-30T12:03:14.954483(LocalDateTime), stackoverflow(String), 65(Integer)
因此,我尝试这样做
// What this does is to strip off all the null values, and keep only those with value
// and convert into a map to pass and run in the dynamic sql later
@PatchMapping(path = "/{id}")
public Post patchById(@PathVariable Integer id, @RequestBody Post post) {
ObjectMapper om = new ObjectMapper();
om.setSerializationInclusion(Include.NON_NULL);
om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
om.registerModule(new JavaTimeModule());
Map<String, Object> mp = om.convertValue(post, new TypeReference<Map<String, Object>>(){});
return this.postService.patchById(id, mp);
}
它去哪里mapper
看起来像这样
@Update({
"<script>",
"UPDATE POST",
"<set>",
"<foreach item='item' index='index' collection='p.entrySet()'>",
"${index} = #{item},",
"</foreach>",
"</set>",
"WHERE id = #{id}",
"</script>"
})
public boolean update(@Param("id") Integer id, @Param("p") Map<String, Object> post);
如果所有值都是string
. 但是,如果存在 的字段LocalDateTime createdAt
,则该createdAt
字段被视为string
类型
021-10-30 15:21:27.666 DEBUG 12324 --- [nio-8080-exec-2] c.b.s.s.post.PostUpdateMapper.update : ==> Preparing: UPDATE POST SET createdAt = ?, title = ?, body = ?
WHERE id = ?
2021-10-30 15:21:27.669 DEBUG 12324 --- [nio-8080-exec-2] c.b.s.s.post.PostUpdateMapper.update : ==> Parameters: 2021-09-10T11:31:07.5306869(String), jsonpatch1(String), bo221(String), 65(Integer)
我相信,那是因为我将其切换到Map<String, Object>
,因此类型 (LocalDateTime) 因转换而丢失。但是,如果我要使用 pojoBean
我会有这样的东西
@PatchMapping(path = "/{id}")
public Post patchById(@PathVariable Integer id, @RequestBody Post post) {
return this.postService.patchById(id, post);
}
@UpdateProvider(type=SQLUpdate.class, method = "update")
public boolean update(Integer id, Post post);
// just a poc to see if it works
public String update(Integer id, Post post) throws IllegalArgumentException, IllegalAccessException {
Field[] f = post.getClass().getDeclaredFields();
return new SQL() {{
UPDATE("POST");
for(Field field: f) {
field.setAccessible(true);
if (field.get(post) != null) {
SET(field.getName() + " = '" + field.get(post) + "'");
}
}
WHERE("id = " + id);
}}.toString();
}
所以无论哪种方式,我都不确定如何传入正确的类型,以便它可以拦截并正确运行
如果我能做到这一点,这将是更理想的解决方案
@Update({
"<script>",
"UPDATE POST",
"<set>",
// being able to check if the value is null and set the field and value dynamically
"<if test='#{p.value} != null>p.fieldname = #{p.value}",
"</set>",
"WHERE id = #{id}",
"</script>"
})
public boolean update(@Param("id") Integer id, @Param("p") Post post);
让我知道是否需要更多信息,或者是否有更好的方法来实现我想做的事情
谢谢!
PS:我知道并且我得到它与mybatis-dynamic-sql lib 一起使用,但有兴趣知道它是否可以在不使用 lib 的情况下工作