1, Scene
There are often List fields in entities that need to be mapped
mybatis plus provides annotation mode for direct injection. The portal: Portal - field type processor
2, Questions
The addition, deletion and modification can take effect, but the format of saving data is different from normal, and the query is invalid.
My operations are as follows:
1. For the custom class Jackson type handler, I wrote @ MappedTypes({Object.class }
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedJdbcTypes; import org.apache.ibatis.type.MappedTypes; import java.io.IOException; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * TypeHandler of generic type */ @Slf4j @MappedTypes({Object.class}) @MappedJdbcTypes(JdbcType.VARCHAR) public class JacksonTypeHandler extends BaseTypeHandler<Object> { private static ObjectMapper objectMapper; private Class<Object> type; static { objectMapper = new ObjectMapper(); } public JacksonTypeHandler(Class<Object> type) { if (log.isTraceEnabled()) { log.trace("JacksonTypeHandler(" + type + ")"); } if (null == type) { throw new MybatisPlusException("Type argument cannot be null"); } this.type = type; } private Object parse(String json) { try { if (json == null || json.length() == 0) { return null; } return objectMapper.readValue(json, type); } catch (IOException e) { throw new RuntimeException(e); } } private String toJsonString(Object obj) { try { return objectMapper.writeValueAsString(obj); } catch (JsonProcessingException e) { throw new RuntimeException(e); } } @Override public Object getNullableResult(ResultSet rs, String columnName) throws SQLException { return parse(rs.getString(columnName)); } @Override public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return parse(rs.getString(columnIndex)); } @Override public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return parse(cs.getString(columnIndex)); } @Override public void setNonNullParameter(PreparedStatement ps, int columnIndex, Object parameter, JdbcType jdbcType) throws SQLException { ps.setString(columnIndex, toJsonString(parameter)); } }
2. The entity method is as follows:
@Data @Accessors(chain = true) @EqualsAndHashCode(callSuper = true) @TableName(value = "goods", autoResultMap = true) public class Goods extends BaseEntity<Goods > { @TableField(typeHandler = JacksonTypeHandler.class, value = "`images`") private List<String> images; }
At this time, insert the data with the program, and the format stored in the database becomes bracketed and quoted, as shown in the following figure:
When querying data, the field returns null
3, Troubleshooting steps
After repeated attempts, we found that there are several points that need attention. You can check them one by one according to the following.
-
On entity class, annotation @ TableName(autoResultMap = true) is required
-
@ MappedTypes({Object.class }) is not valid, or specifies that a specific type is required. For example, a List type handler can be customized for a List type field (the sample code is put below)
-
The user-defined TypeHandler class needs to be annotated:
@MappedJdbcTypes( JdbcType.VARCHAR )/ / database type
@MappedTypes({ List.class }) / / Java data type -
Annotation is required on the field: @ tablefield (typehandler= ListTypeHandler.class )
-
The configuration file needs to add: mybatis-plus.type-handlers-package=com.package.handler (package name)
4, Example
Here, the field type is List.
- Custom TypeHandler class: ListTypeHandler.java
import com.yeepay.shade.org.apache.commons.lang3.StringUtils; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedJdbcTypes; import org.apache.ibatis.type.MappedTypes; import org.apache.ibatis.type.TypeHandler; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Type specific TypeHandler */ @Slf4j @MappedJdbcTypes(JdbcType.VARCHAR) //Database type @MappedTypes({List.class}) //java data type public class ListTypeHandler implements TypeHandler<List<String>> { @Override public void setParameter(PreparedStatement ps, int i, List<String> parameter, JdbcType jdbcType) throws SQLException { String hobbys = dealListToOneStr(parameter); ps.setString(i, hobbys); } /** * Set concatenation string * * @param parameter * @return */ private String dealListToOneStr(List<String> parameter) { if (parameter == null || parameter.size() <= 0) return null; String res = ""; for (int i = 0; i < parameter.size(); i++) { if (i == parameter.size() - 1) { res += parameter.get(i); return res; } res += parameter.get(i) + ","; } return null; } @Override public List<String> getResult(ResultSet rs, String columnName) throws SQLException { if (StringUtils.isBlank(rs.getString(columnName))) { return new ArrayList<>(); } return Arrays.asList(rs.getString(columnName).split(",")); } @Override public List<String> getResult(ResultSet rs, int columnIndex) throws SQLException { if (StringUtils.isBlank(rs.getString(columnIndex))) { return new ArrayList<>(); } return Arrays.asList(rs.getString(columnIndex).split(",")); } @Override public List<String> getResult(CallableStatement cs, int columnIndex) throws SQLException { String hobbys = cs.getString(columnIndex); if (StringUtils.isBlank(hobbys)) { return new ArrayList<>(); } return Arrays.asList(hobbys.split(",")); } }
- Entity class: Goods.java
@Data @Accessors(chain = true) @EqualsAndHashCode(callSuper = true) @TableName(value = "goods", autoResultMap = true) public class Goods extends BaseEntity<Goods > { @TableField(typeHandler = ListTypeHandler.class, value = "`images`") private List<String> images; }
- Profile: application.yml
mybatis-plus: type-handlers-package: com.utlz.ms.handler
That's how it's configured~
The format of database storage is also normal:
5, Questions
In the process of trying, I found an interesting phenomenon~
This is how I operate:
1. In the handle directory, two typeHandler classes are customized, These are the JacksonTypeHandler and ListTypeHandler listed above. 2. Then the configuration file is set normally. 3. On the fields of entity class, * * Jackson type handler is configured** @TableField(typeHandler = JacksonTypeHandler.class, value = "`images`") private List<String> images;
That is to say, there is no reference to ListTypeHandler in the program at this time, and it refers to Jackson typehandler.
But at this time, the query also took effect?
After adding logs to both, it is found that ListTypeHandler has logs and Jackson typehandler has no logs.
Originally, although ListTypeHandler was not referenced, the code automatically recognized the type and used it, so the query also took effect.
OK, so TypeHandler needs to define specific types~