概述 鎖是電腦協調多個進程或線程併發訪問某一資源的機制。在資料庫中,除傳統的計算資源(CPU、RAM、I/O)的爭用以外,數據也是一種供許多用戶共用的資源。如何保證數據併發訪問的一致性、有效性是所有資料庫必須解決的一個問題,鎖衝突也是影響資料庫併發訪問性能的一個重要因素。從這個角度來說,鎖對資料庫 ...
一、背景
對於一些不經常更新的靜態數據,我們喜歡使用json格式存儲。推薦的做法是將json數據存儲在key-value資料庫,但這無疑增加了技術成本,所以我們通常還是存儲在RDB資料庫中。我們在使用hibernate,對json數據的存取期望是,存能自動轉換為json格式存儲,取能自動將json數據轉換為對象數據。在資料庫方面,Mysql5.7版本以後新增的功能,Mysql提供了一個原生的Json類型,Json值將不再以字元串的形式存儲,而是採用一種允許快速讀取文本元素(document elements)的內部二進位(internal binary)格式。在Json列插入或者更新的時候將會自動驗證Json文本,未通過驗證的文本將產生一個錯誤信息。而除了mysql,pg極少部分支持json欄位類型的其他資料庫,還是需要大文本欄位進行存儲。
二、實現方案
關鍵技術: hibernate-types 將Java對象或Jackson JsonNode為JPA實體屬性。
- 引入依賴-hibernate-types
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>2.10.1</version>
</dependency>
- 定義實體類
import com.alibaba.fastjson.JSONObject;
import com.vladmihalcea.hibernate.type.array.IntArrayType;
import com.vladmihalcea.hibernate.type.array.StringArrayType;
import com.vladmihalcea.hibernate.type.json.JsonBinaryType;
import com.vladmihalcea.hibernate.type.json.JsonStringType;
import lombok.Data;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs;
import javax.persistence.*;
@Data
@Entity
@Table(name = "parents")
@TypeDefs({
@TypeDef(name = "string-array", typeClass = StringArrayType.class),
@TypeDef(name = "json", typeClass = JsonStringType.class),
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
})
public class TestEntity implements Serializable {
@Id
private Long id;
@Column(name = "attr")
@Type(type = "jsonb")
private JSONObject attr;
@Column(name = "user_list")
@Type(type = "json")
private List<User> userList;
@Column(name = "ava_img")
@Type(type = "string-array")
private String[] avaImg;
}
以上為常規操作,在多資料庫使用時,你會發現,JsonStringType 類型會轉為資料庫預設的 varchar(255),根本就存儲不了長一點的json數據。
三、擴展JsonStringType,適配多資料庫json欄位存儲
也許網路上一搜JsonStringType適配性問題,答案基本都是建議用 @Column(columnDefinition = "json") 、 @Column(columnDefinition = "varchar(max)") 這種寫法,我在上一篇文章中說了,columnDefinition具有不可移植性,只能適配一種或一類資料庫。所以,需要自定義擴展JsonStringType。
- 先分析JsonStringType源碼
可以看到JsonStringType的類型描述時由JsonStringSqlTypeDescriptor提供的
而JsonStringSqlTypeDescriptor中,將資料庫對應的類型寫死為 12
12對應資料庫欄位類型是varchar,這也太坑了,直接給JsonStringType對應的資料庫欄位類型寫死。
- JsonStringType相容辦法
既然我們找到了問題原因,那麼我們就容易解決,解決主要思路是將JsonStringType類型對應的資料庫欄位類型可配置
接著看源碼
JsonStringSqlTypeDescriptor 的父類是 AbstractJsonSqlTypeDescriptor
在AbstractJsonSqlTypeDescriptor中,將json格式寫死為1111,也就是 Types.OTHER類型
我們重新定義一個JsonStringType,不將SqlType寫死,然後通過配置database-platform指定hibernate方言中,將1111轉換為我們需要的格式。
源碼如下:
MyJsonStringType (點擊展開)
import com.fasterxml.jackson.databind.ObjectMapper;
import com.vladmihalcea.hibernate.type.AbstractHibernateType;
import com.vladmihalcea.hibernate.type.json.internal.JsonTypeDescriptor;
import com.vladmihalcea.hibernate.type.util.Configuration;
import com.vladmihalcea.hibernate.type.util.ObjectMapperWrapper;
import org.hibernate.usertype.DynamicParameterizedType;
import java.lang.reflect.Type;
import java.util.Properties;
public class MyJsonStringType extends AbstractHibernateType<Object> implements DynamicParameterizedType {
public static final MyJsonStringType INSTANCE = new MyJsonStringType();
public MyJsonStringType() {
super(MyJsonStringSqlTypeDescriptor.INSTANCE, new JsonTypeDescriptor(Configuration.INSTANCE.getObjectMapperWrapper()));
}
public MyJsonStringType(Type javaType) {
super(MyJsonStringSqlTypeDescriptor.INSTANCE, new JsonTypeDescriptor(Configuration.INSTANCE.getObjectMapperWrapper(), javaType));
}
public MyJsonStringType(Configuration configuration) {
super(MyJsonStringSqlTypeDescriptor.INSTANCE, new JsonTypeDescriptor(configuration.getObjectMapperWrapper()), configuration);
}
public MyJsonStringType(ObjectMapper objectMapper) {
super(MyJsonStringSqlTypeDescriptor.INSTANCE, new JsonTypeDescriptor(new ObjectMapperWrapper(objectMapper)));
}
public MyJsonStringType(ObjectMapperWrapper objectMapperWrapper) {
super(MyJsonStringSqlTypeDescriptor.INSTANCE, new JsonTypeDescriptor(objectMapperWrapper));
}
public MyJsonStringType(ObjectMapper objectMapper, Type javaType) {
super(MyJsonStringSqlTypeDescriptor.INSTANCE, new JsonTypeDescriptor(new ObjectMapperWrapper(objectMapper), javaType));
}
public MyJsonStringType(ObjectMapperWrapper objectMapperWrapper, Type javaType) {
super(MyJsonStringSqlTypeDescriptor.INSTANCE, new JsonTypeDescriptor(objectMapperWrapper, javaType));
}
public String getName() {
return "json";
}
protected boolean registerUnderJavaType() {
return true;
}
public void setParameterValues(Properties parameters) {
((JsonTypeDescriptor) this.getJavaTypeDescriptor()).setParameterValues(parameters);
}
}
MyJsonStringSqlTypeDescriptor(點擊展開)
import com.vladmihalcea.hibernate.type.json.internal.AbstractJsonSqlTypeDescriptor;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.BasicBinder;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class MyJsonStringSqlTypeDescriptor extends AbstractJsonSqlTypeDescriptor {
public static final MyJsonStringSqlTypeDescriptor INSTANCE = new MyJsonStringSqlTypeDescriptor();
public MyJsonStringSqlTypeDescriptor() {
}
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>(javaTypeDescriptor, this) {
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
st.setString(index, (String)javaTypeDescriptor.unwrap(value, String.class, options));
}
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) throws SQLException {
st.setString(name, (String)javaTypeDescriptor.unwrap(value, String.class, options));
}
};
}
protected Object extractJson(ResultSet rs, String name) throws SQLException {
return rs.getString(name);
}
protected Object extractJson(CallableStatement statement, int index) throws SQLException {
return statement.getString(index);
}
protected Object extractJson(CallableStatement statement, String name) throws SQLException {
return statement.getString(name);
}
}
實體json定義改為
@TypeDef(name="json",typeClass = MyJsonStringType.class)
大功告成,下邊就可以根據不同資料庫自適應資料庫json欄位類型
-
mysql適配json欄位
public class MySQLDialect extends MySQL5InnoDBDialect { public MySQLDialect() { registerColumnType(Types.OTHER, "json"); } }
配置文件:
spring.jpa.database-platform: xxxx.xxxx.xx.MySQLDialect
-
達夢資料庫適配json欄位
public class DaMengDialect extends DmDialect { public DaMengDialect() { registerColumnType(Types.OTHER, "LONGVARCHAR"); } }
配置文件:
spring.jpa.database-platform: xxxx.xxxx.xx.DaMengDialect
本文來自博客園,作者:·志堅行遠·,轉載請註明原文鏈接:https://www.cnblogs.com/luze/p/17185828.html