From 64e227117bb720bbd700496ba515c681952e4bf6 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Fri, 12 Jan 2024 16:14:08 +1300 Subject: [PATCH] Add support for Java 21 SequencedSet and SequencedMap Such that these can be used in place of Set and Map if desired. --- ebean-api/pom.xml | 47 ++ .../main/java/io/ebean/common/BeanMap.java | 10 +- .../main/java/io/ebean/common/BeanSet.java | 10 +- .../main/java21/io/ebean/common/BeanMap.java | 445 +++++++++++++++++ .../main/java21/io/ebean/common/BeanSet.java | 456 ++++++++++++++++++ .../io/ebean/bean/ToStringBuilderTest.java | 6 +- .../server/deploy/BeanListHelp.java | 7 - .../server/deploy/BeanMapHelp.java | 5 +- .../server/deploy/BeanSetHelp.java | 10 - .../server/deploy/ElementHelpMap.java | 3 +- .../server/deploy/ElementHelpSet.java | 3 +- .../deploy/parse/DeployCreateProperties.java | 2 +- .../deploy/parse/DetermineManyType.java | 6 +- .../io/ebean/xtest/common/BeanMapTest.java | 8 +- .../io/ebean/xtest/common/BeanSetTest.java | 8 +- pom.xml | 2 +- tests/pom.xml | 4 +- tests/test-java16/pom.xml | 4 +- .../java/org/example/records/HiBasic.java | 47 ++ .../main/java/org/example/records/HiMap.java | 49 ++ .../main/java/org/example/records/HiSeq.java | 56 +++ .../java/org/example/records/HiBasicTest.java | 37 ++ 22 files changed, 1170 insertions(+), 55 deletions(-) create mode 100644 ebean-api/src/main/java21/io/ebean/common/BeanMap.java create mode 100644 ebean-api/src/main/java21/io/ebean/common/BeanSet.java create mode 100644 tests/test-java16/src/main/java/org/example/records/HiBasic.java create mode 100644 tests/test-java16/src/main/java/org/example/records/HiMap.java create mode 100644 tests/test-java16/src/main/java/org/example/records/HiSeq.java create mode 100644 tests/test-java16/src/test/java/org/example/records/HiBasicTest.java diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml index dd6a63d34..a0c8caba7 100644 --- a/ebean-api/pom.xml +++ b/ebean-api/pom.xml @@ -126,6 +126,53 @@ + + + org.apache.maven.plugins + maven-compiler-plugin + + + compile + + compile + + + 11 + + + + compile-21 + compile + + compile + + + 21 + + ${project.basedir}/src/main/java21 + + true + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + false + + true + + + + + + + + + diff --git a/ebean-api/src/main/java/io/ebean/common/BeanMap.java b/ebean-api/src/main/java/io/ebean/common/BeanMap.java index fe959c22c..796d469be 100644 --- a/ebean-api/src/main/java/io/ebean/common/BeanMap.java +++ b/ebean-api/src/main/java/io/ebean/common/BeanMap.java @@ -17,12 +17,12 @@ public final class BeanMap extends AbstractBeanCollection implements Ma /** * The underlying map implementation. */ - private Map map; + private LinkedHashMap map; /** * Create with a given Map. */ - public BeanMap(Map map) { + public BeanMap(LinkedHashMap map) { this.map = map; } @@ -164,14 +164,14 @@ public final class BeanMap extends AbstractBeanCollection implements Ma * Set the actual underlying map. Used for performing lazy fetch. */ @SuppressWarnings("unchecked") - public void setActualMap(Map map) { - this.map = (Map) map; + public void setActualMap(LinkedHashMap map) { + this.map = (LinkedHashMap) map; } /** * Return the actual underlying map. */ - public Map actualMap() { + public LinkedHashMap actualMap() { return map; } diff --git a/ebean-api/src/main/java/io/ebean/common/BeanSet.java b/ebean-api/src/main/java/io/ebean/common/BeanSet.java index dda2b24ee..bb4eaf7bc 100644 --- a/ebean-api/src/main/java/io/ebean/common/BeanSet.java +++ b/ebean-api/src/main/java/io/ebean/common/BeanSet.java @@ -18,12 +18,12 @@ public final class BeanSet extends AbstractBeanCollection implements Set set; + private LinkedHashSet set; /** * Create with a specific Set implementation. */ - public BeanSet(Set set) { + public BeanSet(LinkedHashSet set) { this.set = set; } @@ -148,14 +148,14 @@ public final class BeanSet extends AbstractBeanCollection implements Set set) { - this.set = (Set) set; + public void setActualSet(LinkedHashSet set) { + this.set = (LinkedHashSet) set; } /** * Return the actual underlying set. */ - public Set actualSet() { + public LinkedHashSet actualSet() { return set; } diff --git a/ebean-api/src/main/java21/io/ebean/common/BeanMap.java b/ebean-api/src/main/java21/io/ebean/common/BeanMap.java new file mode 100644 index 000000000..e8fdc065f --- /dev/null +++ b/ebean-api/src/main/java21/io/ebean/common/BeanMap.java @@ -0,0 +1,445 @@ +package io.ebean.common; + +import io.ebean.bean.BeanCollection; +import io.ebean.bean.BeanCollectionLoader; +import io.ebean.bean.EntityBean; +import io.ebean.bean.ToStringBuilder; + +import java.util.*; + +/** + * Map capable of lazy loading and modification aware. + */ +public final class BeanMap extends AbstractBeanCollection implements SequencedMap { + + private static final long serialVersionUID = 1L; + + /** + * The underlying map implementation. + */ + private LinkedHashMap map; + + /** + * Create with a given Map. + */ + public BeanMap(LinkedHashMap map) { + this.map = map; + } + + /** + * Create using a underlying LinkedHashMap. + */ + public BeanMap() { + this(new LinkedHashMap<>()); + } + + public BeanMap(BeanCollectionLoader ebeanServer, EntityBean ownerBean, String propertyName) { + super(ebeanServer, ownerBean, propertyName); + } + + @Override + public void toString(ToStringBuilder builder) { + if (map == null || map.isEmpty()) { + builder.addRaw("{}"); + } else { + builder.addRaw("{"); + for (Entry entry : map.entrySet()) { + builder.add(String.valueOf(entry.getKey()), entry.getValue()); + } + builder.addRaw("}"); + } + } + + @Override + public void reset(EntityBean ownerBean, String propertyName) { + this.ownerBean = ownerBean; + this.propertyName = propertyName; + this.map = null; + } + + @Override + public boolean isSkipSave() { + return map == null || (map.isEmpty() && !holdsModifications()); + } + + @Override + @SuppressWarnings("unchecked") + public void loadFrom(BeanCollection other) { + BeanMap otherMap = (BeanMap) other; + internalPutNull(); + map.putAll(otherMap.actualMap()); + } + + public void internalPutNull() { + if (map == null) { + map = new LinkedHashMap<>(); + } + } + + @SuppressWarnings("unchecked") + public void internalPut(Object key, Object bean) { + if (map == null) { + map = new LinkedHashMap<>(); + } + if (key != null) { + map.put((K) key, (E) bean); + } + } + + public void internalPutWithCheck(Object key, Object bean) { + if (map == null || key == null || !map.containsKey(key)) { + internalPut(key, bean); + } + } + + @Override + public void internalAddWithCheck(Object bean) { + throw new RuntimeException("Not allowed for map"); + } + + @Override + public void internalAdd(Object bean) { + throw new RuntimeException("Not allowed for map"); + } + + /** + * Return true if the underlying map has been populated. Returns false if it + * has a deferred fetch pending. + */ + @Override + public boolean isPopulated() { + return map != null; + } + + /** + * Return true if this is a reference (lazy loading) bean collection. This is + * the same as !isPopulated(); + */ + @Override + public boolean isReference() { + return map == null; + } + + @Override + public boolean checkEmptyLazyLoad() { + if (map == null) { + map = new LinkedHashMap<>(); + return true; + } else { + return false; + } + } + + private void initClear() { + lock.lock(); + try { + if (map == null) { + if (!disableLazyLoad && modifyListening) { + lazyLoadCollection(true); + } else { + map = new LinkedHashMap<>(); + } + } + } finally { + lock.unlock(); + } + } + + private void init() { + lock.lock(); + try { + if (map == null) { + if (disableLazyLoad) { + map = new LinkedHashMap<>(); + } else { + lazyLoadCollection(false); + } + } + } finally { + lock.unlock(); + } + } + + /** + * Set the actual underlying map. Used for performing lazy fetch. + */ + @SuppressWarnings("unchecked") + public void setActualMap(LinkedHashMap map) { + this.map = (LinkedHashMap) map; + } + + /** + * Return the actual underlying map. + */ + public LinkedHashMap actualMap() { + return map; + } + + /** + * Returns the collection of beans (map values). + */ + @Override + public Collection actualDetails() { + return map.values(); + } + + /** + * Returns the map entrySet. + */ + @Override + public Collection actualEntries() { + return map.entrySet(); + } + + @Override + public String toString() { + if (map == null) { + return "BeanMap"; + } else { + return map.toString(); + } + } + + /** + * Equal if object is a Map and equal in a Map sense. + */ + @Override + public boolean equals(Object object) { + init(); + return map.equals(object); + } + + @Override + public int hashCode() { + init(); + return map.hashCode(); + } + + @Override + public void clear() { + checkReadOnly(); + initClear(); + if (modifyListening) { + // add all beans to the removal list + for (E bean : map.values()) { + modifyRemoval(bean); + } + } + map.clear(); + } + + @Override + public boolean containsKey(Object key) { + init(); + return map.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + init(); + return map.containsValue(value); + } + + @Override + public Set> entrySet() { + init(); + if (readOnly) { + return Collections.unmodifiableSet(map.entrySet()); + } + return modifyListening ? new ModifyEntrySet<>(this, map.entrySet()) : map.entrySet(); + } + + @Override + public E get(Object key) { + init(); + return map.get(key); + } + + @Override + public boolean isEmpty() { + init(); + return map.isEmpty(); + } + + @Override + public Set keySet() { + init(); + if (readOnly) { + return Collections.unmodifiableSet(map.keySet()); + } + return modifyListening ? new ModifyKeySet<>(this, map.keySet()) : map.keySet(); + } + + @Override + public E put(K key, E value) { + checkReadOnly(); + init(); + if (modifyListening) { + E oldBean = map.put(key, value); + if (value != oldBean) { + // register the add of the new and the removal of the old + modifyAddition(value); + modifyRemoval(oldBean); + } + return oldBean; + } else { + return map.put(key, value); + } + } + + @Override + public void putAll(Map puts) { + checkReadOnly(); + init(); + if (modifyListening) { + for (Entry entry : puts.entrySet()) { + Object oldBean = map.put(entry.getKey(), entry.getValue()); + if (entry.getValue() != oldBean) { + modifyAddition(entry.getValue()); + modifyRemoval(oldBean); + } + } + } else { + map.putAll(puts); + } + } + + @Override + public void addBean(E bean) { + throw new IllegalStateException("Method not allowed on Map. Please use List instead."); + } + + @Override + public void removeBean(E bean) { + throw new IllegalStateException("Method not allowed on Map. Please use List instead."); + } + + @Override + public E remove(Object key) { + checkReadOnly(); + init(); + if (modifyListening) { + E o = map.remove(key); + modifyRemoval(o); + return o; + } + return map.remove(key); + } + + @Override + public int size() { + init(); + return map.size(); + } + + @Override + public Collection values() { + init(); + if (readOnly) { + return Collections.unmodifiableCollection(map.values()); + } + return modifyListening ? new ModifyCollection<>(this, map.values()) : map.values(); + } + + @Override + public BeanCollection shallowCopy() { + BeanMap copy = new BeanMap<>(new LinkedHashMap<>(map)); + copy.setFromOriginal(this); + return copy; + } + + @Override + public SequencedMap reversed() { + init(); + if (modifyListening) { + throw new UnsupportedOperationException("Not supported on modify listening map"); + } + return map.reversed(); + } + + @Override + public Entry firstEntry() { + init(); + return map.firstEntry(); + } + + @Override + public Entry lastEntry() { + init(); + return map.lastEntry(); + } + + @Override + public Entry pollFirstEntry() { + checkReadOnly(); + init(); + var entry = map.pollFirstEntry(); + if (modifyListening && entry != null) { + modifyRemoval(entry.getValue()); + } + return entry; + } + + @Override + public Entry pollLastEntry() { + checkReadOnly(); + init(); + var entry = map.pollLastEntry(); + if (modifyListening && entry != null) { + modifyRemoval(entry.getValue()); + } + return entry; + } + + @Override + public E putFirst(K key, E value) { + checkReadOnly(); + init(); + if (modifyListening) { + E oldBean = map.putFirst(key, value); + if (value != oldBean) { + // register the add of the new and the removal of the old + modifyAddition(value); + modifyRemoval(oldBean); + } + return oldBean; + } else { + return map.putFirst(key, value); + } + } + + @Override + public E putLast(K key, E value) { + checkReadOnly(); + init(); + if (modifyListening) { + E oldBean = map.putLast(key, value); + if (value != oldBean) { + // register the add of the new and the removal of the old + modifyAddition(value); + modifyRemoval(oldBean); + } + return oldBean; + } else { + return map.putLast(key, value); + } + } + + @Override + public SequencedSet sequencedKeySet() { + init(); + return map.sequencedKeySet(); + } + + @Override + public SequencedCollection sequencedValues() { + init(); + return map.sequencedValues(); + } + + @Override + public SequencedSet> sequencedEntrySet() { + init(); + return map.sequencedEntrySet(); + } +} diff --git a/ebean-api/src/main/java21/io/ebean/common/BeanSet.java b/ebean-api/src/main/java21/io/ebean/common/BeanSet.java new file mode 100644 index 000000000..cf579c4eb --- /dev/null +++ b/ebean-api/src/main/java21/io/ebean/common/BeanSet.java @@ -0,0 +1,456 @@ +package io.ebean.common; + +import io.ebean.bean.*; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.SequencedSet; + +/** + * Set capable of lazy loading and modification aware. + */ +public final class BeanSet extends AbstractBeanCollection implements SequencedSet, BeanCollectionAdd { + + private static final long serialVersionUID = 1L; + + /** + * The underlying Set implementation. + */ + private LinkedHashSet set; + + /** + * Create with a specific Set implementation. + */ + public BeanSet(LinkedHashSet set) { + this.set = set; + } + + /** + * Create using an underlying LinkedHashSet. + */ + public BeanSet() { + this(new LinkedHashSet<>()); + } + + public BeanSet(BeanCollectionLoader loader, EntityBean ownerBean, String propertyName) { + super(loader, ownerBean, propertyName); + } + + @Override + public void toString(ToStringBuilder builder) { + builder.addCollection(set); + } + + @Override + public void reset(EntityBean ownerBean, String propertyName) { + this.ownerBean = ownerBean; + this.propertyName = propertyName; + this.set = null; + } + + @Override + public boolean isSkipSave() { + return set == null || (set.isEmpty() && !holdsModifications()); + } + + @Override + @SuppressWarnings("unchecked") + public void addEntityBean(EntityBean bean) { + set.add((E) bean); + } + + @Override + @SuppressWarnings("unchecked") + public void loadFrom(BeanCollection other) { + if (set == null) { + set = new LinkedHashSet<>(); + } + set.addAll((Collection) other.actualDetails()); + } + + @Override + public void internalAddWithCheck(Object bean) { + // set add() already de-dups so just add it + internalAdd(bean); + } + + @Override + @SuppressWarnings("unchecked") + public void internalAdd(Object bean) { + if (set == null) { + set = new LinkedHashSet<>(); + } + if (bean != null) { + set.add((E) bean); + } + } + + /** + * Returns true if the underlying set has its data. + */ + @Override + public boolean isPopulated() { + return set != null; + } + + /** + * Return true if this is a reference (lazy loading) bean collection. This is + * the same as !isPopulated(); + */ + @Override + public boolean isReference() { + return set == null; + } + + @Override + public boolean checkEmptyLazyLoad() { + if (set == null) { + set = new LinkedHashSet<>(); + return true; + } else { + return false; + } + } + + private void initClear() { + lock.lock(); + try { + if (set == null) { + if (!disableLazyLoad && modifyListening) { + lazyLoadCollection(false); + } else { + set = new LinkedHashSet<>(); + } + } + } finally { + lock.unlock(); + } + } + + private void init() { + lock.lock(); + try { + if (set == null) { + if (disableLazyLoad) { + set = new LinkedHashSet<>(); + } else { + lazyLoadCollection(false); + } + } + } finally { + lock.unlock(); + } + } + + /** + * Set the underlying set (used for lazy fetch). + */ + @SuppressWarnings("unchecked") + public void setActualSet(LinkedHashSet set) { + this.set = (LinkedHashSet) set; + } + + /** + * Return the actual underlying set. + */ + public LinkedHashSet actualSet() { + return set; + } + + @Override + public Collection actualDetails() { + return set; + } + + @Override + public Collection actualEntries() { + return set; + } + + @Override + public String toString() { + if (set == null) { + return "BeanSet"; + } else { + return set.toString(); + } + } + + /** + * Equal if obj is a Set and equal in a Set sense. + */ + @Override + public boolean equals(Object obj) { + init(); + return set.equals(obj); + } + + @Override + public int hashCode() { + init(); + return set.hashCode(); + } + + @Override + public void addBean(E bean) { + add(bean); + } + + @Override + public void removeBean(E bean) { + if (set.remove(bean)) { + getModifyHolder().modifyRemoval(bean); + } + } + + // -----------------------------------------------------// + // proxy method for map + // -----------------------------------------------------// + + @Override + public boolean add(E bean) { + checkReadOnly(); + init(); + if (modifyListening) { + if (set.add(bean)) { + modifyAddition(bean); + return true; + } else { + return false; + } + } + return set.add(bean); + } + + @Override + public boolean addAll(Collection beans) { + checkReadOnly(); + init(); + if (modifyListening) { + boolean changed = false; + for (E bean : beans) { + if (set.add(bean)) { + // register the addition of the bean + modifyAddition(bean); + changed = true; + } + } + return changed; + } + return set.addAll(beans); + } + + @Override + public void clear() { + checkReadOnly(); + initClear(); + if (modifyListening) { + for (E bean : set) { + modifyRemoval(bean); + } + } + set.clear(); + } + + @Override + public boolean contains(Object bean) { + init(); + return set.contains(bean); + } + + @Override + public boolean containsAll(Collection beans) { + init(); + return set.containsAll(beans); + } + + @Override + public boolean isEmpty() { + init(); + return set.isEmpty(); + } + + @Override + public Iterator iterator() { + init(); + if (readOnly) { + return new ReadOnlyIterator<>(set.iterator()); + } + if (modifyListening) { + return new ModifyIterator<>(this, set.iterator()); + } + return set.iterator(); + } + + @Override + public boolean remove(Object bean) { + checkReadOnly(); + init(); + if (modifyListening) { + if (set.remove(bean)) { + modifyRemoval(bean); + return true; + } + return false; + } + return set.remove(bean); + } + + @Override + public boolean removeAll(Collection beans) { + checkReadOnly(); + init(); + if (modifyListening) { + boolean changed = false; + for (Object bean : beans) { + if (set.remove(bean)) { + modifyRemoval(bean); + changed = true; + } + } + return changed; + } + return set.removeAll(beans); + } + + @Override + public boolean retainAll(Collection beans) { + checkReadOnly(); + init(); + if (modifyListening) { + boolean changed = false; + Iterator it = set.iterator(); + while (it.hasNext()) { + Object bean = it.next(); + if (!beans.contains(bean)) { + // not retaining this bean so add it to the removal list + it.remove(); + modifyRemoval(bean); + changed = true; + } + } + return changed; + } + return set.retainAll(beans); + } + + @Override + public int size() { + init(); + return set.size(); + } + + @Override + public Object[] toArray() { + init(); + return set.toArray(); + } + + @Override + public T[] toArray(T[] array) { + init(); + //noinspection SuspiciousToArrayCall + return set.toArray(array); + } + + + @Override + public SequencedSet reversed() { + init(); + if (modifyListening) { + throw new UnsupportedOperationException("Not supported on modify listening set"); + } + return set.reversed(); + } + + @Override + public void addFirst(E bean) { + checkReadOnly(); + init(); + if (modifyListening) { + modifyAddition(bean); + } + set.addFirst(bean); + } + + @Override + public void addLast(E bean) { + checkReadOnly(); + init(); + if (modifyListening) { + modifyAddition(bean); + } + set.addLast(bean); + } + + @Override + public E getFirst() { + init(); + return set.getFirst(); + } + + @Override + public E getLast() { + init(); + return set.getLast(); + } + + @Override + public E removeFirst() { + checkReadOnly(); + init(); + if (modifyListening) { + var bean = set.removeFirst(); + modifyRemoval(bean); + return bean; + } + return set.removeFirst(); + } + + @Override + public E removeLast() { + checkReadOnly(); + init(); + if (modifyListening) { + var bean = set.removeLast(); + modifyRemoval(bean); + return bean; + } + return set.removeLast(); + } + + private static final class ReadOnlyIterator implements Iterator, Serializable { + + private static final long serialVersionUID = 2577697326745352605L; + + private final Iterator it; + + ReadOnlyIterator(Iterator it) { + this.it = it; + } + + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public E next() { + return it.next(); + } + + @Override + public void remove() { + throw new IllegalStateException("This collection is in ReadOnly mode"); + } + } + + @Override + public BeanCollection shallowCopy() { + BeanSet copy = new BeanSet<>(new LinkedHashSet<>(set)); + copy.setFromOriginal(this); + return copy; + } +} diff --git a/ebean-api/src/test/java/io/ebean/bean/ToStringBuilderTest.java b/ebean-api/src/test/java/io/ebean/bean/ToStringBuilderTest.java index c049ec9b0..fad27d5d0 100644 --- a/ebean-api/src/test/java/io/ebean/bean/ToStringBuilderTest.java +++ b/ebean-api/src/test/java/io/ebean/bean/ToStringBuilderTest.java @@ -136,13 +136,13 @@ class ToStringBuilderTest { @Test void beanSet_null_empty() { assertThat(toStringFor(new BeanSet(null))).isEqualTo("[]"); - assertThat(toStringFor(new BeanSet(Collections.emptySet()))).isEqualTo("[]"); + assertThat(toStringFor(new BeanSet(new LinkedHashSet<>()))).isEqualTo("[]"); } @Test void beanMap_null_empty() { assertThat(toStringFor(new BeanMap(null))).isEqualTo("{}"); - assertThat(toStringFor(new BeanMap(Collections.emptyMap()))).isEqualTo("{}"); + assertThat(toStringFor(new BeanMap(new LinkedHashMap<>()))).isEqualTo("{}"); } @Test @@ -159,7 +159,7 @@ class ToStringBuilderTest { @Test void beanMap_some() { - Map under = new LinkedHashMap<>(); + var under = new LinkedHashMap(); under.put("a", new Recurse(1, "a")); under.put("b", new Recurse(2, "b")); BeanMap list = new BeanMap<>(under); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanListHelp.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanListHelp.java index dc4a8c520..ed069c9c2 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanListHelp.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanListHelp.java @@ -1,12 +1,9 @@ package io.ebeaninternal.server.deploy; -import io.ebean.Transaction; import io.ebean.bean.BeanCollection; import io.ebean.bean.BeanCollectionAdd; import io.ebean.bean.EntityBean; import io.ebean.common.BeanList; -import io.ebeaninternal.api.SpiEbeanServer; -import io.ebeaninternal.api.SpiQuery; import io.ebeaninternal.api.json.SpiJsonWriter; import java.io.IOException; @@ -23,10 +20,6 @@ public class BeanListHelp extends BaseCollectionHelp { super(many); } - BeanListHelp() { - super(); - } - @Override public final BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) { if (bc instanceof BeanList) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanMapHelp.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanMapHelp.java index 041a6ab7d..12748af70 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanMapHelp.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanMapHelp.java @@ -1,12 +1,9 @@ package io.ebeaninternal.server.deploy; -import io.ebean.Transaction; import io.ebean.bean.BeanCollection; import io.ebean.bean.BeanCollectionAdd; import io.ebean.bean.EntityBean; import io.ebean.common.BeanMap; -import io.ebeaninternal.api.SpiEbeanServer; -import io.ebeaninternal.api.SpiQuery; import io.ebeaninternal.api.json.SpiJsonWriter; import java.io.IOException; @@ -44,7 +41,7 @@ public class BeanMapHelp extends BaseCollectionHelp { BeanProperty beanProp = targetDescriptor.beanProperty(mapKey); if (bc instanceof BeanMap) { BeanMap bm = (BeanMap) bc; - Map actualMap = bm.actualMap(); + var actualMap = bm.actualMap(); if (actualMap == null) { actualMap = new LinkedHashMap<>(); bm.setActualMap(actualMap); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanSetHelp.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanSetHelp.java index da390bf17..35d7797c4 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanSetHelp.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanSetHelp.java @@ -1,12 +1,9 @@ package io.ebeaninternal.server.deploy; -import io.ebean.Transaction; import io.ebean.bean.BeanCollection; import io.ebean.bean.BeanCollectionAdd; import io.ebean.bean.EntityBean; import io.ebean.common.BeanSet; -import io.ebeaninternal.api.SpiEbeanServer; -import io.ebeaninternal.api.SpiQuery; import io.ebeaninternal.api.json.SpiJsonWriter; import java.io.IOException; @@ -26,13 +23,6 @@ public class BeanSetHelp extends BaseCollectionHelp { super(many); } - /** - * For a query that returns a set. - */ - BeanSetHelp() { - super(); - } - @Override public final BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) { if (bc instanceof BeanSet) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/ElementHelpMap.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/ElementHelpMap.java index e00d2ef2f..0cb9b8437 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/ElementHelpMap.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/ElementHelpMap.java @@ -4,7 +4,6 @@ import io.ebean.bean.BeanCollection; import io.ebean.common.BeanMap; import java.util.LinkedHashMap; -import java.util.Map; final class ElementHelpMap implements ElementHelp { @@ -15,7 +14,7 @@ final class ElementHelpMap implements ElementHelp { private static class Collector implements ElementCollector { - private final Map map = new LinkedHashMap<>(); + private final LinkedHashMap map = new LinkedHashMap<>(); @Override public void addElement(Object element) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/ElementHelpSet.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/ElementHelpSet.java index e15e74e28..c944c6351 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/ElementHelpSet.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/ElementHelpSet.java @@ -4,7 +4,6 @@ import io.ebean.bean.BeanCollection; import io.ebean.common.BeanSet; import java.util.LinkedHashSet; -import java.util.Set; final class ElementHelpSet implements ElementHelp { @@ -15,7 +14,7 @@ final class ElementHelpSet implements ElementHelp { private static class Collector implements ElementCollector { - private final Set set = new LinkedHashSet<>(); + private final LinkedHashSet set = new LinkedHashSet<>(); @Override public void addElement(Object element) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployCreateProperties.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployCreateProperties.java index 4e887bbbc..75f7bb4d8 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployCreateProperties.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployCreateProperties.java @@ -122,7 +122,7 @@ public final class DeployCreateProperties { return new DeployBeanProperty(desc, propertyType, field.getGenericType()); } // check for Collection type (list, set or map) - ManyType manyType = determineManyType.getManyType(propertyType); + ManyType manyType = determineManyType.manyType(propertyType); if (manyType != null) { // List, Set or Map based object Class targetType = determineTargetType(field); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DetermineManyType.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DetermineManyType.java index 40fc6016b..2e5304fb1 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DetermineManyType.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DetermineManyType.java @@ -11,14 +11,14 @@ import java.util.Set; */ final class DetermineManyType { - ManyType getManyType(Class type) { + ManyType manyType(Class type) { if (type.equals(List.class)) { return ManyType.LIST; } - if (type.equals(Set.class)) { + if (type.equals(Set.class) || type.getCanonicalName().equals("java.util.SequencedSet")) { return ManyType.SET; } - if (type.equals(Map.class)) { + if (type.equals(Map.class) || type.getCanonicalName().equals("java.util.SequencedMap")) { return ManyType.MAP; } return null; diff --git a/ebean-test/src/test/java/io/ebean/xtest/common/BeanMapTest.java b/ebean-test/src/test/java/io/ebean/xtest/common/BeanMapTest.java index 4b6a48c76..910d96661 100644 --- a/ebean-test/src/test/java/io/ebean/xtest/common/BeanMapTest.java +++ b/ebean-test/src/test/java/io/ebean/xtest/common/BeanMapTest.java @@ -20,16 +20,16 @@ public class BeanMapTest { private final EBasic object4 = new EBasic("o4"); private final EBasic object5 = new EBasic("o5"); - private Map all() { - Map all = new LinkedHashMap<>(); + private LinkedHashMap all() { + LinkedHashMap all = new LinkedHashMap<>(); all.put("1", object1); all.put("2", object2); all.put("3", object3); return all; } - private Map some() { - Map all = new LinkedHashMap<>(); + private LinkedHashMap some() { + LinkedHashMap all = new LinkedHashMap<>(); all.put("2", object2); all.put("3", object3); return all; diff --git a/ebean-test/src/test/java/io/ebean/xtest/common/BeanSetTest.java b/ebean-test/src/test/java/io/ebean/xtest/common/BeanSetTest.java index 5053f71dc..ca265affd 100644 --- a/ebean-test/src/test/java/io/ebean/xtest/common/BeanSetTest.java +++ b/ebean-test/src/test/java/io/ebean/xtest/common/BeanSetTest.java @@ -16,16 +16,16 @@ public class BeanSetTest { Object object2 = new Object(); Object object3 = new Object(); - private Set all() { - Set all = new LinkedHashSet<>(); + private LinkedHashSet all() { + var all = new LinkedHashSet<>(); all.add(object1); all.add(object2); all.add(object3); return all; } - private Set some() { - Set some = new LinkedHashSet<>(); + private LinkedHashSet some() { + var some = new LinkedHashSet<>(); some.add(object2); some.add(object3); return some; diff --git a/pom.xml b/pom.xml index 8e3ed1d45..76650b581 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.avaje java11-oss - 3.12 + 4.0 io.ebean diff --git a/tests/pom.xml b/tests/pom.xml index 854ce738f..3bdd4862c 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -19,9 +19,9 @@ - jdk16plus + jdk21plus - [17,21] + 21 test-java16 diff --git a/tests/test-java16/pom.xml b/tests/test-java16/pom.xml index 61ba94a75..018d4ae4a 100644 --- a/tests/test-java16/pom.xml +++ b/tests/test-java16/pom.xml @@ -11,7 +11,7 @@ test-java16 - 16 + 21 @@ -52,7 +52,7 @@ true - io.ebean.tile:enhancement:13.22.0 + io.ebean.tile:enhancement:13.26.0 diff --git a/tests/test-java16/src/main/java/org/example/records/HiBasic.java b/tests/test-java16/src/main/java/org/example/records/HiBasic.java new file mode 100644 index 000000000..2d7d157fe --- /dev/null +++ b/tests/test-java16/src/main/java/org/example/records/HiBasic.java @@ -0,0 +1,47 @@ +package org.example.records; + +import jakarta.persistence.*; + +import java.util.SequencedMap; +import java.util.SequencedSet; + +@Entity +public class HiBasic { + + @Id + long id; + + @OneToMany(cascade = CascadeType.ALL) + SequencedSet seqs; + + @MapKey(name="key") + @OneToMany(cascade = CascadeType.ALL) + SequencedMap map; + + public long id() { + return id; + } + + public HiBasic setId(long id) { + this.id = id; + return this; + } + + public SequencedSet seqs() { + return seqs; + } + + public HiBasic setSeqs(SequencedSet seqs) { + this.seqs = seqs; + return this; + } + + public SequencedMap map() { + return map; + } + + public HiBasic setMap(SequencedMap map) { + this.map = map; + return this; + } +} diff --git a/tests/test-java16/src/main/java/org/example/records/HiMap.java b/tests/test-java16/src/main/java/org/example/records/HiMap.java new file mode 100644 index 000000000..4b3ed5ee9 --- /dev/null +++ b/tests/test-java16/src/main/java/org/example/records/HiMap.java @@ -0,0 +1,49 @@ +package org.example.records; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; + +@Entity +public class HiMap { + + @Id + private long id; + + @ManyToOne + private HiBasic parent; + + private final String key; + private final String val; + + public HiMap(String key, String val) { + this.key = key; + this.val = val; + } + + public long id() { + return id; + } + + public HiMap setId(long id) { + this.id = id; + return this; + } + + public HiBasic parent() { + return parent; + } + + public HiMap setParent(HiBasic parent) { + this.parent = parent; + return this; + } + + public String key() { + return key; + } + + public String val() { + return val; + } +} diff --git a/tests/test-java16/src/main/java/org/example/records/HiSeq.java b/tests/test-java16/src/main/java/org/example/records/HiSeq.java new file mode 100644 index 000000000..a2f679e1e --- /dev/null +++ b/tests/test-java16/src/main/java/org/example/records/HiSeq.java @@ -0,0 +1,56 @@ +package org.example.records; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; + +import java.util.SequencedSet; + +@Entity +public class HiSeq { + + @Id + private long id; + + @ManyToOne + private HiBasic parent; + + private final String name; + + private SequencedSet courses; + + public HiSeq(String name) { + this.name = name; + } + + public long id() { + return id; + } + + public HiSeq setId(long id) { + this.id = id; + return this; + } + + public HiBasic parent() { + return parent; + } + + public HiSeq setParent(HiBasic parent) { + this.parent = parent; + return this; + } + + public String name() { + return name; + } + + public SequencedSet courses() { + return courses; + } + + public HiSeq setCourses(SequencedSet courses) { + this.courses = courses; + return this; + } +} diff --git a/tests/test-java16/src/test/java/org/example/records/HiBasicTest.java b/tests/test-java16/src/test/java/org/example/records/HiBasicTest.java new file mode 100644 index 000000000..13391ecb8 --- /dev/null +++ b/tests/test-java16/src/test/java/org/example/records/HiBasicTest.java @@ -0,0 +1,37 @@ +package org.example.records; + +import io.ebean.DB; +import org.example.records.query.QHiBasic; +import org.junit.jupiter.api.Test; + +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +class HiBasicTest { + + @Test + void insert() { + + var bean = new HiBasic(); + var map = new LinkedHashMap(); + map.put("a", new HiMap("a", "b")); + bean.setMap(map); + bean.setSeqs(new LinkedHashSet<>(Set.of(new HiSeq("x")))); + + DB.save(bean); + + List list = new QHiBasic() + .seqs.fetch() + .map.fetch() + .findList(); + + assertThat(list).hasSize(1); + HiBasic hiBasic = list.get(0); + assertThat(hiBasic.seqs()).hasSize(1); + assertThat(hiBasic.map()).hasSize(1); + } +}