Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Store/Retrieve Json data to/from Mysql 5.7 database using hibernate

I am going to start a project where I need to generate dynamic google forms. The requirement for this project is I need to use mysql 5.7 so that I could use json datatype to store/retrieve json data. I am fine with this. I know, it is possible using HQL. But what I couldn't figure out is how do I implement it with Hibernate using object relational mapping ?

Any Ideas ?

Thanks in advance !

like image 368
Roshan Shahukhal Avatar asked Sep 28 '15 09:09

Roshan Shahukhal


People also ask

Does MySQL 5.7 support JSON?

JSON support was introduced in MySQL 5.7. 8. In addition to the benefits just listed, having JSON as a native type in MySQL means that the database can automatically validate JSON documents stored in JSON columns. Invalid documents produce an error.

Can we store JSON data in MySQL?

MySQL supports a native JSON data type defined by RFC 7159 that enables efficient access to data in JSON (JavaScript Object Notation) documents. The JSON data type provides these advantages over storing JSON-format strings in a string column: Automatic validation of JSON documents stored in JSON columns.

Does hibernate support JSON?

Hibernate 6 provides a standard mapping for entity attributes to JSON columns; you only need to activate it. Unfortunately, Hibernate 4 and 5 do not support any JSON mappings, and you have to implement a UserType.

Does MySQL 5.6 support JSON?

Yes, however, Domo's MySQL 5.6 environment predates JSON parsing support which was introduced in MySQL 5.7 and expanded in MySQL8.


1 Answers

Recently I have solved similar task. I guess it's too late, but maybe someone finds this useful.

Short answer: you should create class (like "com.test.MyJsonType") that must implement org.hibernate.usertype.UserType interface where the nullSafeGet method should deserialize JSON to java object (using Jackson), the nullSafeSet serialize POJO to JSON and some other auxiliary methods.

Then you should extend MySQLDialect and register new column type "json".

Finally you can annotate entity fields by @Type(type = "com.test.MyJsonType") which should be mapped to MySQL json columns.

You may also read about @TypeDef here if you don't want write type with package name.

For example:

public class MyJsonType implements UserType {

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

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

@Override
public Object nullSafeGet(final ResultSet rs, final String[] names, final SessionImplementor session, final Object owner)
        throws HibernateException, SQLException {
    final String cellContent = rs.getString(names[0]);
    if (cellContent == null) {
        return null;
    }
    try {
        final ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        return mapper.readValue(cellContent.getBytes("UTF-8"), returnedClass());
    } catch (final Exception ex) {
        throw new RuntimeException("Failed to convert String to Invoice: " + ex.getMessage(), ex);
    }
}

@Override
public void nullSafeSet(final PreparedStatement ps, final Object value, final int idx, final SessionImplementor session)
        throws HibernateException, SQLException {
    if (value == null) {
        ps.setNull(idx, Types.VARCHAR);
        return;
    }
    try {
        final ObjectMapper mapper = new ObjectMapper();
        final StringWriter w = new StringWriter();
        mapper.writeValue(w, value);
        w.flush();
        ps.setObject(idx, w.toString(), Types.VARCHAR);
    } catch (final Exception ex) {
        throw new RuntimeException("Failed to convert Invoice to String: " + ex.getMessage(), ex);
    }
}

@Override
public Object deepCopy(final Object value) throws HibernateException {
    try {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(value);
        oos.flush();
        oos.close();
        bos.close();

        ByteArrayInputStream bais = new ByteArrayInputStream(bos.toByteArray());
        return new ObjectInputStream(bais).readObject();
    } catch (ClassNotFoundException | IOException ex) {
        throw new HibernateException(ex);
    }
}

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

@Override
public Serializable disassemble(final Object value) throws HibernateException {
    return (Serializable) this.deepCopy(value);
}

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

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

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

@Override
public int hashCode(Object x) throws HibernateException {
    return Objects.hashCode(x);
}

}

POJO class:

public class Characteristics implements Serializable {

private String field;

public String getField() {
    return field;
}

public void setField(String field) {
    this.field= field;
}

@Override
public boolean equals(Object obj) {

    if (obj == null) return false;
    if (getClass() != obj.getClass()) return false;
    final Characteristics other = (Characteristics) obj;
    return Objects.equals(this.field, other.field);

}

@Override
public int hashCode() {

    return Objects.hash(this.field);

}
}

Register new column type:

public class JsonMySQLDialect extends MySQLDialect {

public JsonMySQLDialect() {
    this.registerColumnType(Types.VARCHAR, "json");
}

}

Using:

@Entity
@Table(name = "Table")
public class TableClass {
...
@Column
@Type(type = "com.test.MyJsonType")
protected Characteristics characteristics;
...
}
like image 92
Kirill Dmitriev Avatar answered Oct 15 '22 13:10

Kirill Dmitriev