Support Postgres JSONB/JSON with String - @DbJsonb String content;

This commit is contained in:
Rob Bygrave
2022-06-14 14:57:50 +12:00
parent ab047996d2
commit 583c5f74eb
10 changed files with 273 additions and 10 deletions
@@ -353,6 +353,9 @@ public final class DefaultTypeManager implements TypeManager {
Type genericType = prop.getGenericType();
boolean hasJacksonAnnotations = objectMapperPresent && checkJacksonAnnotations(prop);
if (type.equals(String.class)) {
return ScalarTypeJsonString.typeFor(postgres, dbType);
}
if (type.equals(List.class)) {
DocPropertyType docType = getDocType(genericType);
if (!hasJacksonAnnotations && isValueTypeSimple(genericType)) {
@@ -0,0 +1,41 @@
package io.ebeaninternal.server.type;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.config.dbplatform.ExtraDbTypes;
import io.ebean.core.type.DataBinder;
import io.ebean.core.type.ScalarType;
import java.sql.SQLException;
final class ScalarTypeJsonString {
static final Postgres JSONB = new Postgres(ExtraDbTypes.JSONB, PostgresHelper.JSONB_TYPE);
static final Postgres JSON = new Postgres(ExtraDbTypes.JSON, PostgresHelper.JSON_TYPE);
static ScalarType<?> typeFor(boolean postgres, int dbType) {
if (postgres) {
switch (dbType) {
case DbPlatformType.JSONB:
return JSONB;
case DbPlatformType.JSON:
return JSON;
}
}
return ScalarTypeString.INSTANCE;
}
private static class Postgres extends ScalarTypeStringBase {
final String postgresType;
Postgres(int jdbcType, String postgresType) {
super(true, jdbcType);
this.postgresType = postgresType;
}
@Override
public void bind(DataBinder binder, String rawJson) throws SQLException {
binder.setObject(PostgresHelper.asObject(postgresType, rawJson));
}
}
}
+2 -1
View File
@@ -163,7 +163,8 @@
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.3.3</version>
<!-- <version>42.3.3</version>-->
<version>42.2.9</version>
<exclusions>
<exclusion>
<groupId>org.checkerframework</groupId>
@@ -5,12 +5,12 @@ import io.ebean.test.containers.PostgresContainer;
public class StartPostgres {
public static void main(String[] args) {
PostgresContainer.builder("13")
.port(5432)
PostgresContainer.builder("14")
.dbName("unit")
.user("unit")
.password("unit")
.containerName("pg13x")
//.port(6432)
//.user("unit")
//.password("test")
//.containerName("ut_postgres")
.extensions("hstore,pgcrypto")
.build()
.startWithDropCreate();
@@ -0,0 +1,47 @@
package org.tests.json;
import io.ebean.DB;
import io.ebean.test.LoggedSql;
import org.junit.jupiter.api.Test;
import org.tests.model.json.EBasicJsonBString;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
class TestDbJsonBStringType {
@Test
void test() {
var bean = new EBasicJsonBString("hi").content("{\"mykey\": 42}"); // JsonB format with space
DB.save(bean);
var found = DB.find(EBasicJsonBString.class, bean.id());
// Note that Postgres JsonB will format the result
assertThat(found.content()).isEqualTo("{\"mykey\": 42}");
LoggedSql.start();
// change title only, expect content not in update
found.title("changeTitleOnly");
DB.save(found);
List<String> sql = LoggedSql.collect();
// update does NOT contain our json content
assertThat(sql.get(0)).contains("update ebasic_json_bstring set title=?, version=? where id=? and version=?");
// change title and content
found.title("changeAgain");
found.content("{\"mykey\": 92}");
DB.save(found);
sql = LoggedSql.collect();
assertThat(sql.get(0)).contains("update ebasic_json_bstring set title=?, content=?, version=? where id=? and version=?");
// change content only
found.content("{\"mykey\": 95}");
DB.save(found);
sql = LoggedSql.stop();
assertThat(sql.get(0)).contains("update ebasic_json_bstring set content=?, version=? where id=? and version=?");
}
}
@@ -0,0 +1,47 @@
package org.tests.json;
import io.ebean.DB;
import io.ebean.test.LoggedSql;
import org.junit.jupiter.api.Test;
import org.tests.model.json.EBasicJsonString;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
class TestDbJsonStringType {
@Test
void test() {
var bean = new EBasicJsonString("hi").content("{\"mykey\": 52}"); // JsonB format with space
DB.save(bean);
var found = DB.find(EBasicJsonString.class, bean.id());
// Note that Postgres JsonB will format the result
assertThat(found.content()).isEqualTo("{\"mykey\": 52}");
LoggedSql.start();
// change title only, expect content not in update
found.title("changeTitleOnly");
DB.save(found);
List<String> sql = LoggedSql.collect();
// update does NOT contain our json content
assertThat(sql.get(0)).contains("update ebasic_json_string set title=?, version=? where id=? and version=?");
// change title and content
found.title("changeAgain");
found.content("{\"mykey\": 92}");
DB.save(found);
sql = LoggedSql.collect();
assertThat(sql.get(0)).contains("update ebasic_json_string set title=?, content=?, version=? where id=? and version=?");
// change content only
found.content("{\"mykey\": 95}");
DB.save(found);
sql = LoggedSql.stop();
assertThat(sql.get(0)).contains("update ebasic_json_string set content=?, version=? where id=? and version=?");
}
}
@@ -0,0 +1,62 @@
package org.tests.model.json;
import io.ebean.annotation.DbJsonB;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Version;
@Entity
public class EBasicJsonBString {
@Id
long id;
String title;
@DbJsonB
String content;
@Version
long version;
public EBasicJsonBString(String title) {
this.title = title;
}
public long id() {
return id;
}
public EBasicJsonBString id(long id) {
this.id = id;
return this;
}
public String title() {
return title;
}
public EBasicJsonBString title(String title) {
this.title = title;
return this;
}
public String content() {
return content;
}
public EBasicJsonBString content(String content) {
this.content = content;
return this;
}
public long version() {
return version;
}
public EBasicJsonBString version(long version) {
this.version = version;
return this;
}
}
@@ -0,0 +1,62 @@
package org.tests.model.json;
import io.ebean.annotation.DbJson;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Version;
@Entity
public class EBasicJsonString {
@Id
long id;
String title;
@DbJson
String content;
@Version
long version;
public EBasicJsonString(String title) {
this.title = title;
}
public long id() {
return id;
}
public EBasicJsonString id(long id) {
this.id = id;
return this;
}
public String title() {
return title;
}
public EBasicJsonString title(String title) {
this.title = title;
return this;
}
public String content() {
return content;
}
public EBasicJsonString content(String content) {
this.content = content;
return this;
}
public long version() {
return version;
}
public EBasicJsonString version(long version) {
this.version = version;
return this;
}
}
@@ -22,7 +22,7 @@ ebean.ddl.generate=true
ebean.ddl.run=true
ebean.ddl.header=-- Generated by ebean ${version} at ${timestamp}
ebean.packages=org.tests,org.etest
datasource.default=h2
datasource.default=pg
#datasource.default=sqlserver
#datasource.h2.capturestacktrace=true
@@ -80,9 +80,9 @@
<!-- <logger name="io.ebean.DDL" level="DEBUG"/>-->
<!-- <logger name="io.ebean.SQL" level="TRACE"/>-->
<!-- <logger name="io.ebean.TXN" level="TRACE"/>-->
<!-- <logger name="io.ebean.SUM" level="TRACE"/>-->
<logger name="io.ebean.SQL" level="TRACE"/>
<logger name="io.ebean.TXN" level="TRACE"/>
<logger name="io.ebean.SUM" level="TRACE"/>
<!-- <logger name="io.ebean.cache" level="TRACE"/>-->
<!-- <logger name="io.ebean.cache.REGION" level="TRACE"/>-->