Use SpringConverter to optimize the association between Ids and Ids
We will encounter this problem during project development. List ingredients; List ingredients; It is also a collection of principles. One back-end likes it and the other front-end likes it. These two are often written in a POJO. According to the relationship of objects, we prefer the first one. Database query is not good!
1. Problem description
1.1. The form submitted multiple same name values
Take a pancake fruit as an example. Making pancake fruit requires a variety of raw materials, so you have the form data below
<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. We generally use array to receive
For example, get the ID array and delete it in batch. Now let's add the ID set of raw materials to the pancake fruit
package com.wnx.toca.domain; import lombok.Data; import java.io.Serializable; import java.util.Date; import java.util.List; /** * pancake rolled with crisp fritter * @author wangnaixing * @Classname ChineseHamburger * @Description TODO * @Date 2021/12/14 22:43 * @Created by wangnaixing */ @Data public class ChineseHamburger implements Serializable { /** * Auto increment primary key */ private Long id; /** * Producer name */ private String name; /** * Creation time */ private Date created; //=======Additional properties==========// /** * Raw material collection */ List<Long> ingredients; //=======Additional properties==========// }
1.3. Now we want to save the pancake fruit
Our general idea must be:
- Save the pancake fruit information first
- Then add the correlation between pancake fruit and raw materials
2. My previous handling
Here, I have used Spring's JdbcTemplate as the persistence layer operation as an example
2.1 define the method of querying raw materials by ID on the Dao of raw materials
/** * Query pancake fruit raw materials according to ID * @param id * @return */ Ingredient findById(Long id);
/** * Query pancake fruit raw materials according to 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. Define the method of adding pancake fruit
-
Operate JDBC template to add pancake fruit to obtain the self increasing primary key
-
Inject the Serivce of raw materials, traverse the raw material ID set of pancake fruit attributes, and query the raw materials according to the ID
@Repository public class JdbcChineseHamburgerDaoImpl implements ChineseHamburgerDao { @Autowired private JdbcTemplate jdbcTemplate; @Autowired private IngredientService ingredientService; /** * Add pancake fruit * @param chineseHamburger * @return */ @Override public ChineseHamburger save(ChineseHamburger chineseHamburger) { //Add pancake fruit table record first long chineseHamburgerId = this.saveChineseHamburger(chineseHamburger); List<Long> ingredientIds = chineseHamburger.getIngredients(); List<Ingredient> ingredientList = new ArrayList<>(); for (Long ingredientId : ingredientIds) { //Call the persistence layer to query the pancake fruit raw materials Ingredient ingredient = ingredientService.findById(ingredientId); ingredientList.add(ingredient); } //Add the record of pancake fruit and pancake fruit seasoning relationship table ingredientList.forEach(ingredient -> {this.saveChineseHamburgerIngredientRelation(ingredient,chineseHamburgerId);}); return chineseHamburger; } }
2.3 extraction method
/** * Save the pancake fruit and return the self incremented primary key * @return */ private long saveChineseHamburger(ChineseHamburger chineseHamburger){ chineseHamburger.setCreated(new Date()); KeyHolder keyHolder = new GeneratedKeyHolder(); //DDL based on the definition of PreparedStatementCreator 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); //Get the self incrementing primary key and return return keyHolder.getKey().longValue(); } /** * Add the record of pancake fruit and pancake fruit seasoning relationship table * @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. New method: Converter converter processing
Can I write a principle set of List < increment > instead of List when the domain is set? This part of the code is obviously disgusting every time it is converted according to the ID. Converter converter can solve this problem.
3.1. Define custom converter
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; /** * Pancake fruit material and pancake fruit ID converter * @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. Modify domain
package com.wnx.toca.domain; import lombok.Data; import java.io.Serializable; import java.util.Date; import java.util.List; /** * pancake rolled with crisp fritter * @author wangnaixing * @Classname ChineseHamburger * @Description TODO * @Date 2021/12/14 22:43 * @Created by wangnaixing */ @Data public class ChineseHamburger implements Serializable { /** * Auto increment primary key */ private Long id; /** * Producer name */ private String name; /** * Creation time */ private Date created; //=======Additional properties==========// /** * Raw material collection */ List<Ingredient> ingredients; //=======Additional properties==========// }