Merge pull request #2107 from ebean-orm/feature/ModifyAwareType

Refactor io.ebeaninternal.json.ModifyAwareOwner to io.ebean.ModifyAwareType
This commit is contained in:
Rob Bygrave
2020-11-24 22:05:04 +13:00
committed by GitHub
19 changed files with 314 additions and 90 deletions
@@ -1,11 +1,9 @@
package io.ebeaninternal.json;
import java.io.Serializable;
package io.ebean;
/**
* Owner object notified when a modification is detected.
*/
public interface ModifyAwareOwner extends Serializable {
public interface ModifyAwareType {
/**
* Return true if the value is considered dirty.
@@ -16,11 +14,6 @@ public interface ModifyAwareOwner extends Serializable {
/**
* Marks the object as modified.
*/
void markAsModified();
/**
* Reset the dirty state to clean.
*/
void resetMarkedDirty();
void setMarkedDirty(boolean markedDirty);
}
@@ -3,6 +3,7 @@ package io.ebeaninternal.json;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import io.ebean.ModifyAwareType;
import java.io.IOException;
import java.io.Reader;
@@ -145,7 +146,7 @@ class EJsonReader {
currentContext = stack.pop(currentContext);
}
if (modifyAwareOwner != null) {
modifyAwareOwner.resetMarkedDirty();
modifyAwareOwner.setMarkedDirty(false);
}
}
@@ -296,7 +297,7 @@ class EJsonReader {
map = new LinkedHashMap<>();
}
ObjectContext(ModifyAwareOwner owner) {
ObjectContext(ModifyAwareType owner) {
map = new ModifyAwareMap<>(owner, new LinkedHashMap<>());
}
@@ -334,7 +335,7 @@ class EJsonReader {
values = new ArrayList<>();
}
ArrayContext(ModifyAwareOwner owner) {
ArrayContext(ModifyAwareType owner) {
values = new ModifyAwareList<>(owner, new ArrayList<>());
}
@@ -1,28 +1,26 @@
package io.ebeaninternal.json;
import io.ebean.ModifyAwareType;
import java.io.Serializable;
/**
* Detects when content has been modified and as such needs to be persisted (included in an update).
*/
public class ModifyAwareFlag implements ModifyAwareOwner {
public class ModifyAwareFlag implements ModifyAwareType, Serializable {
private static final long serialVersionUID = 1;
boolean dirty;
private boolean markedDirty;
@Override
public boolean isMarkedDirty() {
if (!dirty) return false;
dirty = false;
return true;
return markedDirty;
}
@Override
public void markAsModified() {
dirty = true;
public void setMarkedDirty(boolean markedDirty) {
this.markedDirty = markedDirty;
}
@Override
public void resetMarkedDirty() {
dirty = false;
}
}
@@ -1,5 +1,7 @@
package io.ebeaninternal.json;
import io.ebean.ModifyAwareType;
import java.util.Iterator;
/**
@@ -7,7 +9,7 @@ import java.util.Iterator;
*/
public class ModifyAwareIterator<E> implements Iterator<E> {
private final ModifyAwareOwner owner;
private final ModifyAwareType owner;
private final Iterator<E> it;
@@ -17,7 +19,7 @@ public class ModifyAwareIterator<E> implements Iterator<E> {
* The owner is notified of the removals.
* </p>
*/
public ModifyAwareIterator(ModifyAwareOwner owner, Iterator<E> it) {
public ModifyAwareIterator(ModifyAwareType owner, Iterator<E> it) {
this.owner = owner;
this.it = it;
}
@@ -34,7 +36,7 @@ public class ModifyAwareIterator<E> implements Iterator<E> {
@Override
public void remove() {
owner.markAsModified();
owner.setMarkedDirty(true);
it.remove();
}
@@ -1,5 +1,8 @@
package io.ebeaninternal.json;
import io.ebean.ModifyAwareType;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
@@ -10,20 +13,20 @@ import java.util.Objects;
/**
* Modify aware wrapper of a list.
*/
public class ModifyAwareList<E> implements List<E>, ModifyAwareOwner {
public class ModifyAwareList<E> implements List<E>, ModifyAwareType, Serializable {
private static final long serialVersionUID = 1;
final List<E> list;
final ModifyAwareOwner owner;
final ModifyAwareType owner;
public ModifyAwareList(List<E> list) {
this.list = list;
this.owner = new ModifyAwareFlag();
}
public ModifyAwareList(ModifyAwareOwner owner, List<E> list) {
public ModifyAwareList(ModifyAwareType owner, List<E> list) {
this.list = list;
this.owner = owner;
}
@@ -56,13 +59,12 @@ public class ModifyAwareList<E> implements List<E>, ModifyAwareOwner {
}
@Override
public void markAsModified() {
owner.markAsModified();
public void setMarkedDirty(boolean markedDirty) {
owner.setMarkedDirty(markedDirty);
}
@Override
public void resetMarkedDirty() {
owner.resetMarkedDirty();
private void markAsDirty() {
owner.setMarkedDirty(true);
}
@Override
@@ -98,13 +100,13 @@ public class ModifyAwareList<E> implements List<E>, ModifyAwareOwner {
@Override
public boolean add(E e) {
owner.markAsModified();
markAsDirty();
return list.add(e);
}
@Override
public boolean remove(Object o) {
owner.markAsModified();
markAsDirty();
return list.remove(o);
}
@@ -115,31 +117,31 @@ public class ModifyAwareList<E> implements List<E>, ModifyAwareOwner {
@Override
public boolean addAll(Collection<? extends E> c) {
owner.markAsModified();
markAsDirty();
return list.addAll(c);
}
@Override
public boolean addAll(int index, Collection<? extends E> c) {
owner.markAsModified();
markAsDirty();
return list.addAll(index, c);
}
@Override
public boolean removeAll(Collection<?> c) {
owner.markAsModified();
markAsDirty();
return list.removeAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
owner.markAsModified();
markAsDirty();
return list.retainAll(c);
}
@Override
public void clear() {
owner.markAsModified();
markAsDirty();
list.clear();
}
@@ -150,19 +152,19 @@ public class ModifyAwareList<E> implements List<E>, ModifyAwareOwner {
@Override
public E set(int index, E element) {
owner.markAsModified();
markAsDirty();
return list.set(index, element);
}
@Override
public void add(int index, E element) {
owner.markAsModified();
markAsDirty();
list.add(index, element);
}
@Override
public E remove(int index) {
owner.markAsModified();
markAsDirty();
return list.remove(index);
}
@@ -195,7 +197,6 @@ public class ModifyAwareList<E> implements List<E>, ModifyAwareOwner {
* Create an return a modify aware Set.
*/
public ModifyAwareSet<E> asSet() {
return new ModifyAwareSet<>(owner, new LinkedHashSet<>(list));
}
}
@@ -1,5 +1,7 @@
package io.ebeaninternal.json;
import io.ebean.ModifyAwareType;
import java.util.ListIterator;
/**
@@ -7,11 +9,11 @@ import java.util.ListIterator;
*/
public class ModifyAwareListIterator<E> implements ListIterator<E> {
final ModifyAwareOwner owner;
final ModifyAwareType owner;
final ListIterator<E> iterator;
public ModifyAwareListIterator(ModifyAwareOwner owner, ListIterator<E> iterator) {
public ModifyAwareListIterator(ModifyAwareType owner, ListIterator<E> iterator) {
this.owner = owner;
this.iterator = iterator;
}
@@ -48,19 +50,19 @@ public class ModifyAwareListIterator<E> implements ListIterator<E> {
@Override
public void remove() {
owner.markAsModified();
owner.setMarkedDirty(true);
iterator.remove();
}
@Override
public void set(E e) {
owner.markAsModified();
owner.setMarkedDirty(true);
iterator.set(e);
}
@Override
public void add(E e) {
owner.markAsModified();
owner.setMarkedDirty(true);
iterator.add(e);
}
}
@@ -1,5 +1,8 @@
package io.ebeaninternal.json;
import io.ebean.ModifyAwareType;
import java.io.Serializable;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Map;
@@ -9,11 +12,11 @@ import java.util.Set;
/**
* Map that is wraps an underlying map for the purpose of detecting changes.
*/
public class ModifyAwareMap<K, V> implements Map<K, V>, ModifyAwareOwner {
public class ModifyAwareMap<K, V> implements Map<K, V>, ModifyAwareType, Serializable {
private static final long serialVersionUID = 1;
final ModifyAwareOwner owner;
final ModifyAwareType owner;
/**
* The underlying map.
@@ -25,7 +28,7 @@ public class ModifyAwareMap<K, V> implements Map<K, V>, ModifyAwareOwner {
this.owner = new ModifyAwareFlag();
}
public ModifyAwareMap(ModifyAwareOwner owner, Map<K, V> underlying) {
public ModifyAwareMap(ModifyAwareType owner, Map<K, V> underlying) {
this.owner = owner;
this.map = underlying;
}
@@ -58,13 +61,12 @@ public class ModifyAwareMap<K, V> implements Map<K, V>, ModifyAwareOwner {
}
@Override
public void markAsModified() {
owner.markAsModified();
public void setMarkedDirty(boolean markedDirty) {
owner.setMarkedDirty(markedDirty);
}
@Override
public void resetMarkedDirty() {
owner.resetMarkedDirty();
private void markAsDirty() {
owner.setMarkedDirty(true);
}
@Override
@@ -94,7 +96,7 @@ public class ModifyAwareMap<K, V> implements Map<K, V>, ModifyAwareOwner {
@Override
public V put(K key, V value) {
markAsModified();
markAsDirty();
return map.put(key, value);
}
@@ -102,14 +104,14 @@ public class ModifyAwareMap<K, V> implements Map<K, V>, ModifyAwareOwner {
public V remove(Object key) {
V value = map.remove(key);
if (value != null) {
markAsModified();
markAsDirty();
}
return value;
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
markAsModified();
markAsDirty();
map.putAll(m);
}
@@ -117,7 +119,7 @@ public class ModifyAwareMap<K, V> implements Map<K, V>, ModifyAwareOwner {
@Override
public void clear() {
if (!map.isEmpty()) {
markAsModified();
markAsDirty();
}
map.clear();
}
@@ -1,5 +1,8 @@
package io.ebeaninternal.json;
import io.ebean.ModifyAwareType;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.Objects;
@@ -8,11 +11,11 @@ import java.util.Set;
/**
* Wraps a Set for the purposes of detecting modifications.
*/
public class ModifyAwareSet<E> implements Set<E>, ModifyAwareOwner {
public class ModifyAwareSet<E> implements Set<E>, ModifyAwareType, Serializable {
private static final long serialVersionUID = 1;
protected final ModifyAwareOwner owner;
protected final ModifyAwareType owner;
protected final Set<E> set;
@@ -26,7 +29,7 @@ public class ModifyAwareSet<E> implements Set<E>, ModifyAwareOwner {
/**
* Create with an Owner that is notified of modifications.
*/
public ModifyAwareSet(ModifyAwareOwner owner, Set<E> underlying) {
public ModifyAwareSet(ModifyAwareType owner, Set<E> underlying) {
this.owner = owner;
this.set = underlying;
}
@@ -37,16 +40,14 @@ public class ModifyAwareSet<E> implements Set<E>, ModifyAwareOwner {
}
@Override
public void markAsModified() {
owner.markAsModified();
public void setMarkedDirty(boolean markedDirty) {
owner.setMarkedDirty(markedDirty);
}
@Override
public void resetMarkedDirty() {
owner.resetMarkedDirty();
private void markAsDirty() {
owner.setMarkedDirty(true);
}
@Override
public String toString() {
return set.toString();
@@ -72,7 +73,7 @@ public class ModifyAwareSet<E> implements Set<E>, ModifyAwareOwner {
@Override
public boolean add(E o) {
if (set.add(o)) {
owner.markAsModified();
markAsDirty();
return true;
}
return false;
@@ -83,7 +84,7 @@ public class ModifyAwareSet<E> implements Set<E>, ModifyAwareOwner {
boolean changed = false;
for (E o : collection) {
if (set.add(o)) {
owner.markAsModified();
markAsDirty();
changed = true;
}
}
@@ -93,7 +94,7 @@ public class ModifyAwareSet<E> implements Set<E>, ModifyAwareOwner {
@Override
public void clear() {
if (!set.isEmpty()) {
owner.markAsModified();
markAsDirty();
}
set.clear();
}
@@ -121,7 +122,7 @@ public class ModifyAwareSet<E> implements Set<E>, ModifyAwareOwner {
@Override
public boolean remove(Object o) {
if (set.remove(o)) {
owner.markAsModified();
markAsDirty();
return true;
}
return false;
@@ -132,7 +133,7 @@ public class ModifyAwareSet<E> implements Set<E>, ModifyAwareOwner {
boolean changed = false;
for (Object element : collection) {
if (set.remove(element)) {
owner.markAsModified();
markAsDirty();
changed = true;
}
}
@@ -147,7 +148,7 @@ public class ModifyAwareSet<E> implements Set<E>, ModifyAwareOwner {
Object o = it.next();
if (!collection.contains(o)) {
it.remove();
owner.markAsModified();
markAsDirty();
changed = true;
}
}
@@ -0,0 +1,26 @@
package io.ebeaninternal.server.type;
import io.ebean.ModifyAwareType;
/**
* Check dirty state of json value which might be modify aware.
*/
class CheckMarkedDirty {
/**
* Return true if the value should be considered dirty (and included in an update).
*/
static boolean isDirty(Object value) {
if (value instanceof ModifyAwareType) {
ModifyAwareType modifyAware = (ModifyAwareType) value;
if (modifyAware.isMarkedDirty()) {
// reset the dirty state (consider not dirty after update)
modifyAware.setMarkedDirty(false);
return true;
} else {
return false;
}
}
return true;
}
}
@@ -2,7 +2,6 @@ package io.ebeaninternal.server.type;
import io.ebean.core.type.DataBinder;
import io.ebean.core.type.DocPropertyType;
import io.ebeaninternal.json.ModifyAwareOwner;
import java.io.DataInput;
import java.io.DataOutput;
@@ -62,7 +61,7 @@ abstract class ScalarTypeJsonCollection<T> extends ScalarTypeBase<T> implements
*/
@Override
public boolean isDirty(Object value) {
return !(value instanceof ModifyAwareOwner) || ((ModifyAwareOwner) value).isMarkedDirty();
return CheckMarkedDirty.isDirty(value);
}
@Override
@@ -8,7 +8,6 @@ import io.ebean.core.type.DataReader;
import io.ebean.core.type.DocPropertyType;
import io.ebean.text.TextException;
import io.ebean.text.json.EJson;
import io.ebeaninternal.json.ModifyAwareOwner;
import java.io.DataInput;
import java.io.DataOutput;
@@ -124,7 +123,7 @@ public abstract class ScalarTypeJsonMap extends ScalarTypeBase<Map> {
*/
@Override
public boolean isDirty(Object value) {
return !(value instanceof ModifyAwareOwner) || ((ModifyAwareOwner) value).isMarkedDirty();
return CheckMarkedDirty.isDirty(value);
}
@Override
@@ -19,7 +19,6 @@ import io.ebean.core.type.ScalarType;
import io.ebean.text.TextException;
import io.ebeaninternal.json.ModifyAwareList;
import io.ebeaninternal.json.ModifyAwareMap;
import io.ebeaninternal.json.ModifyAwareOwner;
import io.ebeaninternal.json.ModifyAwareSet;
import javax.persistence.PersistenceException;
@@ -200,7 +199,7 @@ public class ScalarTypeJsonObjectMapper {
*/
@Override
public boolean isDirty(Object value) {
return !(value instanceof ModifyAwareOwner) || ((ModifyAwareOwner) value).isMarkedDirty();
return CheckMarkedDirty.isDirty(value);
}
@Override
@@ -9,7 +9,6 @@ import io.ebean.core.type.DocPropertyType;
import io.ebean.text.TextException;
import io.ebean.text.json.EJson;
import io.ebeaninternal.json.ModifyAwareMap;
import io.ebeaninternal.json.ModifyAwareOwner;
import java.io.DataInput;
import java.io.DataOutput;
@@ -34,7 +33,7 @@ public class ScalarTypePostgresHstore extends ScalarTypeBase<Map> {
@Override
public boolean isDirty(Object value) {
return !(value instanceof ModifyAwareOwner) || ((ModifyAwareOwner) value).isMarkedDirty();
return CheckMarkedDirty.isDirty(value);
}
@SuppressWarnings("unchecked")
@@ -2,7 +2,7 @@ package io.ebean.json;
import io.ebean.text.json.EJson;
import io.ebeaninternal.json.ModifyAwareMap;
import io.ebeaninternal.json.ModifyAwareOwner;
import io.ebean.ModifyAwareType;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import org.junit.Test;
@@ -293,7 +293,7 @@ public class EJsonTests {
Set set = EJson.parseSet(jsonInput, true);
ModifyAwareOwner modAware = (ModifyAwareOwner) set;
ModifyAwareType modAware = (ModifyAwareType) set;
assertFalse(modAware.isMarkedDirty());
Iterator iterator = set.iterator();
@@ -59,7 +59,7 @@ public class ModifyAwareMapTest {
ModifyAwareMap<String, String> map = createMap();
assertFalse(map.isMarkedDirty());
map.markAsModified();
map.setMarkedDirty(true);
Assert.assertTrue(map.isMarkedDirty());
}
@@ -20,7 +20,7 @@ public class ModifyAwareFlagTest {
ObjectOutputStream oos = new ObjectOutputStream(os);
ModifyAwareFlag flag = new ModifyAwareFlag();
flag.markAsModified();
flag.setMarkedDirty(true);
oos.writeObject(flag);
oos.flush();
oos.close();
@@ -0,0 +1,76 @@
package org.tests.json;
import io.ebean.BaseTestCase;
import io.ebean.DB;
import io.ebeantest.LoggedSql;
import org.junit.Test;
import org.tests.model.json.EBasicJsonJackson3;
import org.tests.model.json.EBasicJsonList;
import org.tests.model.json.PlainBean;
import org.tests.model.json.PlainBeanDirtyAware;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class TestDbJson_Jackson3 extends BaseTestCase {
@Test
public void updateIncludesJsonColumn_when_explicit_isMarkedDirty() {
PlainBeanDirtyAware contentBean = new PlainBeanDirtyAware("a", 42);
EBasicJsonJackson3 bean = new EBasicJsonJackson3();
bean.setName("b1");
bean.setPlainValue(contentBean);
bean.save();
final EBasicJsonJackson3 found = DB.find(EBasicJsonJackson3.class, bean.getId());
found.setName("b1-mod");
LoggedSql.start();
found.save();
List<String> sql = LoggedSql.collect();
assertThat(sql).hasSize(1);
assertThat(sql.get(0)).contains("update ebasic_json_jackson3 set name=?, version=? where id=? and version=?");
found.setName("b1-mod2");
found.getPlainValue().setName("b");
found.getPlainValue().setMarkedDirty(true);
found.save();
sql = LoggedSql.stop();
assertThat(sql).hasSize(1);
assertThat(sql.get(0)).contains("update ebasic_json_jackson3 set name=?, plain_value=?, version=? where id=? and version=?");
final EBasicJsonJackson3 found2 = DB.find(EBasicJsonJackson3.class, bean.getId());
assertThat(found2.getName()).isEqualTo("b1-mod2");
assertThat(found2.getPlainValue().getName()).isEqualTo("b");
}
@Test
public void updateIncludesJsonColumn_when_loadedAndNotDirtyAware() {
PlainBean contentBean = new PlainBean("a", 42);
EBasicJsonList bean = new EBasicJsonList();
bean.setName("p1");
bean.setPlainBean(contentBean);
DB.save(bean);
final EBasicJsonList found = DB.find(EBasicJsonList.class, bean.getId());
// json bean not modified but not aware
// ideally don't load the json content if we are not going to modify it
found.setName("p1-mod");
LoggedSql.start();
DB.save(found);
final List<String> sql = LoggedSql.stop();
assertThat(sql).hasSize(1);
assertThat(sql.get(0)).contains("update ebasic_json_list set name=?, bean_set=?, bean_list=?, plain_bean=?, version=? where id=?");
}
}
@@ -0,0 +1,55 @@
package org.tests.model.json;
import io.ebean.Model;
import io.ebean.annotation.DbJson;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Version;
@Entity
public class EBasicJsonJackson3 extends Model {
@Id
Long id;
String name;
@DbJson(length = 500)
PlainBeanDirtyAware plainValue;
@Version
long version;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public PlainBeanDirtyAware getPlainValue() {
return plainValue;
}
public void setPlainValue(PlainBeanDirtyAware plainValue) {
this.plainValue = plainValue;
}
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
}
@@ -0,0 +1,71 @@
package org.tests.model.json;
import io.ebean.ModifyAwareType;
import java.sql.Timestamp;
/**
* Something for Jackson ObjectMapper to marshall that is ModifyAwareType.
*/
public class PlainBeanDirtyAware implements ModifyAwareType {
private transient boolean markedDirty;
String name;
long along;
Timestamp timestamp;
public PlainBeanDirtyAware(String name, long along) {
this.name = name;
this.along = along;
this.timestamp = new Timestamp(System.currentTimeMillis());
}
/**
* A constructor for Jackson.
*/
public PlainBeanDirtyAware() {
}
// @JsonIgnore
@Override
public boolean isMarkedDirty() {
return markedDirty;
}
@Override
public void setMarkedDirty(boolean markedDirty) {
this.markedDirty = markedDirty;
}
@Override
public String toString() {
return "name:" + name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getAlong() {
return along;
}
public void setAlong(long along) {
this.along = along;
}
public Timestamp getTimestamp() {
return timestamp;
}
public void setTimestamp(Timestamp timestamp) {
this.timestamp = timestamp;
}
}