I use in application MySQL 5.7 and I have JSON columns. When I try running my integration tests don't work because the H2 database can't create the table. This is the error:
2016-09-21 16:35:29.729 ERROR 10981 --- [ main] org.hibernate.tool.hbm2ddl.SchemaExport : HHH000389: Unsuccessful: create table payment_transaction (id bigint generated by default as identity, creation_date timestamp not null, payload json, period integer, public_id varchar(255) not null, state varchar(255) not null, subscription_id_zuora varchar(255), type varchar(255) not null, user_id bigint not null, primary key (id)) 2016-09-21 16:35:29.730 ERROR 10981 --- [ main] org.hibernate.tool.hbm2ddl.SchemaExport : Unknown data type: "JSON"; SQL statement:
This is the entity class.
@Table(name = "payment_transaction") public class PaymentTransaction extends DomainObject implements Serializable { @Convert(converter = JpaPayloadConverter.class) @Column(name = "payload", insertable = true, updatable = true, nullable = true, columnDefinition = "json") private Payload payload; public Payload getPayload() { return payload; } public void setPayload(Payload payload) { this.payload = payload; } }
And the subclass:
public class Payload implements Serializable { private Long userId; private SubscriptionType type; private String paymentId; private List<String> ratePlanId; private Integer period; public Long getUserId() { return userId; } public void setUserId(Long userId) { this.userId = userId; } public SubscriptionType getType() { return type; } public void setType(SubscriptionType type) { this.type = type; } public String getPaymentId() { return paymentId; } public void setPaymentId(String paymentId) { this.paymentId = paymentId; } public List<String> getRatePlanId() { return ratePlanId; } public void setRatePlanId(List<String> ratePlanId) { this.ratePlanId = ratePlanId; } public Integer getPeriod() { return period; } public void setPeriod(Integer period) { this.period = period; } }
And this converter for insert in database:
public class JpaPayloadConverter implements AttributeConverter<Payload, String> { // ObjectMapper is thread safe private final static ObjectMapper objectMapper = new ObjectMapper(); private Logger log = LoggerFactory.getLogger(getClass()); @Override public String convertToDatabaseColumn(Payload attribute) { String jsonString = ""; try { log.debug("Start convertToDatabaseColumn"); // convert list of POJO to json jsonString = objectMapper.writeValueAsString(attribute); log.debug("convertToDatabaseColumn" + jsonString); } catch (JsonProcessingException ex) { log.error(ex.getMessage()); } return jsonString; } @Override public Payload convertToEntityAttribute(String dbData) { Payload payload = new Payload(); try { log.debug("Start convertToEntityAttribute"); // convert json to list of POJO payload = objectMapper.readValue(dbData, Payload.class); log.debug("JsonDocumentsConverter.convertToDatabaseColumn" + payload); } catch (IOException ex) { log.error(ex.getMessage()); } return payload; } }
I just came across this problem working with the JSONB
column type - the binary version of the JSON
type, which doesn't map to TEXT
.
For future reference, you can define a custom type in H2 using CREATE DOMAIN
, as follows:
CREATE domain IF NOT EXISTS jsonb AS other;
This seemed to work for me, and allowed me to successfully test my code against the entity.
Source: https://objectpartners.com/2015/05/26/grails-postgresql-9-4-and-jsonb/
Champagne time! 🍾
Starting with the version 2.11.0
, the Hibernate Types project now provides a generic JsonType
that works auto-magically with:
@Entity(name = "Book") @Table(name = "book") @TypeDef(name = "json", typeClass = JsonType.class) public class Book { @Id @GeneratedValue private Long id; @NaturalId @Column(length = 15) private String isbn; @Type(type = "json") @Column(columnDefinition = "VARCHAR2(1000) CONSTRAINT IS_VALID_JSON CHECK (properties IS JSON)") private Map<String, String> properties = new HashMap<>(); }
@Entity(name = "Book") @Table(name = "book") @TypeDef(name = "json", typeClass = JsonType.class) public class Book { @Id @GeneratedValue private Long id; @NaturalId @Column(length = 15) private String isbn; @Type(type = "json") @Column(columnDefinition = "NVARCHAR(1000) CHECK(ISJSON(properties) = 1)") private Map<String, String> properties = new HashMap<>(); }
@Entity(name = "Book") @Table(name = "book") @TypeDef(name = "json", typeClass = JsonType.class) public class Book { @Id @GeneratedValue private Long id; @NaturalId @Column(length = 15) private String isbn; @Type(type = "json") @Column(columnDefinition = "jsonb") private Map<String, String> properties = new HashMap<>(); }
@Entity(name = "Book") @Table(name = "book") @TypeDef(name = "json", typeClass = JsonType.class) public class Book { @Id @GeneratedValue private Long id; @NaturalId @Column(length = 15) private String isbn; @Type(type = "json") @Column(columnDefinition = "json") private Map<String, String> properties = new HashMap<>(); }
@Entity(name = "Book") @Table(name = "book") @TypeDef(name = "json", typeClass = JsonType.class) public class Book { @Id @GeneratedValue private Long id; @NaturalId @Column(length = 15) private String isbn; @Type(type = "json") @Column(columnDefinition = "json") private Map<String, String> properties = new HashMap<>(); }
Works like a charm!
So, no more hacks and workarounds, the JsonType
will work no matter what DB you are using.
If you want to see it in action, check out this test folder on GitHub.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With