MyBatis通過TypeHandler自動編解碼物件的Json屬性
阿新 • • 發佈:2020-08-17
mysql從5.7.版本開始支援json列。它本質上仍然是一個字串,比起直接用varchar
來說,它有專門對於json的的檢索,修改方法。更加的靈活。
在jdbc規範中,還沒json
型別的定義。所以物件一般都是用String
屬性,對映資料庫的json列。在儲存和讀取的時候,需要自己完成json的序列化和反序列化。
在使用MyBatis的框架,可以通過定義TypeHandler
來自動完成Json屬性的序列化和反序列化。
演示一個Demo
這裡使用Gson
的JsonElement
作為物件的Json屬性物件。我覺得它比較靈活,可以在JsonObject
和JsonArray
中隨意轉換。
整合MyBatis
... 略
表結構 & 模型物件
表結構
CREATE TABLE `website` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`name` varchar(255) NOT NULL COMMENT '網站名稱',
`properties` json DEFAULT NULL COMMENT '網站屬性,這是一個Json列',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
模型物件
import com.google.gson.JsonElement; public class WebSite { // 網站id private Integer id; // 網站名稱 private String name; // 網站屬性,json private JsonElement properties; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public JsonElement getProperties() { return properties; } public void setProperties(JsonElement properties) { this.properties = properties; } @Override public String toString() { return "WebSite [id=" + id + ", name=" + name + ", properties=" + properties + "]"; } }
自定義 TypeHandler
import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import com.google.gson.JsonElement; import com.google.gson.JsonParser; public class JsonElementTypeHandler extends BaseTypeHandler<JsonElement> { /** * Json編碼,物件 ==> Json字串 */ @Override public void setNonNullParameter(PreparedStatement ps, int i, JsonElement parameter, JdbcType jdbcType) throws SQLException { String value = parameter.toString(); if (jdbcType == null) { ps.setObject(i, value); } else { ps.setObject(i, value, jdbcType.TYPE_CODE); } } /** * Json解碼,Json字串 ==> 物件 */ @Override public JsonElement getNullableResult(ResultSet rs, String columnName) throws SQLException { String result = rs.getString(columnName); return result == null ? null : JsonParser.parseString(result); } /** * Json解碼,Json字串 ==> 物件 */ @Override public JsonElement getNullableResult(ResultSet rs, int columnIndex) throws SQLException { String result = rs.getString(columnIndex); return result == null ? null : JsonParser.parseString(result); } /** * Json解碼,Json字串 ==> 物件 */ @Override public JsonElement getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { String result = cs.getString(columnIndex); return result == null ? null : JsonParser.parseString(result); } }
在mybatis配置檔案中,配置handler
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeHandlers>
<typeHandler handler="io.springboot.jpa.mybatis.handler.JsonElementTypeHandler" javaType="com.google.gson.JsonElement"/>
</typeHandlers>
</configuration>
Mapper的定義
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import io.springboot.jpa.entity.WebSite;
@Mapper
public interface WebSiteMapper {
@Insert("INSERT INTO `website`(`id`, `name`, `properties`) VALUES(#{id}, #{name}, #{properties});")
@Options(useGeneratedKeys = true, keyColumn = "id", keyProperty = "id")
int save(WebSite webSite);
@Select("SELECT * FROM `website` WHERE `id` = #{id};")
WebSite findById(@Param("id") Integer id);
}
測試
private static final Logger LOGGER = LoggerFactory.getLogger(JpaApplicationTest.class);
@Autowired
private WebSiteMapper webSiteMapper;
@Test
@Transactional
@Rollback(false)
public void test () {
WebSite webSite = new WebSite();
webSite.setName("SpringBoot中文社群");
// 初始化properties屬性
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("url", "https://springboot.io");
jsonObject.addProperty("initializr", "https://start.springboot.io");
jsonObject.addProperty("Github", "https://github.com/springboot-community");
webSite.setProperties(jsonObject);
// 儲存
this.webSiteMapper.save(webSite);
// 根據id檢索
webSite = this.webSiteMapper.findById(webSite.getId());
LOGGER.info("resut={}", new Gson().toJson(webSite));
}
日誌輸出
org.mybatis.spring.SqlSessionUtils : Creating a new SqlSession
org.mybatis.spring.SqlSessionUtils : Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@34be7efb]
o.m.s.t.SpringManagedTransaction : JDBC Connection [HikariProxyConnection@885723920 wrapping com.mysql.cj.jdbc.ConnectionImpl@625487a6] will be managed by Spring
i.s.jpa.mapper.WebSiteMapper.save : ==> Preparing: INSERT INTO `website`(`id`, `name`, `properties`) VALUES(?, ?, ?);
i.s.jpa.mapper.WebSiteMapper.save : ==> Parameters: null, SpringBoot中文社群(String), {"url":"https://springboot.io","initializr":"https://start.springboot.io","Github":"https://github.com/springboot-community"}(String)
i.s.jpa.mapper.WebSiteMapper.save : <== Updates: 1
org.mybatis.spring.SqlSessionUtils : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@34be7efb]
org.mybatis.spring.SqlSessionUtils : Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@34be7efb] from current transaction
i.s.jpa.mapper.WebSiteMapper.findById : ==> Preparing: SELECT * FROM `website` WHERE `id` = ?;
i.s.jpa.mapper.WebSiteMapper.findById : ==> Parameters: 4(Integer)
i.s.jpa.mapper.WebSiteMapper.findById : <== Total: 1
org.mybatis.spring.SqlSessionUtils : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@34be7efb]
i.s.jpa.test.JpaApplicationTest : resut={"id":4,"name":"SpringBoot中文社群","properties":{"url":"https://springboot.io","Github":"https://github.com/springboot-community","initializr":"https://start.springboot.io"}}
不論是寫入還是讀取,都無誤的對JsonElement
完成了編碼和解碼。
核心只要明白了
TypeHandler
的幾個方法,不管是Gson
,Fastjson
,Jackson
甚至是自定義物件,都可以很簡單的完成自動對映。