diff --git a/ebean-test/src/test/java/org/tests/docstore/CustomerReportTest.java b/ebean-test/src/test/java/org/tests/docstore/CustomerReportTest.java index 1dc65930a7..1b45aef06b 100644 --- a/ebean-test/src/test/java/org/tests/docstore/CustomerReportTest.java +++ b/ebean-test/src/test/java/org/tests/docstore/CustomerReportTest.java @@ -1,14 +1,18 @@ package org.tests.docstore; -import io.ebean.xtest.BaseTestCase; +import io.ebean.BeanState; +import io.ebean.DB; +import io.ebean.test.LoggedSql; import io.ebean.text.json.JsonReadOptions; +import io.ebean.xtest.BaseTestCase; import org.junit.jupiter.api.Test; import org.tests.model.basic.Customer; import org.tests.model.basic.Product; import org.tests.model.basic.ResetBasicData; import org.tests.model.docstore.CustomerReport; import org.tests.model.docstore.ProductReport; +import org.tests.model.docstore.ReportContainer; import java.util.Arrays; @@ -21,7 +25,6 @@ public void testToJson() throws Exception { ResetBasicData.reset(); - String json = server().json().toJson(getCustomerReport()); assertThat(json).isEqualTo("{\"dtype\":\"CR\",\"friends\":[{\"id\":2},{\"id\":3}],\"customer\":{\"id\":1}}"); } @@ -52,9 +55,9 @@ public void testEmbeddedDocs() throws Exception { String json = server().json().toJson(report); assertThat(json).isEqualTo("{\"dtype\":\"CR\"," - + "\"embeddedReports\":[{\"dtype\":\"PR\",\"title\":\"This is a good product\",\"product\":{\"id\":1}}]," - + "\"friends\":[{\"id\":2},{\"id\":3}]," - + "\"customer\":{\"id\":1}}"); + + "\"embeddedReports\":[{\"dtype\":\"PR\",\"title\":\"This is a good product\",\"product\":{\"id\":1}}]," + + "\"friends\":[{\"id\":2},{\"id\":3}]," + + "\"customer\":{\"id\":1}}"); JsonReadOptions opts = new JsonReadOptions(); opts.setEnableLazyLoading(true); @@ -85,4 +88,85 @@ private ProductReport getProductReport() { report.setProduct(product); return report; } + + /** + * Tests the inheritance support for DocStore/Jsons. + * We do not use default jackson serialization. In Report-class + * there are (de)serializers, that will delegate the (de)serialization + * back to ebean. + *
+ * Big advantage: Ebean supports Inheritance with JSONS and some kind + * of "autodiscovery". + *
+ * In theory, Jackson could do serialization with `@JsonSubTypes`, but
+ * they have to be specified in the top class. See
+ * https://github.com/FasterXML/jackson-databind/issues/2104
+ */
+ @Test
+ public void testInheritance() {
+ ReportContainer container = new ReportContainer();
+ container.setReport(new ProductReport());
+ DB.save(container);
+
+ ReportContainer container2 = new ReportContainer();
+ container2.setReport(new CustomerReport());
+ DB.save(container2);
+
+ container = DB.find(ReportContainer.class, container.getId());
+ container2 = DB.find(ReportContainer.class, container2.getId());
+
+ assertThat(container.getReport()).isInstanceOf(ProductReport.class);
+ assertThat(container2.getReport()).isInstanceOf(CustomerReport.class);
+ }
+
+ /**
+ * This test shows how we do the (de)serialization when we reference entites, that are persisted in the DB
+ */
+ @Test
+ public void testReferenceBean() {
+ ResetBasicData.reset();
+ ReportContainer container = new ReportContainer();
+ CustomerReport report = new CustomerReport();
+ container.setReport(report);
+
+ Object robId = DB.find(Customer.class).where().eq("name", "Rob").findIds().get(0);
+ report.setFriends(DB.find(Customer.class).where().eq("name", "Fiona").findList());
+ LoggedSql.start();
+ report.setCustomer(DB.reference(Customer.class, robId));
+ DB.save(container);
+ assertThat(LoggedSql.stop()).hasSize(1); // no lazy load
+
+ container = DB.find(ReportContainer.class, container.getId());
+ assertThat(((CustomerReport) container.getReport()).getCustomer().getName()).isEqualTo("Rob");
+ assertThat(((CustomerReport) container.getReport()).getFriends().get(0).getName()).isEqualTo("Fiona");
+ }
+
+ /**
+ * This test shows the dirty detection on docstore beans.
+ */
+ @Test
+ public void testJsonBeanState() {
+ ResetBasicData.reset();
+ ReportContainer container = new ReportContainer();
+ CustomerReport report = new CustomerReport();
+ report.setTitle("Foo");
+ container.setReport(report);
+
+ BeanState state = DB.beanState(container.getReport());
+ assertThat(state.isNew()).isTrue();
+
+ DB.save(container);
+ container = DB.find(ReportContainer.class, container.getId());
+
+ state = DB.beanState(container.getReport());
+ assertThat(state.isDirty()).isFalse();
+ assertThat(state.isNew()).isFalse(); // this detection does not work on JSONs
+
+ container.getReport().setTitle("Bar");
+ state = DB.beanState(container.getReport());
+ assertThat(state.isDirty()).isTrue();
+ //BTW: ValuePair has no equals & hashcode
+ //assertThat(state.dirtyValues()).hasSize(1).containsEntry("title", new ValuePair("Bar", "Foo"));
+ assertThat(state.dirtyValues()).hasSize(1).hasToString("{title=Bar,Foo}");
+ }
}
diff --git a/ebean-test/src/test/java/org/tests/model/docstore/Report.java b/ebean-test/src/test/java/org/tests/model/docstore/Report.java
index d5dcaf8376..bf8c8d9c5a 100644
--- a/ebean-test/src/test/java/org/tests/model/docstore/Report.java
+++ b/ebean-test/src/test/java/org/tests/model/docstore/Report.java
@@ -1,13 +1,19 @@
package org.tests.model.docstore;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.ebean.annotation.DocStore;
-
+import jakarta.persistence.DiscriminatorColumn;
import jakarta.persistence.Inheritance;
import jakarta.persistence.OneToMany;
+
import java.util.List;
@DocStore
@Inheritance
+@DiscriminatorColumn
+@JsonSerialize(using = ReportJsonSerializer.class)
+@JsonDeserialize(using = ReportJsonDeserializer.class)
public class Report {
private String title;
diff --git a/ebean-test/src/test/java/org/tests/model/docstore/ReportContainer.java b/ebean-test/src/test/java/org/tests/model/docstore/ReportContainer.java
new file mode 100644
index 0000000000..4f768223c8
--- /dev/null
+++ b/ebean-test/src/test/java/org/tests/model/docstore/ReportContainer.java
@@ -0,0 +1,31 @@
+package org.tests.model.docstore;
+
+import io.ebean.annotation.DbJson;
+import org.tests.model.basic.BasicDomain;
+
+import javax.persistence.Entity;
+
+/**
+ * The reportcontainer itself persists the Report JSON in the database.
+ *
+ * @author Roland Praml, FOCONIS AG
+ */
+@Entity
+public class ReportContainer extends BasicDomain {
+
+ // By default, ebean will use Jackson as serializer here.
+ // It would be great, if ebean will serialize docstore beans automatically with DB.json()
+ // This is currently achieved through the JsonSerialize/Deserialize annotations in the report class
+ // (ebean -> jackson -> ebean)
+ @DbJson(length = 1024 * 1024) // we do not expect report definitions bigger than 1M
+ private Report report;
+
+
+ public Report getReport() {
+ return report;
+ }
+
+ public void setReport(Report report) {
+ this.report = report;
+ }
+}
diff --git a/ebean-test/src/test/java/org/tests/model/docstore/ReportJsonDeserializer.java b/ebean-test/src/test/java/org/tests/model/docstore/ReportJsonDeserializer.java
new file mode 100644
index 0000000000..76fe07d4e8
--- /dev/null
+++ b/ebean-test/src/test/java/org/tests/model/docstore/ReportJsonDeserializer.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed Materials - Property of FOCONIS AG
+ * (C) Copyright FOCONIS AG.
+ */
+
+package org.tests.model.docstore;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import io.ebean.DB;
+import io.ebean.bean.EntityBean;
+import io.ebean.text.json.JsonReadOptions;
+
+import java.io.IOException;
+
+/**
+ * Deserializer, that uses ebean deserialization when deserializing beans from DB.
+ */
+public class ReportJsonDeserializer extends JsonDeserializer
+ * It also handles the "id" serialization of referenced properties.
+ *
+ * Note: This is a very simple class stripped down to provide a test case
+ * (we use a more generic one in our code)
+ */
+public class ReportJsonSerializer extends JsonSerializer