In our projects, we often encounter uncertain data structures. At this time, ordinary relational databases cannot meet our requirements. Postgres provides us with jsonb
data types where we can store json
data in fields of this type and query this data. This example will combine hibernate
, Spring Data JPA
, Spring Boot
to achieve.
1. Custom dialect
public class JsonbPostgresDialect extends PostgreSQL94Dialect {
public JsonbPostgresDialect() {
this.registerColumnType(Types.JAVA_OBJECT,"jsonb");
}
}
Specify the dialect
spring.jpa.database-platform: com.example.jpajsonb.support.JsonbPostgresDialect
2. Custom jsonb data type
Map
The mapping PGObject
(postgres object type) is mainly implemented here , and ObjectMapper
the conversion of the two data types is achieved through it.
public class JsonbType implements UserType{
private final ObjectMapper mapper = new ObjectMapper();;
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor 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, SessionImplementor session, Object owner) throws HibernateException, SQLException {
PGobject o = (PGobject) rs.getObject(names[0]);
if (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 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};
}
}
3. Statement of use
Define the data type first, then use it on the field
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@TypeDef(name = "JsonbType", typeClass = JsonbType.class)
public class Person {
@Id
@GeneratedValue
private Long id;
@Column(columnDefinition = "jsonb")
@Type(type = "JsonbType")
private Map<String,Object> info;
}
4.Repository
Query by postgres
native sql statement, this example means that the value of one of the json
data is equal to. Please refer to the specific sql query method of JSON: https://www.postgresql.org/docs/9.5/static/functions-json.htmlinfo
key
name
public interface PersonRepository extends JpaRepository<Person,Long> {
@Query(value = "select * from person where info ->> 'name' = :name" , nativeQuery = true)
List<Person> findByName(@Param("name") String name);
}
5. Save and Read Tests
@Bean
CommandLineRunner saveAndReadJsonb(PersonRepository personRepository){
return e -> {
Person p = new Person();
Map m = new HashMap();
m.put("name","汪云飞");
m.put("age",11);
p.setInfo(m);
Person returnPerson = personRepository.save(p);
Map returnMap = returnPerson.getInfo();
for(Object entry :returnMap.entrySet()){
log.info(entry.toString());
}
};
}
6. Query test
@Bean
CommandLineRunner queryJsonb(PersonRepository personRepository){
return e -> {
List<Person> people = personRepository.findByName("吴亦凡");
for (Person person : people){
Map info = person.getInfo();
log.info(person.getId() + "/" + info.get("name") + "/" +info.get("age"));
}
};
}
7. Source address
http://www.wisely.top/2017/06/27/spring-data-jpa-postgresql-jsonb/