mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#1145 - Refactor @ChangeLog based on JSON documents for 'new values' and 'old values'
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
package io.ebean.bean;
|
||||
|
||||
/**
|
||||
* Visitor for collecting new/old values for a bean update.
|
||||
*/
|
||||
public interface BeanDiffVisitor {
|
||||
|
||||
/**
|
||||
* Collect a new/old value pair.
|
||||
*/
|
||||
void visit(int position, Object newVal, Object oldVal);
|
||||
|
||||
/**
|
||||
* Start processing an associated bean.
|
||||
*/
|
||||
void visitPush(int position);
|
||||
|
||||
/**
|
||||
* Stop processing an associated bean.
|
||||
*/
|
||||
void visitPop();
|
||||
}
|
||||
@@ -694,6 +694,28 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively add dirty properties.
|
||||
*/
|
||||
public void addDirtyPropertyValues(BeanDiffVisitor visitor) {
|
||||
int len = getPropertyLength();
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (changedProps != null && changedProps[i]) {
|
||||
// the property has been changed on this bean
|
||||
Object newVal = owner._ebean_getField(i);
|
||||
Object oldVal = getOrigValue(i);
|
||||
visitor.visit(i, newVal, oldVal);
|
||||
|
||||
} else if (embeddedDirty != null && embeddedDirty[i]) {
|
||||
// an embedded property has been changed - recurse
|
||||
EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
|
||||
visitor.visitPush(i);
|
||||
embeddedBean._ebean_getIntercept().addDirtyPropertyValues(visitor);
|
||||
visitor.visitPop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a dirty property hash taking into account embedded beans.
|
||||
*/
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
package io.ebean.event.changelog;
|
||||
|
||||
import io.ebean.ValuePair;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A bean insert, update or delete change sent as part of a ChangeSet.
|
||||
*/
|
||||
@@ -12,68 +8,68 @@ public class BeanChange {
|
||||
/**
|
||||
* The underling base table name.
|
||||
*/
|
||||
String table;
|
||||
private final String type;
|
||||
|
||||
/**
|
||||
* The tenantId value.
|
||||
*/
|
||||
Object tenantId;
|
||||
|
||||
private final Object tenantId;
|
||||
|
||||
/**
|
||||
* The id value.
|
||||
*/
|
||||
Object id;
|
||||
private final Object id;
|
||||
|
||||
/**
|
||||
* The INSERT, UPDATE or DELETE change type.
|
||||
*/
|
||||
ChangeType type;
|
||||
private final ChangeType event;
|
||||
|
||||
/**
|
||||
* The time the bean change was created.
|
||||
*/
|
||||
long eventTime;
|
||||
private final long eventTime;
|
||||
|
||||
/**
|
||||
* The values for insert or update. Note that null values are not included for insert.
|
||||
* The change in JSON form.
|
||||
*/
|
||||
Map<String, ValuePair> values;
|
||||
private final String data;
|
||||
|
||||
/**
|
||||
* Construct with all the details.
|
||||
* The change in JSON form.
|
||||
*/
|
||||
public BeanChange(String table, Object tenantId, Object id, ChangeType type, Map<String, ValuePair> values) {
|
||||
this.table = table;
|
||||
private final String oldData;
|
||||
|
||||
/**
|
||||
* Construct with change as JSON.
|
||||
*/
|
||||
public BeanChange(String type, Object tenantId, Object id, ChangeType event, String data, String oldData) {
|
||||
this.type = type;
|
||||
this.tenantId = tenantId;
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.event = event;
|
||||
this.eventTime = System.currentTimeMillis();
|
||||
this.values = values;
|
||||
this.data = data;
|
||||
this.oldData = oldData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor for JSON tools.
|
||||
* Construct with change as JSON.
|
||||
*/
|
||||
public BeanChange() {
|
||||
public BeanChange(String table, Object tenantId, Object id, ChangeType event, String data) {
|
||||
this(table, tenantId , id , event , data , null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "table:" + table + " tenantId: " + tenantId + " id:" + id + " values:" + values;
|
||||
return "type:" + type + " tenantId: " + tenantId + " id:" + id + " data:" + data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the object type (typically table name).
|
||||
*/
|
||||
public String getTable() {
|
||||
return table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the object type (for JSON tools).
|
||||
*/
|
||||
public void setTable(String table) {
|
||||
this.table = table;
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,13 +79,6 @@ public class BeanChange {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the bean id (for JSON tools).
|
||||
*/
|
||||
public void setTenantId(Object tenantId) {
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the object id.
|
||||
*/
|
||||
@@ -97,25 +86,11 @@ public class BeanChange {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the bean id (for JSON tools).
|
||||
*/
|
||||
public void setId(Object id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the change type (INSERT, UPDATE or DELETE).
|
||||
*/
|
||||
public ChangeType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the type (for JSON tools).
|
||||
*/
|
||||
public void setType(ChangeType type) {
|
||||
this.type = type;
|
||||
public ChangeType getEvent() {
|
||||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,23 +101,16 @@ public class BeanChange {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the event time in epoch millis.
|
||||
* Return the change data in JSON form.
|
||||
*/
|
||||
public void setEventTime(long eventTime) {
|
||||
this.eventTime = eventTime;
|
||||
public String getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value pairs. For inserts the ValuePair oldValue is always null.
|
||||
* Return the old data in JSON form.
|
||||
*/
|
||||
public Map<String, ValuePair> getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value pairs (for JSON tools).
|
||||
*/
|
||||
public void setValues(Map<String, ValuePair> values) {
|
||||
this.values = values;
|
||||
public String getOldData() {
|
||||
return oldData;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,11 @@ import java.util.List;
|
||||
*/
|
||||
public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionLoader {
|
||||
|
||||
/**
|
||||
* Return the server extended Json context.
|
||||
*/
|
||||
SpiJsonContext jsonExtended();
|
||||
|
||||
/**
|
||||
* For internal use, shutdown of the server invoked by JVM Shutdown.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import io.ebean.text.json.JsonContext;
|
||||
import io.ebean.text.json.JsonWriteOptions;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* Extended Json Context for internal server use.
|
||||
*/
|
||||
public interface SpiJsonContext extends JsonContext {
|
||||
|
||||
/**
|
||||
* Create a Json Writer for writing beans as JSON.
|
||||
*/
|
||||
SpiJsonWriter createJsonWriter(JsonGenerator gen, JsonWriteOptions options);
|
||||
|
||||
/**
|
||||
* Create a Json Writer for writing beans as JSON supplying a writer.
|
||||
*/
|
||||
SpiJsonWriter createJsonWriter(Writer writer);
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
package io.ebeaninternal.server.changelog;
|
||||
|
||||
import io.ebean.ValuePair;
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import io.ebean.event.changelog.BeanChange;
|
||||
import io.ebean.event.changelog.ChangeSet;
|
||||
import io.ebean.event.changelog.ChangeType;
|
||||
import io.ebean.text.json.JsonContext;
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
@@ -15,25 +14,23 @@ import java.util.Map;
|
||||
/**
|
||||
* Builds JSON document for a bean change.
|
||||
*/
|
||||
public class ChangeJsonBuilder {
|
||||
class ChangeJsonBuilder {
|
||||
|
||||
protected final JsonFactory jsonFactory = new JsonFactory();
|
||||
|
||||
protected final JsonContext json;
|
||||
|
||||
protected ChangeJsonBuilder(JsonContext json) {
|
||||
ChangeJsonBuilder(JsonContext json) {
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the bean change as JSON.
|
||||
*/
|
||||
public void writeBeanJson(Writer writer, BeanChange bean, ChangeSet changeSet, int position) throws IOException {
|
||||
void writeBeanJson(Writer writer, BeanChange bean, ChangeSet changeSet) throws IOException {
|
||||
|
||||
try (JsonGenerator generator = jsonFactory.createGenerator(writer)) {
|
||||
|
||||
writeBeanChange(generator, bean, changeSet, position);
|
||||
|
||||
writeBeanChange(generator, bean, changeSet);
|
||||
generator.flush();
|
||||
}
|
||||
}
|
||||
@@ -41,34 +38,29 @@ public class ChangeJsonBuilder {
|
||||
/**
|
||||
* Write the bean change as JSON document containing the transaction header details.
|
||||
*/
|
||||
protected void writeBeanChange(JsonGenerator gen, BeanChange bean, ChangeSet changeSet, int position) throws IOException {
|
||||
private void writeBeanChange(JsonGenerator gen, BeanChange bean, ChangeSet changeSet) throws IOException {
|
||||
|
||||
gen.writeStartObject();
|
||||
|
||||
writeBeanTransactionDetails(gen, changeSet, position);
|
||||
|
||||
gen.writeStringField("object", bean.getTable());
|
||||
gen.writeNumberField("ts", bean.getEventTime());
|
||||
gen.writeStringField("change", bean.getEvent().getCode());
|
||||
gen.writeStringField("type", bean.getType());
|
||||
gen.writeStringField("id", bean.getId().toString());
|
||||
if (bean.getTenantId() != null) {
|
||||
gen.writeStringField("tenantId", bean.getTenantId().toString());
|
||||
}
|
||||
gen.writeStringField("objectId", bean.getId().toString());
|
||||
gen.writeStringField("change", bean.getType().getCode());
|
||||
gen.writeNumberField("eventTime", bean.getEventTime());
|
||||
|
||||
writeBeanTransactionDetails(gen, changeSet);
|
||||
|
||||
writeBeanValues(gen, bean);
|
||||
|
||||
gen.writeEndObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Denormalise by writing the transaction header details.
|
||||
*/
|
||||
protected void writeBeanTransactionDetails(JsonGenerator gen, ChangeSet changeSet, int position) throws IOException {
|
||||
private void writeBeanTransactionDetails(JsonGenerator gen, ChangeSet changeSet) throws IOException {
|
||||
|
||||
gen.writeStringField("txnId", changeSet.getTxnId());
|
||||
gen.writeStringField("txnState", changeSet.getTxnState().getCode());
|
||||
gen.writeNumberField("txnBatch", changeSet.getTxnBatch());
|
||||
gen.writeNumberField("txnPosition", position);
|
||||
String source = changeSet.getSource();
|
||||
if (source != null) {
|
||||
gen.writeStringField("source", source);
|
||||
@@ -94,39 +86,18 @@ public class ChangeJsonBuilder {
|
||||
/**
|
||||
* For insert and update write the new/old values.
|
||||
*/
|
||||
protected void writeBeanValues(JsonGenerator gen, BeanChange bean) throws IOException {
|
||||
private void writeBeanValues(JsonGenerator gen, BeanChange bean) throws IOException {
|
||||
|
||||
if (bean.getType() != ChangeType.DELETE) {
|
||||
gen.writeFieldName("values");
|
||||
gen.writeStartObject();
|
||||
writeValuePairs(bean, gen);
|
||||
gen.writeEndObject();
|
||||
}
|
||||
}
|
||||
if (bean.getEvent() != ChangeType.DELETE) {
|
||||
gen.writeFieldName("data");
|
||||
gen.writeRaw(":");
|
||||
gen.writeRaw(bean.getData());
|
||||
|
||||
/**
|
||||
* Write all the value pairs suppressing null values.
|
||||
* <p>
|
||||
* We are intentionally keeping the same new/old structure for both inserts and updates.
|
||||
* </p>
|
||||
*/
|
||||
protected void writeValuePairs(BeanChange bean, JsonGenerator gen) throws IOException {
|
||||
|
||||
for (Map.Entry<String, ValuePair> entry : bean.getValues().entrySet()) {
|
||||
gen.writeFieldName(entry.getKey());
|
||||
gen.writeStartObject();
|
||||
ValuePair value = entry.getValue();
|
||||
Object newValue = value.getNewValue();
|
||||
if (newValue != null) {
|
||||
gen.writeFieldName("new");
|
||||
json.writeScalar(gen, newValue);
|
||||
String oldData = bean.getOldData();
|
||||
if (oldData != null) {
|
||||
gen.writeRaw(",\"oldData\":");
|
||||
gen.writeRaw(oldData);
|
||||
}
|
||||
Object oldValue = value.getOldValue();
|
||||
if (oldValue != null) {
|
||||
gen.writeFieldName("old");
|
||||
json.writeScalar(gen, oldValue);
|
||||
}
|
||||
gen.writeEndObject();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,15 +10,10 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Logs the change sets in JSON to logger named <code>io.ebean.ChangeLog</code>.
|
||||
* <p>
|
||||
* The logged entries duplicate/denormalise the transaction details so that each bean change
|
||||
* is fully contained with the transaction information.
|
||||
* </p>
|
||||
* Simply logs the change sets in JSON form to logger named <code>io.ebean.ChangeLog</code>.
|
||||
*/
|
||||
public class DefaultChangeLogListener implements ChangeLogListener, Plugin {
|
||||
|
||||
@@ -30,22 +25,17 @@ public class DefaultChangeLogListener implements ChangeLogListener, Plugin {
|
||||
/**
|
||||
* The named logger we send the change set payload to. Can be externally configured as desired.
|
||||
*/
|
||||
protected static final Logger changeLog = LoggerFactory.getLogger("io.ebean.ChangeLog");
|
||||
private static final Logger changeLog = LoggerFactory.getLogger("io.ebean.ChangeLog");
|
||||
|
||||
/**
|
||||
* Used to build the JSON.
|
||||
*/
|
||||
protected ChangeJsonBuilder jsonBuilder;
|
||||
private ChangeJsonBuilder jsonBuilder;
|
||||
|
||||
/**
|
||||
* A bigger default buffer for bean inserts and updates (that have value pairs).
|
||||
*/
|
||||
protected int defaultBufferSize = 400;
|
||||
|
||||
/**
|
||||
* Expected to be a reasonable buffer size for deletes (which do not have value pairs).
|
||||
*/
|
||||
protected int defaultDeleteBufferSize = 250;
|
||||
private int defaultBufferSize = 400;
|
||||
|
||||
public DefaultChangeLogListener() {
|
||||
}
|
||||
@@ -79,13 +69,11 @@ public class DefaultChangeLogListener implements ChangeLogListener, Plugin {
|
||||
@Override
|
||||
public void log(ChangeSet changeSet) {
|
||||
|
||||
List<BeanChange> changes = changeSet.getChanges();
|
||||
for (int i = 0; i < changes.size(); i++) {
|
||||
for (BeanChange beanChange : changeSet.getChanges()) {
|
||||
// log each bean change as a separate log entry
|
||||
BeanChange beanChange = changes.get(i);
|
||||
try {
|
||||
StringWriter writer = new StringWriter(getBufferSize(beanChange));
|
||||
jsonBuilder.writeBeanJson(writer, beanChange, changeSet, i);
|
||||
jsonBuilder.writeBeanJson(writer, beanChange, changeSet);
|
||||
changeLog.info(writer.toString());
|
||||
} catch (Exception e) {
|
||||
logger.error("Exception logging beanChange " + beanChange.toString(), e);
|
||||
@@ -96,9 +84,9 @@ public class DefaultChangeLogListener implements ChangeLogListener, Plugin {
|
||||
/**
|
||||
* Return a decent buffer size based on the bean change.
|
||||
*/
|
||||
protected int getBufferSize(BeanChange beanChange) {
|
||||
private int getBufferSize(BeanChange beanChange) {
|
||||
|
||||
return ChangeType.DELETE == beanChange.getType() ? defaultDeleteBufferSize : defaultBufferSize;
|
||||
return ChangeType.DELETE == beanChange.getEvent() ? 250 : defaultBufferSize;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -51,16 +51,8 @@ import io.ebean.plugin.Plugin;
|
||||
import io.ebean.plugin.SpiServer;
|
||||
import io.ebean.text.csv.CsvReader;
|
||||
import io.ebean.text.json.JsonContext;
|
||||
import io.ebeaninternal.api.LoadBeanRequest;
|
||||
import io.ebeaninternal.api.LoadManyRequest;
|
||||
import io.ebeaninternal.api.ScopeTrans;
|
||||
import io.ebeaninternal.api.ScopedTransaction;
|
||||
import io.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.*;
|
||||
import io.ebeaninternal.api.SpiQuery.Type;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.TransactionEventTable;
|
||||
import io.ebeaninternal.server.autotune.AutoTuneService;
|
||||
import io.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
@@ -179,7 +171,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
private final EncryptKeyManager encryptKeyManager;
|
||||
|
||||
private final JsonContext jsonContext;
|
||||
private final SpiJsonContext jsonContext;
|
||||
|
||||
private final DocumentStore documentStore;
|
||||
|
||||
@@ -2209,6 +2201,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return jsonContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiJsonContext jsonExtended() {
|
||||
return jsonContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collectQueryStats(ObjectGraphNode node, long loadedBeanCount, long timeMicros) {
|
||||
|
||||
|
||||
@@ -3,12 +3,7 @@ package io.ebeaninternal.server.core;
|
||||
import io.ebean.ValuePair;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -54,51 +49,4 @@ public class DiffHelp {
|
||||
return desc.diff((EntityBean) newBean, (EntityBean) oldBean);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Flattens an existing diff map converting assoc one beans into the associated id changes.
|
||||
*/
|
||||
public static Map<String, ValuePair> flatten(Map<String, ValuePair> values, BeanDescriptor<?> desc) {
|
||||
|
||||
Map<String, ValuePair> flattened = null;
|
||||
|
||||
Iterator<Map.Entry<String, ValuePair>> iterator = values.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<String, ValuePair> entry = iterator.next();
|
||||
BeanProperty beanProperty = desc.getBeanProperty(entry.getKey());
|
||||
if (beanProperty instanceof BeanPropertyAssocMany) {
|
||||
// filter out assoc many bean properties
|
||||
iterator.remove();
|
||||
|
||||
} else if (beanProperty instanceof BeanPropertyAssocOne) {
|
||||
BeanPropertyAssocOne<?> assoc = (BeanPropertyAssocOne<?>) beanProperty;
|
||||
if (!assoc.isEmbedded()) {
|
||||
// flatten for assoc one beans
|
||||
if (flattened == null) {
|
||||
flattened = new LinkedHashMap<>();
|
||||
}
|
||||
flattenToId(flattened, entry, beanProperty, assoc);
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flattened != null) {
|
||||
values.putAll(flattened);
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
private static void flattenToId(Map<String, ValuePair> flattened, Map.Entry<String, ValuePair> entry, BeanProperty beanProperty, BeanPropertyAssocOne<?> assoc) {
|
||||
|
||||
BeanDescriptor<?> oneDesc = assoc.getTargetDescriptor();
|
||||
|
||||
ValuePair value = entry.getValue();
|
||||
Object newId = value.getNewValue() == null ? null : oneDesc.getId((EntityBean) value.getNewValue());
|
||||
Object oldId = value.getOldValue() == null ? null : oneDesc.getId((EntityBean) value.getOldValue());
|
||||
|
||||
String propName = beanProperty.getName() + "." + oneDesc.getIdProperty().getName();
|
||||
flattened.put(propName, new ValuePair(newId, oldId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import io.ebean.ExpressionFactory;
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.cache.ServerCacheManager;
|
||||
import io.ebean.config.ExternalTransactionManager;
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.DbHistorySupport;
|
||||
@@ -14,9 +15,9 @@ import io.ebean.event.readaudit.ReadAuditLogger;
|
||||
import io.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import io.ebean.plugin.Plugin;
|
||||
import io.ebean.plugin.SpiServer;
|
||||
import io.ebean.text.json.JsonContext;
|
||||
import io.ebeaninternal.api.SpiBackgroundExecutor;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiJsonContext;
|
||||
import io.ebeaninternal.server.autotune.AutoTuneService;
|
||||
import io.ebeaninternal.server.autotune.service.AutoTuneServiceFactory;
|
||||
import io.ebeaninternal.server.cache.DefaultCacheAdapter;
|
||||
@@ -60,7 +61,6 @@ import io.ebeanservice.docstore.api.DocStoreFactory;
|
||||
import io.ebeanservice.docstore.api.DocStoreIntegration;
|
||||
import io.ebeanservice.docstore.api.DocStoreUpdateProcessor;
|
||||
import io.ebeanservice.docstore.none.NoneDocStoreFactory;
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import org.avaje.datasource.DataSourcePool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -261,7 +261,7 @@ public class InternalConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
public JsonContext createJsonContext(SpiEbeanServer server) {
|
||||
public SpiJsonContext createJsonContext(SpiEbeanServer server) {
|
||||
return new DJsonContext(server, jsonFactory, typeManager);
|
||||
}
|
||||
|
||||
|
||||
@@ -385,7 +385,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Set<?> findSet() {
|
||||
public Set<T> findSet() {
|
||||
return (Set<T>) queryEngine.findMany(this);
|
||||
}
|
||||
|
||||
@@ -393,7 +393,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
* Execute the query as findMap.
|
||||
*/
|
||||
@Override
|
||||
public Map<?, ?> findMap() {
|
||||
@SuppressWarnings("unchecked")
|
||||
public <K> Map<K, T> findMap() {
|
||||
String mapKey = query.getMapKey();
|
||||
if (mapKey == null) {
|
||||
BeanProperty idProp = beanDescriptor.getIdProperty();
|
||||
@@ -403,7 +404,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
throw new PersistenceException("No mapKey specified for query");
|
||||
}
|
||||
}
|
||||
return (Map<?, ?>) queryEngine.findMany(this);
|
||||
return (Map<K, T>) queryEngine.findMany(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -480,7 +481,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
} else {
|
||||
cacheKey = query.queryHash();
|
||||
}
|
||||
|
||||
|
||||
if (!query.getUseQueryCache().isGet()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -828,17 +828,20 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
@Override
|
||||
public void postExecute() {
|
||||
|
||||
changeLog();
|
||||
|
||||
if (controller != null) {
|
||||
controllerPost();
|
||||
}
|
||||
setNotifyCache();
|
||||
|
||||
if (type == Type.UPDATE && (notifyCache || docStoreMode == DocStoreMode.UPDATE)) {
|
||||
boolean isChangeLog = beanDescriptor.isChangeLog();
|
||||
if (type == Type.UPDATE && (isChangeLog || notifyCache || docStoreMode == DocStoreMode.UPDATE)) {
|
||||
// get the dirty properties for update notification to the doc store
|
||||
dirtyProperties = intercept.getDirtyProperties();
|
||||
}
|
||||
if (isChangeLog) {
|
||||
changeLog();
|
||||
}
|
||||
|
||||
// if bean persisted again then should result in an update
|
||||
intercept.setLoaded();
|
||||
if (isInsert()) {
|
||||
@@ -1174,4 +1177,10 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
|
||||
return now;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is a stateless update request (in which case it doesn't really have 'old values').
|
||||
*/
|
||||
public boolean isStatelessUpdate() {
|
||||
return statelessUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,12 +101,12 @@ public interface SpiOrmQueryRequest<T> extends DocQueryRequest<T> {
|
||||
/**
|
||||
* Execute the query as findSet.
|
||||
*/
|
||||
Set<?> findSet();
|
||||
Set<T> findSet();
|
||||
|
||||
/**
|
||||
* Execute the query as findMap.
|
||||
*/
|
||||
Map<?, ?> findMap();
|
||||
<K> Map<K, T> findMap();
|
||||
|
||||
/**
|
||||
* Execute the findSingleAttributeList query.
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.PersistenceIOException;
|
||||
import io.ebean.bean.BeanDiffVisitor;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.util.ArrayStack;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
* Builds the 'new values' and 'old values' in JSON form for ChangeLog.
|
||||
*/
|
||||
class BeanChangeJson implements BeanDiffVisitor {
|
||||
|
||||
private final StringWriter newData;
|
||||
private final StringWriter oldData;
|
||||
|
||||
private final SpiJsonWriter newJson;
|
||||
private final SpiJsonWriter oldJson;
|
||||
|
||||
private final ArrayStack<BeanDescriptor<?>> stack = new ArrayStack<>();
|
||||
|
||||
private BeanDescriptor<?> descriptor;
|
||||
|
||||
BeanChangeJson(BeanDescriptor<?> descriptor, boolean statelessUpdate) {
|
||||
this.descriptor = descriptor;
|
||||
this.newData = new StringWriter(200);
|
||||
this.newJson = descriptor.createJsonWriter(newData);
|
||||
newJson.writeStartObject();
|
||||
|
||||
if (statelessUpdate) {
|
||||
this.oldJson = null;
|
||||
this.oldData = null;
|
||||
} else {
|
||||
this.oldData = new StringWriter(200);
|
||||
this.oldJson = descriptor.createJsonWriter(oldData);
|
||||
oldJson.writeStartObject();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int position, Object newVal, Object oldVal) {
|
||||
|
||||
try {
|
||||
BeanProperty prop = descriptor.propertiesIndex[position];
|
||||
if (prop.isDbUpdatable()) {
|
||||
prop.jsonWriteValue(newJson, newVal);
|
||||
if (oldJson != null) {
|
||||
prop.jsonWriteValue(oldJson, oldVal);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPush(int position) {
|
||||
stack.push(descriptor);
|
||||
|
||||
BeanPropertyAssocOne<?> embedded = (BeanPropertyAssocOne<?>)descriptor.propertiesIndex[position];
|
||||
descriptor = embedded.getTargetDescriptor();
|
||||
newJson.writeStartObject(embedded.getName());
|
||||
if (oldJson != null) {
|
||||
oldJson.writeStartObject(embedded.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPop() {
|
||||
newJson.writeEndObject();
|
||||
if (oldJson != null) {
|
||||
oldJson.writeEndObject();
|
||||
}
|
||||
descriptor = stack.pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the buffers.
|
||||
*/
|
||||
void flush() {
|
||||
try {
|
||||
newJson.writeEndObject();
|
||||
newJson.gen().flush();
|
||||
if (oldJson != null) {
|
||||
oldJson.writeEndObject();
|
||||
oldJson.gen().flush();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the new values JSON.
|
||||
*/
|
||||
String newJson() {
|
||||
return newData.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the old values JSON.
|
||||
*/
|
||||
String oldJson() {
|
||||
return oldData == null ? null : oldData.toString();
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.BeanCollectionAdd;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.text.json.WriteJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -62,6 +62,6 @@ public interface BeanCollectionHelp<T> {
|
||||
/**
|
||||
* Write the collection out as json.
|
||||
*/
|
||||
void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException;
|
||||
void jsonWrite(SpiJsonWriter ctx, String name, Object collection, boolean explicitInclude) throws IOException;
|
||||
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@ import io.ebeaninternal.server.cache.CachedBeanData;
|
||||
import io.ebeaninternal.server.cache.CachedManyIds;
|
||||
import io.ebeaninternal.server.core.CacheOptions;
|
||||
import io.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
import io.ebeaninternal.server.core.DiffHelp;
|
||||
import io.ebeaninternal.server.core.InternString;
|
||||
import io.ebeaninternal.server.core.PersistRequest;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
@@ -67,7 +66,7 @@ import io.ebeaninternal.server.query.CQueryPlanStats.Snapshot;
|
||||
import io.ebeaninternal.server.query.SplitName;
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
import io.ebeaninternal.server.text.json.WriteJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.type.DataBind;
|
||||
import io.ebeaninternal.util.SortByClause;
|
||||
import io.ebeaninternal.util.SortByClauseParser;
|
||||
@@ -89,7 +88,6 @@ import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -810,11 +808,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
* Return true if this request should be included in the change log.
|
||||
*/
|
||||
public BeanChange getChangeLogBean(PersistRequestBean<T> request) {
|
||||
|
||||
if (changeLogFilter == null) {
|
||||
return null;
|
||||
}
|
||||
PersistRequest.Type type = request.getType();
|
||||
switch (request.getType()) {
|
||||
case INSERT:
|
||||
return changeLogFilter.includeInsert(request) ? insertBeanChange(request) : null;
|
||||
@@ -828,30 +821,75 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
}
|
||||
}
|
||||
|
||||
private BeanChange beanChange(ChangeType type, Object id, String data, String oldData) {
|
||||
Object tenantId = ebeanServer.currentTenantId();
|
||||
return new BeanChange(name, tenantId, id, type, data, oldData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bean change for a delete.
|
||||
*/
|
||||
private BeanChange deleteBeanChange(PersistRequestBean<T> request) {
|
||||
return newBeanChange(request.getBeanId(), ChangeType.DELETE, Collections.<String, ValuePair>emptyMap());
|
||||
return beanChange(ChangeType.DELETE, request.getBeanId(), null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bean change for an update.
|
||||
* Return the bean change for an update generating 'new values' and 'old values' in JSON form.
|
||||
*/
|
||||
private BeanChange updateBeanChange(PersistRequestBean<T> request) {
|
||||
return newBeanChange(request.getBeanId(), ChangeType.UPDATE, diffFlatten(request.getEntityBeanIntercept().getDirtyValues()));
|
||||
|
||||
try {
|
||||
BeanChangeJson changeJson = new BeanChangeJson(this, request.isStatelessUpdate());
|
||||
request.getEntityBeanIntercept().addDirtyPropertyValues(changeJson);
|
||||
changeJson.flush();
|
||||
|
||||
return beanChange(ChangeType.UPDATE, request.getBeanId(), changeJson.newJson(), changeJson.oldJson());
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
logger.error("Failed to write ChangeLog entry for update", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bean change for an insert.
|
||||
*/
|
||||
private BeanChange insertBeanChange(PersistRequestBean<T> request) {
|
||||
return newBeanChange(request.getBeanId(), ChangeType.INSERT, diffForInsert(request.getEntityBean()));
|
||||
|
||||
try {
|
||||
StringWriter writer = new StringWriter(200);
|
||||
SpiJsonWriter jsonWriter = createJsonWriter(writer);
|
||||
|
||||
jsonWriteForInsert(jsonWriter, request.getEntityBean());
|
||||
jsonWriter.gen().flush();
|
||||
|
||||
return beanChange(ChangeType.INSERT, request.getBeanId(), writer.toString(), null);
|
||||
|
||||
} catch (IOException e) {
|
||||
logger.error("Failed to write ChangeLog entry for insert", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private BeanChange newBeanChange(Object id, ChangeType changeType, Map<String, ValuePair> values) {
|
||||
Object tenantId = ebeanServer.currentTenantId();
|
||||
return new BeanChange(getBaseTable(), tenantId, id, changeType, values);
|
||||
SpiJsonWriter createJsonWriter(StringWriter writer) {
|
||||
return ebeanServer.jsonExtended().createJsonWriter(writer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the diff for inserts with flattened non-null property values.
|
||||
*/
|
||||
protected void jsonWriteForInsert(SpiJsonWriter jsonWriter, EntityBean newBean) throws IOException {
|
||||
jsonWriter.writeStartObject();
|
||||
for (BeanProperty prop : propertiesBaseScalar) {
|
||||
prop.jsonWriteForInsert(jsonWriter, newBean);
|
||||
}
|
||||
for (BeanPropertyAssocOne<?> prop : propertiesOne) {
|
||||
prop.jsonWriteForInsert(jsonWriter, newBean);
|
||||
}
|
||||
for (BeanPropertyAssocOne<?> prop : propertiesEmbedded) {
|
||||
prop.jsonWriteForInsert(jsonWriter, newBean);
|
||||
}
|
||||
jsonWriter.writeEndObject();
|
||||
}
|
||||
|
||||
public SqlUpdate deleteById(Object id, List<Object> idList, boolean softDelete) {
|
||||
@@ -2856,45 +2894,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten the diff that comes from the entity bean intercept.
|
||||
*/
|
||||
Map<String, ValuePair> diffFlatten(Map<String, ValuePair> diff) {
|
||||
return DiffHelp.flatten(diff, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a map of the differences between a and b.
|
||||
* <p>
|
||||
* A and B must be of the same type. B can be null, in which case the 'dirty
|
||||
* diff' of a is returned.
|
||||
* </p>
|
||||
* <p>
|
||||
* This intentionally does not include as OneToMany or ManyToMany properties.
|
||||
* </p>
|
||||
*/
|
||||
public Map<String, ValuePair> diffForInsert(EntityBean newBean) {
|
||||
|
||||
Map<String, ValuePair> map = new LinkedHashMap<>();
|
||||
diffForInsert(null, map, newBean);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the diff for inserts with flattened non-null property values.
|
||||
*/
|
||||
public void diffForInsert(String prefix, Map<String, ValuePair> map, EntityBean newBean) {
|
||||
for (BeanProperty aPropertiesBaseScalar : propertiesBaseScalar) {
|
||||
aPropertiesBaseScalar.diffForInsert(prefix, map, newBean);
|
||||
}
|
||||
for (BeanPropertyAssocOne<?> aPropertiesOne : propertiesOne) {
|
||||
aPropertiesOne.diffForInsert(prefix, map, newBean);
|
||||
}
|
||||
for (BeanPropertyAssocOne<?> aPropertiesEmbedded : propertiesEmbedded) {
|
||||
aPropertiesEmbedded.diffForInsert(prefix, map, newBean);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the diff comparing the bean values.
|
||||
*/
|
||||
@@ -3071,23 +3070,23 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
return propertiesGenUpdate;
|
||||
}
|
||||
|
||||
public void jsonWriteDirty(WriteJson writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
|
||||
public void jsonWriteDirty(SpiJsonWriter writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
|
||||
jsonHelp.jsonWriteDirty(writeJson, bean, dirtyProps);
|
||||
}
|
||||
|
||||
protected void jsonWriteDirtyProperties(WriteJson writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
|
||||
protected void jsonWriteDirtyProperties(SpiJsonWriter writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
|
||||
jsonHelp.jsonWriteDirtyProperties(writeJson, bean, dirtyProps);
|
||||
}
|
||||
|
||||
public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException {
|
||||
public void jsonWrite(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
|
||||
jsonHelp.jsonWrite(writeJson, bean, null);
|
||||
}
|
||||
|
||||
public void jsonWrite(WriteJson writeJson, EntityBean bean, String key) throws IOException {
|
||||
public void jsonWrite(SpiJsonWriter writeJson, EntityBean bean, String key) throws IOException {
|
||||
jsonHelp.jsonWrite(writeJson, bean, key);
|
||||
}
|
||||
|
||||
protected void jsonWriteProperties(WriteJson writeJson, EntityBean bean) throws IOException {
|
||||
protected void jsonWriteProperties(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
|
||||
jsonHelp.jsonWriteProperties(writeJson, bean);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,7 @@ import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.text.json.EJson;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
import io.ebeaninternal.server.text.json.WriteJson;
|
||||
import io.ebeaninternal.server.text.json.WriteJson.WriteBean;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -24,7 +23,7 @@ public class BeanDescriptorJsonHelp<T> {
|
||||
this.inheritInfo = desc.inheritInfo;
|
||||
}
|
||||
|
||||
public void jsonWrite(WriteJson writeJson, EntityBean bean, String key) throws IOException {
|
||||
public void jsonWrite(SpiJsonWriter writeJson, EntityBean bean, String key) throws IOException {
|
||||
|
||||
writeJson.writeStartObject(key);
|
||||
|
||||
@@ -43,13 +42,12 @@ public class BeanDescriptorJsonHelp<T> {
|
||||
writeJson.writeEndObject();
|
||||
}
|
||||
|
||||
protected void jsonWriteProperties(WriteJson writeJson, EntityBean bean) throws IOException {
|
||||
protected void jsonWriteProperties(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
|
||||
|
||||
WriteBean writeBean = writeJson.createWriteBean(desc, bean);
|
||||
writeBean.write(writeJson);
|
||||
writeJson.writeBean(desc, bean);
|
||||
}
|
||||
|
||||
public void jsonWriteDirty(WriteJson writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
|
||||
public void jsonWriteDirty(SpiJsonWriter writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
|
||||
|
||||
if (inheritInfo == null) {
|
||||
jsonWriteDirtyProperties(writeJson, bean, dirtyProps);
|
||||
@@ -58,7 +56,7 @@ public class BeanDescriptorJsonHelp<T> {
|
||||
}
|
||||
}
|
||||
|
||||
protected void jsonWriteDirtyProperties(WriteJson writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
|
||||
protected void jsonWriteDirtyProperties(SpiJsonWriter writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
|
||||
|
||||
writeJson.writeStartObject(null);
|
||||
// render the dirty properties
|
||||
|
||||
@@ -8,7 +8,7 @@ import io.ebean.bean.BeanCollectionAdd;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.common.BeanList;
|
||||
import io.ebeaninternal.server.text.json.WriteJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@@ -125,7 +125,7 @@ public final class BeanListHelp<T> implements BeanCollectionHelp<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException {
|
||||
public void jsonWrite(SpiJsonWriter ctx, String name, Object collection, boolean explicitInclude) throws IOException {
|
||||
|
||||
List<?> list;
|
||||
if (collection instanceof BeanCollection<?>) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import io.ebean.bean.BeanCollectionAdd;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.common.BeanMap;
|
||||
import io.ebeaninternal.server.text.json.WriteJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -160,7 +160,7 @@ public final class BeanMapHelp<T> implements BeanCollectionHelp<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException {
|
||||
public void jsonWrite(SpiJsonWriter ctx, String name, Object collection, boolean explicitInclude) throws IOException {
|
||||
|
||||
Map<?, ?> map;
|
||||
if (collection instanceof BeanCollection<?>) {
|
||||
|
||||
@@ -24,7 +24,7 @@ import io.ebeaninternal.server.query.SplitName;
|
||||
import io.ebeaninternal.server.query.SqlBeanLoad;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
import io.ebeaninternal.server.text.json.WriteJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.type.DataBind;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
import io.ebeaninternal.server.type.ScalarTypeBoolean;
|
||||
@@ -1360,21 +1360,55 @@ public class BeanProperty implements ElPropertyValue, Property {
|
||||
return jsonSerialize;
|
||||
}
|
||||
|
||||
@SuppressWarnings(value = "unchecked")
|
||||
public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException {
|
||||
/**
|
||||
* JSON write the property for 'insert only depth'.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void jsonWriteForInsert(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
|
||||
if (!jsonSerialize) {
|
||||
return;
|
||||
}
|
||||
Object value = getValueIntercept(bean);
|
||||
Object value = getValue(bean);
|
||||
if (value != null) {
|
||||
jsonWriteScalar(writeJson, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON write the property value.
|
||||
*/
|
||||
public void jsonWriteValue(SpiJsonWriter writeJson, Object value) throws IOException {
|
||||
if (!jsonSerialize) {
|
||||
return;
|
||||
}
|
||||
jsonWriteVal(writeJson, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON write the bean property.
|
||||
*/
|
||||
public void jsonWrite(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
|
||||
if (!jsonSerialize) {
|
||||
return;
|
||||
}
|
||||
jsonWriteVal(writeJson, getValueIntercept(bean));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void jsonWriteVal(SpiJsonWriter writeJson, Object value) throws IOException {
|
||||
if (value == null) {
|
||||
writeJson.writeNullField(name);
|
||||
} else {
|
||||
if (scalarType != null) {
|
||||
writeJson.writeFieldName(name);
|
||||
scalarType.jsonWrite(writeJson.gen(), value);
|
||||
} else {
|
||||
writeJson.writeValueUsingObjectMapper(name, value);
|
||||
}
|
||||
jsonWriteScalar(writeJson, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void jsonWriteScalar(SpiJsonWriter writeJson, Object value) throws IOException {
|
||||
if (scalarType != null) {
|
||||
writeJson.writeFieldName(name);
|
||||
scalarType.jsonWrite(writeJson.gen(), value);
|
||||
} else {
|
||||
writeJson.writeValueUsingObjectMapper(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1407,17 +1441,6 @@ public class BeanProperty implements ElPropertyValue, Property {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate diff map for insert if the property is not null.
|
||||
*/
|
||||
public void diffForInsert(String prefix, Map<String, ValuePair> map, EntityBean newBean) {
|
||||
Object newVal = (newBean == null) ? null : getValue(newBean);
|
||||
if (newVal != null) {
|
||||
String propName = (prefix == null) ? name : prefix + "." + name;
|
||||
map.put(propName, new ValuePair(newVal, null));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate diff map comparing the property values between the beans.
|
||||
*/
|
||||
|
||||
@@ -20,7 +20,7 @@ import io.ebeaninternal.server.el.ElPropertyChainBuilder;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.query.SqlBeanLoad;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
import io.ebeaninternal.server.text.json.WriteJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -962,8 +962,15 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
return null != targetDescriptor.getId(otherBean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip JSON write value for ToMany property.
|
||||
*/
|
||||
public void jsonWriteValue(SpiJsonWriter writeJson, Object value) throws IOException {
|
||||
// do nothing, exclude ToMany properties
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(WriteJson ctx, EntityBean bean) throws IOException {
|
||||
public void jsonWrite(SpiJsonWriter ctx, EntityBean bean) throws IOException {
|
||||
if (!this.jsonSerialize) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public class BeanPropertyAssocManyJsonHelp {
|
||||
return;
|
||||
}
|
||||
if (JsonToken.START_ARRAY != event) {
|
||||
throw new JsonParseException("Unexpected token " + event + " - expecting start_array ", parser.getCurrentLocation());
|
||||
throw new JsonParseException(parser, "Unexpected token " + event + " - expecting start_array ");
|
||||
}
|
||||
|
||||
if (many.isTransient()) {
|
||||
|
||||
@@ -18,7 +18,7 @@ import io.ebeaninternal.server.query.SplitName;
|
||||
import io.ebeaninternal.server.query.SqlBeanLoad;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
import io.ebeaninternal.server.text.json.WriteJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.IOException;
|
||||
@@ -351,22 +351,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
return importedPrimaryKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void diffForInsert(String prefix, Map<String, ValuePair> map, EntityBean newBean) {
|
||||
Object newEmb = (newBean == null) ? null : getValue(newBean);
|
||||
if (newEmb != null) {
|
||||
prefix = (prefix == null) ? name : prefix + "." + name;
|
||||
if (embedded) {
|
||||
getTargetDescriptor().diffForInsert(prefix, map, (EntityBean) newEmb);
|
||||
} else {
|
||||
// we are only interested in the Id value
|
||||
BeanDescriptor<T> targetDescriptor = getTargetDescriptor();
|
||||
BeanProperty idProperty = targetDescriptor.getIdProperty();
|
||||
idProperty.diffForInsert(prefix, map, (EntityBean) newEmb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void diff(String prefix, Map<String, ValuePair> map, EntityBean newBean, EntityBean oldBean) {
|
||||
|
||||
@@ -676,8 +660,57 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON write property (non-recursive to other beans).
|
||||
*/
|
||||
@Override
|
||||
public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException {
|
||||
public void jsonWriteForInsert(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
|
||||
|
||||
if (!jsonSerialize) {
|
||||
return;
|
||||
}
|
||||
jsonWriteBean(writeJson, getValue(bean));
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON write property value (non-recursive to other beans).
|
||||
*/
|
||||
@Override
|
||||
public void jsonWriteValue(SpiJsonWriter writeJson, Object value) throws IOException {
|
||||
if (!jsonSerialize) {
|
||||
return;
|
||||
}
|
||||
jsonWriteBean(writeJson, value);
|
||||
}
|
||||
|
||||
private void jsonWriteBean(SpiJsonWriter writeJson, Object value) throws IOException {
|
||||
|
||||
if (value instanceof EntityBean) {
|
||||
if (embedded) {
|
||||
writeJson.writeFieldName(name);
|
||||
BeanDescriptor<?> refDesc = descriptor.getBeanDescriptor(value.getClass());
|
||||
refDesc.jsonWriteForInsert(writeJson, (EntityBean)value);
|
||||
|
||||
} else {
|
||||
jsonWriteTargetId(writeJson, (EntityBean)value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Just write the Id property of the ToOne property.
|
||||
*/
|
||||
private void jsonWriteTargetId(SpiJsonWriter writeJson, EntityBean childBean) throws IOException {
|
||||
BeanProperty idProperty = targetDescriptor.getIdProperty();
|
||||
if (idProperty != null) {
|
||||
writeJson.writeStartObject(name);
|
||||
idProperty.jsonWriteForInsert(writeJson, childBean);
|
||||
writeJson.writeEndObject();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
|
||||
|
||||
if (!jsonSerialize) {
|
||||
return;
|
||||
@@ -688,7 +721,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
writeJson.writeNullField(name);
|
||||
|
||||
} else {
|
||||
//noinspection StatementWithEmptyBody
|
||||
if (writeJson.isParentBean(value)) {
|
||||
// bi-directional and already rendered parent
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import io.ebean.bean.BeanCollectionAdd;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.common.BeanSet;
|
||||
import io.ebeaninternal.server.text.json.WriteJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashSet;
|
||||
@@ -124,7 +124,7 @@ public final class BeanSetHelp<T> implements BeanCollectionHelp<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException {
|
||||
public void jsonWrite(SpiJsonWriter ctx, String name, Object collection, boolean explicitInclude) throws IOException {
|
||||
|
||||
Set<?> set;
|
||||
if (collection instanceof BeanCollection<?>) {
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
package io.ebeaninternal.server.text.json;
|
||||
|
||||
import io.ebean.FetchPath;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.config.JsonConfig;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.text.json.EJson;
|
||||
import io.ebean.text.json.JsonContext;
|
||||
import io.ebean.text.json.JsonIOException;
|
||||
import io.ebean.text.json.JsonReadOptions;
|
||||
import io.ebean.text.json.JsonWriteBeanVisitor;
|
||||
import io.ebean.text.json.JsonWriteOptions;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.type.TypeManager;
|
||||
import io.ebeaninternal.util.ParamTypeHelper;
|
||||
import io.ebeaninternal.util.ParamTypeHelper.ManyType;
|
||||
import io.ebeaninternal.util.ParamTypeHelper.TypeInfo;
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.ebean.FetchPath;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.config.JsonConfig;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.text.json.EJson;
|
||||
import io.ebean.text.json.JsonIOException;
|
||||
import io.ebean.text.json.JsonReadOptions;
|
||||
import io.ebean.text.json.JsonWriteBeanVisitor;
|
||||
import io.ebean.text.json.JsonWriteOptions;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiJsonContext;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.type.TypeManager;
|
||||
import io.ebeaninternal.util.ParamTypeHelper;
|
||||
import io.ebeaninternal.util.ParamTypeHelper.ManyType;
|
||||
import io.ebeaninternal.util.ParamTypeHelper.TypeInfo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
@@ -39,7 +39,7 @@ import java.util.Set;
|
||||
/**
|
||||
* Default implementation of JsonContext.
|
||||
*/
|
||||
public class DJsonContext implements JsonContext {
|
||||
public class DJsonContext implements SpiJsonContext {
|
||||
|
||||
private final SpiEbeanServer server;
|
||||
|
||||
@@ -179,7 +179,7 @@ public class DJsonContext implements JsonContext {
|
||||
if (currentToken != JsonToken.START_ARRAY) {
|
||||
JsonToken event = src.nextToken();
|
||||
if (event != JsonToken.START_ARRAY) {
|
||||
throw new JsonParseException("Expecting start_array event but got " + event, src.getCurrentLocation());
|
||||
throw new JsonParseException(src, "Expecting start_array event but got " + event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,9 +336,23 @@ public class DJsonContext implements JsonContext {
|
||||
BeanDescriptor<?> d = getDescriptor(value.getClass());
|
||||
WriteJson writeJson = createWriteJson(gen, options);
|
||||
d.jsonWrite(writeJson, (EntityBean) value, null);
|
||||
|
||||
} else {
|
||||
jsonScalar.write(gen, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiJsonWriter createJsonWriter(Writer writer) {
|
||||
JsonGenerator generator = createGenerator(writer);
|
||||
return createJsonWriter(generator, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiJsonWriter createJsonWriter(JsonGenerator gen, JsonWriteOptions options) {
|
||||
return createWriteJson(gen, options);
|
||||
}
|
||||
|
||||
private WriteJson createWriteJson(JsonGenerator gen, JsonWriteOptions options) {
|
||||
FetchPath pathProps = (options == null) ? null : options.getPathProperties();
|
||||
Map<String, JsonWriteBeanVisitor<?>> visitors = (options == null) ? null : options.getVisitorMap();
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package io.ebeaninternal.server.text.json;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.text.json.JsonWriter;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Internal API extensions for JSON writing of Bean properties.
|
||||
*/
|
||||
public interface SpiJsonWriter extends JsonWriter {
|
||||
|
||||
/**
|
||||
* Return true if the value is a parent bean.
|
||||
*/
|
||||
boolean isParentBean(Object value);
|
||||
|
||||
/**
|
||||
* Start an assoc one path.
|
||||
*/
|
||||
void beginAssocOne(String name, EntityBean bean);
|
||||
|
||||
/**
|
||||
* End an assoc one path.
|
||||
*/
|
||||
void endAssocOne();
|
||||
|
||||
/**
|
||||
* Return true if the many property should be included.
|
||||
*/
|
||||
Boolean includeMany(String name);
|
||||
|
||||
/**
|
||||
* Push the parent bean of a ToMany.
|
||||
*/
|
||||
void pushParentBeanMany(EntityBean bean);
|
||||
|
||||
/**
|
||||
* Pop the parent of a ToMany.
|
||||
*/
|
||||
void popParentBeanMany();
|
||||
|
||||
/**
|
||||
* Write the collection.
|
||||
*/
|
||||
void toJson(String name, Collection<?> collection);
|
||||
|
||||
/**
|
||||
* Start a Many.
|
||||
*/
|
||||
void beginAssocMany(String name);
|
||||
|
||||
/**
|
||||
* End a Many.
|
||||
*/
|
||||
void endAssocMany();
|
||||
|
||||
/**
|
||||
* Write value using underlying Jaskson object mapper if available.
|
||||
*/
|
||||
void writeValueUsingObjectMapper(String name, Object value);
|
||||
|
||||
/**
|
||||
* Write the bean properties.
|
||||
*/
|
||||
<T> void writeBean(BeanDescriptor<T> desc, EntityBean bean);
|
||||
}
|
||||
@@ -1,18 +1,17 @@
|
||||
package io.ebeaninternal.server.text.json;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.ebean.FetchPath;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.config.JsonConfig;
|
||||
import io.ebean.text.json.EJson;
|
||||
import io.ebean.text.json.JsonIOException;
|
||||
import io.ebean.text.json.JsonWriteBeanVisitor;
|
||||
import io.ebean.text.json.JsonWriter;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.util.ArrayStack;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -21,7 +20,7 @@ import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class WriteJson implements JsonWriter {
|
||||
public class WriteJson implements SpiJsonWriter {
|
||||
|
||||
private final SpiEbeanServer server;
|
||||
|
||||
@@ -347,7 +346,7 @@ public class WriteJson implements JsonWriter {
|
||||
return !parentBeans.isEmpty() && parentBeans.contains(bean);
|
||||
}
|
||||
|
||||
public void pushParentBeanMany(Object parentBean) {
|
||||
public void pushParentBeanMany(EntityBean parentBean) {
|
||||
parentBeans.push(parentBean);
|
||||
}
|
||||
|
||||
@@ -355,7 +354,7 @@ public class WriteJson implements JsonWriter {
|
||||
parentBeans.pop();
|
||||
}
|
||||
|
||||
public void beginAssocOne(String key, Object bean) {
|
||||
public void beginAssocOne(String key, EntityBean bean) {
|
||||
parentBeans.push(bean);
|
||||
pathStack.pushPathKey(key);
|
||||
}
|
||||
@@ -384,7 +383,12 @@ public class WriteJson implements JsonWriter {
|
||||
}
|
||||
}
|
||||
|
||||
public WriteBean createWriteBean(BeanDescriptor<?> desc, EntityBean bean) {
|
||||
@Override
|
||||
public <T> void writeBean(BeanDescriptor<T> desc, EntityBean bean) {
|
||||
createWriteBean(desc, bean).write(this);
|
||||
}
|
||||
|
||||
private <T> WriteBean createWriteBean(BeanDescriptor<T> desc, EntityBean bean) {
|
||||
|
||||
String path = pathStack.peekWithNull();
|
||||
JsonWriteBeanVisitor<?> visitor = (visitors == null) ? null : visitors.get(path);
|
||||
@@ -436,7 +440,7 @@ public class WriteJson implements JsonWriter {
|
||||
final Set<String> currentIncludeProps;
|
||||
final BeanDescriptor<?> desc;
|
||||
final EntityBean currentBean;
|
||||
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
final JsonWriteBeanVisitor visitor;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user