Spring Data Jpa+PostgreSQL对jsonb字段类型做映射实现

PostgreSQL支持json字段类型,json 数据类型可以用来存储 JSON(JavaScript Object Notation)数据, 这样的数据也可以存储为 text,但是 json 数据类型更有利于检查每个存储的数值是可用的 JSON 值。

Spring Data Jpa若想使用json类型字段,需要自定义数据解析,具体实现方式就是实现UserType,重新方法实现编写解析逻辑代码解析数据以达到目的。

下面将详细说明实现方式(已测试通过):

简单的Map格式实现:

  1. 定义自己的数据库方言
package com.cai.demo.config;

import org.hibernate.dialect.PostgreSQL95Dialect;
import java.sql.Types;

/**
 * @Description: 自定义PostgreSQL方言
 * @Author: caijun
 */
public class JsonbPostgresDialect extends PostgreSQL95Dialect {
    public JsonbPostgresDialect() {
        this.registerColumnType(Types.JAVA_OBJECT, "jsonb");
    }
}
  1. 配置自定义方言
spring:
  jpa:
    hibernate:
      database-platform: com.cai.demo.config.JsonbPostgresDialect
  1. 编写json类型转换代码
package com.cai.demo.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.SerializationException;
import org.hibernate.usertype.UserType;
import org.postgresql.util.PGobject;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description: Jsonb类型转换
 * @Author: caijun
 */
public class JsonbType implements UserType {

    private final ObjectMapper mapper = new ObjectMapper();


    @Override
    public Object deepCopy(Object originalValue) throws HibernateException {
        if (originalValue != null) {
            try {
                return mapper.readValue(mapper.writeValueAsString(originalValue), returnedClass());
            } catch (IOException e) {
                throw new HibernateException("Failed to deep copy object", e);
            }
        }
        return null;
    }


    @Override
    public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException {
        PGobject o = (PGobject) rs.getObject(names[0]);
        // 具体实现逻辑
        if (o != null && o.getValue() != null) {
            try {
                return mapper.readValue(o.getValue(), Map.class);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return new HashMap<String, Object>();
    }

    @Override
    public Serializable disassemble(Object value) throws HibernateException {
        Object copy = deepCopy(value);

        if (copy instanceof Serializable) {
            return (Serializable) copy;
        }

        throw new SerializationException(String.format("Cannot serialize '%s', %s is not Serializable.", value, value.getClass()), null);
    }

    @Override
    public Object assemble(Serializable cached, Object owner) throws HibernateException {
        return deepCopy(cached);
    }

    @Override
    public Object replace(Object original, Object target, Object owner) throws HibernateException {
        return deepCopy(original);
    }

    @Override
    public boolean isMutable() {
        return true;
    }

    @Override
    public int hashCode(Object x) throws HibernateException {
        if (x == null) {
            return 0;
        }

        return x.hashCode();
    }


    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
        if (value == null) {
            st.setNull(index, Types.OTHER);
        } else {
            try {
                st.setObject(index, mapper.writeValueAsString(value), Types.OTHER);
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    @Override
    public boolean equals(Object x, Object y) throws HibernateException {
        return ObjectUtils.nullSafeEquals(x, y);
    }

    @Override
    public Class<?> returnedClass() {
        return Map.class;
    }

    @Override
    public int[] sqlTypes() {
        return new int[]{Types.JAVA_OBJECT};
    }
}

  1. 实体类添加注解
@Getter
@Setter
@ToString
@Entity
@Table(name = "test_tb")
@TypeDef(name = "JsonbType", typeClass = JsonbType.class)
public class TestEntity implements Serializable {

    private static final long serialVersionUID = 1L;
    /**
     * 主键id
     */
    @Id
    @Column(name = "pri_id")
    private Integer priId;

	@Column(name = "test_json_field", columnDefinition = "jsonb")
    @Type(type = "JsonbType")
    private Map<String, List<String>> testJsonField;
}

'test_json_field’字段需要存储如以下格式的数据:
{“2019-11-16”: [“0-24”], “2019-11-17”: [“0-24”], “2019-11-18”: [“0-24”]}

  1. 测试
/**
 * 测试'jsonb'字段类型映射(测试通过)
 */
@Test
public void testJsonField() {
	// jpa查询实体对象
    TestEntity testEntity = testRepository.findById(1).get();
    log.info(testEntity.toString());
    // 增加新的值
    Map<String, List<String>> map = testEntity.getTestJsonField();
    List<String> list = ImmutableList.of("8-9");
    map.put("2019-11-19", list);
    testEntity.setTestJsonField(map);
    // 保存
    testRepository.save(testEntity);
}

对象直接映射:

  1. 定义自己的数据库方言
package com.cai.demo.config;

import org.hibernate.dialect.PostgreSQL95Dialect;
import java.sql.Types;

/**
 * @Description: 自定义PostgreSQL方言
 * @Author: caijun
 */
public class JsonbPostgresDialect extends PostgreSQL95Dialect {
    public JsonbPostgresDialect() {
    	super();
        this.registerColumnType(Types.JAVA_OBJECT, "jsonb");
        registerHibernateType(Types.ARRAY, StringType.class.getName());
    }
}
  1. 配置自定义方言
spring:
  jpa:
    hibernate:
      database-platform: com.cai.demo.config.JsonbPostgresDialect
  1. 编写json类型转换代码
package com.cai.demo.config;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.hibernate.HibernateException;
import org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.SerializationException;
import org.hibernate.usertype.ParameterizedType;
import org.hibernate.usertype.UserType;
import org.postgresql.util.PGobject;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Properties;

/**
 * @Description: Jsonb类型转换
 * @Author: caijun
 */
public class JsonbType implements UserType, ParameterizedType {

    private final ObjectMapper mapper = new ObjectMapper();

    private static final ClassLoaderService classLoaderService = new ClassLoaderServiceImpl();

    public static final String CLASS = "CLASS";

    private Class<?> jsonClassType;

    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
        if (value == null) {
            st.setNull(index, Types.OTHER);
        } else {
            try {
                st.setObject(index, mapper.writeValueAsString(value), Types.OTHER);
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    @Override
    public Object deepCopy(Object originalValue) throws HibernateException {
        if (originalValue != null) {
            try {
                return mapper.readValue(mapper.writeValueAsString(originalValue),
                        returnedClass());
            } catch (IOException e) {
                throw new HibernateException("Failed to deep copy object", e);
            }
        }
        return null;
    }

    @Override
    public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException {
        PGobject o = (PGobject) rs.getObject(names[0]);
        // 具体实现逻辑
        if (o != null && o.getValue() != null) {
            return JSON.parseObject(o.getValue(), jsonClassType);
        }
        return null;
    }

    @Override
    public Serializable disassemble(Object value) throws HibernateException {
        Object copy = deepCopy(value);

        if (copy instanceof Serializable) {
            return (Serializable) copy;
        }

        throw new SerializationException(String.format("Cannot serialize '%s', %s is not Serializable.", value, value.getClass()), null);
    }

    @Override
    public Object assemble(Serializable cached, Object owner) throws HibernateException {
        return deepCopy(cached);
    }

    @Override
    public Object replace(Object original, Object target, Object owner) throws HibernateException {
        return deepCopy(original);
    }

    @Override
    public boolean isMutable() {
        return true;
    }

    @Override
    public int hashCode(Object x) throws HibernateException {
        if (x == null) {
            return 0;
        }

        return x.hashCode();
    }

    @Override
    public boolean equals(Object x, Object y) throws HibernateException {
        return ObjectUtils.nullSafeEquals(x, y);
    }

    @Override
    public Class<?> returnedClass() {
        return jsonClassType;
    }

    @Override
    public int[] sqlTypes() {
        return new int[]{Types.JAVA_OBJECT};
    }

    @Override
    public void setParameterValues(Properties properties) {
        final String clazz = (String) properties.get(CLASS);
        if (clazz != null) {
            jsonClassType = classLoaderService.classForName(clazz);
        }
    }
}
  1. 实体类添加注解
@Data
@Entity
@Table(name = "test_tb")
@TypeDef(name = "entityJsonbType1", typeClass = JsonbType.class, parameters = {
        @Parameter(name = JsonbType.CLASS, value = "com.cai.demo.entity.TestEntity2")
})
public class TestEntity implements Serializable {

    private static final long serialVersionUID = 1L;
    /**
     * 主键id
     */
    @Id
    @Column(name = "pri_id")
    private Integer priId;

	@Column(name = "entity_json_field", columnDefinition = "jsonb")
    @Type(type = "entityJsonbType1")
    private TestEntity2 testEntityJson;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "test_tb_2")
public class TestEntity2 implements Serializable {

    private static final long serialVersionUID = 1L;
    /**
     * 主键id
     */
    @Id
    @Column(name = "pri_id")
    private Integer priId;

	@Column(name = "name")
    private String name;
}
  1. 测试
@Test
public void testJsonbEntity() {
    TestEntity testEntity = testRepository.findById(1).get();

    TestEntity2 testEntity2 = new TestEntity2(11, "testEntity2");
    // 添加
    testEntity.setTestEntityJson(testEntity2);
    testRepository.save(testEntity);

    // 修改保存
    TestEntity testEntity1_1 = testRepository.findById(1).get();
    TestEntity2 testEntity2_1 = testEntity1_1.getTestEntityJson();
    testEntity2_1.setName("testEntity2_1");
    testEntity1_1.setTestEntityJson(testEntity2_1);
    testRepository.save(testEntity1_1);

    // 删除
    TestEntity testEntity1_2 = testRepository.findById(1).get();
    testEntity1_2.setTestEntityJson(null);
    testRepository.save(testEntity1_2);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值