使用SpringConverter来优化Ids和Id构成集合的关联关系
我们在进行项目开发的时候,就会遇到这个问题, List ingredients; List ingredients; 同样都是原理集合,一个后端喜欢,一个前端喜欢。在一个POJO里面经常把这两个都写上。都按照对象的关系来说,肯定第一个我们更为青睐,数据库也好查询不是!
1、问题描述
1.1、form表单提交了多个相同name值
以一个煎饼果子为例,制作煎饼果子需要多种原料就有了下方的form表单数据
<input type="checkbox" name="ingredients" th:value="${item.id}" />
<input type="checkbox" name="ingredients" th:value="${item.id}" />
<input type="checkbox" name="ingredients" th:value="${item.id}" />
<input type="checkbox" name="ingredients" th:value="${item.id}" />
1.2、而我们一般用数组接收
举个例子比如说得到id数组,进行批量删除操作。现在我们就给煎饼果子添加原料构成的ID集合
package com.wnx.toca.domain;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 煎饼果子
* @author wangnaixing
* @Classname ChineseHamburger
* @Description TODO
* @Date 2021/12/14 22:43
* @Created by wangnaixing
*/
@Data
public class ChineseHamburger implements Serializable {
/**
* 自增主键
*/
private Long id;
/**
* 制作人姓名
*/
private String name;
/**
* 创建时间
*/
private Date created;
//=======附加属性==========//
/**
* 原料集合
*/
List<Long> ingredients;
//=======附加属性==========//
}
1.3、我们现在要保存这个煎饼果子噢
我们常规想法一定是:
- 先保存煎饼果子信息
- 再添加煎饼果子和原料的关联关系
2、以前我的处理
这里以我已Spring的JdbcTemplate为持久层操作为例子哈
2.1、在原料的Dao上定义上根据ID查询原料的方法
/**
* 根据ID查询煎饼果子原料
* @param id
* @return
*/
Ingredient findById(Long id);
/**
* 根据ID查询煎饼果子原料
* @param id
* @return
*/
@Override
public Ingredient findById(Long id) {
return jdbcTemplate.queryForObject("select id,name,type from ingredient where id = ?",this::mapRowToIngredient,id);
}
2.2、定义添加煎饼果子的方法
-
操作jdbctemplate进行新增煎饼果子,得到自增主键
-
注入原料的Serivce,遍历煎饼果子属性原料id集合,根据ID查询原料
@Repository
public class JdbcChineseHamburgerDaoImpl implements ChineseHamburgerDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private IngredientService ingredientService;
/**
* 新增煎饼果子
* @param chineseHamburger
* @return
*/
@Override
public ChineseHamburger save(ChineseHamburger chineseHamburger) {
//先新增煎饼果子表记录
long chineseHamburgerId = this.saveChineseHamburger(chineseHamburger);
List<Long> ingredientIds = chineseHamburger.getIngredients();
List<Ingredient> ingredientList = new ArrayList<>();
for (Long ingredientId : ingredientIds) {
//调用持久层查询煎饼果子原料
Ingredient ingredient = ingredientService.findById(ingredientId);
ingredientList.add(ingredient);
}
//再增加煎饼果子和煎饼果子调料关系表记录
ingredientList.forEach(ingredient -> {this.saveChineseHamburgerIngredientRelation(ingredient,chineseHamburgerId);});
return chineseHamburger;
}
}
2.3、抽取私有方法
/**
* 保存煎饼果子,返回自增的主键
* @return
*/
private long saveChineseHamburger(ChineseHamburger chineseHamburger){
chineseHamburger.setCreated(new Date());
KeyHolder keyHolder = new GeneratedKeyHolder();
//基于PreparedStatementCreator的定义的DDL
PreparedStatementCreator creator =
new PreparedStatementCreatorFactory(
"insert into chinese_hamburger(name,created) values(?,?)",
Types.VARCHAR, Types.TIMESTAMP
).newPreparedStatementCreator(
Arrays.asList(chineseHamburger.getName(),
new Timestamp(chineseHamburger.getCreated().getTime())));
jdbcTemplate.update(creator, keyHolder);
//得到自增主键并返回
return keyHolder.getKey().longValue();
}
/**
* 增加煎饼果子和煎饼果子调料关系表记录
* @param ingredient
* @param chineseHamburgerId
*/
private void saveChineseHamburgerIngredientRelation(Ingredient ingredient,long chineseHamburgerId){
jdbcTemplate.update(
"insert into chinese_hamburger_ingredient_relation (chinese_hamburger_id, ingredient_id) " +
"values (?, ?)",
chineseHamburgerId, ingredient.getId());
}
3、新做法:Converter转换器的处理
可不可以在定于domain的时候,写的原理集合是
List<Ingredient>
,而不是List,每次根据ID转换,这部分代码明显是恶心了。Converter转换器就可解决这个问题。
3.1、定义自定义转换器
package com.wnx.toca.converter;
import com.wnx.toca.domain.Ingredient;
import com.wnx.toca.serivce.IngredientService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
/**
* 煎饼果子材料和煎饼果子ID转换器
* @author wangnaixing
*/
@Component
public class IngredientByIdConverter implements Converter<String, Ingredient> {
@Autowired
private IngredientService ingredientService;
@Override
public Ingredient convert(String id) {
if (StringUtils.isNotBlank(id)){
return ingredientService.findById(Long.parseLong(id));
}
return null;
}
}
3.2、修改domain
package com.wnx.toca.domain;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 煎饼果子
* @author wangnaixing
* @Classname ChineseHamburger
* @Description TODO
* @Date 2021/12/14 22:43
* @Created by wangnaixing
*/
@Data
public class ChineseHamburger implements Serializable {
/**
* 自增主键
*/
private Long id;
/**
* 制作人姓名
*/
private String name;
/**
* 创建时间
*/
private Date created;
//=======附加属性==========//
/**
* 原料集合
*/
List<Ingredient> ingredients;
//=======附加属性==========//
}