diff --git a/src/main/java/com/avaje/ebean/bean/BeanCollection.java b/src/main/java/com/avaje/ebean/bean/BeanCollection.java index a8955b341..2b7e83342 100644 --- a/src/main/java/com/avaje/ebean/bean/BeanCollection.java +++ b/src/main/java/com/avaje/ebean/bean/BeanCollection.java @@ -145,14 +145,19 @@ public interface BeanCollection extends Serializable { public boolean isEmpty(); /** - * Returns the underlying details as an iterator. - *

- * Note that for maps this returns the entrySet as we need the keys of the - * map. - *

+ * Returns the underlying collection of beans from the Set, Map or List. */ public Collection getActualDetails(); + /** + * Returns the underlying entries so for Maps this is a collection of + * Map.Entry. + *

+ * For maps this returns the entrySet as we need the keys of the map. + *

+ */ + public Collection getActualEntries(); + /** * Set to true if maxRows was hit and there are actually more rows available. *

diff --git a/src/main/java/com/avaje/ebean/common/BeanList.java b/src/main/java/com/avaje/ebean/common/BeanList.java index 8207eff8e..e605b1945 100644 --- a/src/main/java/com/avaje/ebean/common/BeanList.java +++ b/src/main/java/com/avaje/ebean/common/BeanList.java @@ -106,6 +106,11 @@ public final class BeanList extends AbstractBeanCollection implements List public Collection getActualDetails() { return list; } + + @Override + public Collection getActualEntries() { + return list; + } /** * Returns the underlying list. diff --git a/src/main/java/com/avaje/ebean/common/BeanMap.java b/src/main/java/com/avaje/ebean/common/BeanMap.java index ff3c3e6f2..fd7af32cf 100644 --- a/src/main/java/com/avaje/ebean/common/BeanMap.java +++ b/src/main/java/com/avaje/ebean/common/BeanMap.java @@ -104,14 +104,23 @@ public final class BeanMap extends AbstractBeanCollection implements Ma } /** - * Returns the map entrySet iterator. + * Returns the collection of beans (map values). + */ + public Collection getActualDetails() { + return map.values(); + } + + + /** + * Returns the map entrySet. *

* This is because the key values may need to be set against the details (so * they don't need to be set twice). *

*/ - public Collection getActualDetails() { - return map.values(); + public Collection getActualEntries() { + return map.entrySet(); + } /** diff --git a/src/main/java/com/avaje/ebean/common/BeanSet.java b/src/main/java/com/avaje/ebean/common/BeanSet.java index f3a41b4dc..fddcc3cc0 100644 --- a/src/main/java/com/avaje/ebean/common/BeanSet.java +++ b/src/main/java/com/avaje/ebean/common/BeanSet.java @@ -12,8 +12,7 @@ import com.avaje.ebean.bean.BeanCollectionLoader; /** * Set capable of lazy loading. */ -public final class BeanSet extends AbstractBeanCollection implements Set, - BeanCollectionAdd { +public final class BeanSet extends AbstractBeanCollection implements Set, BeanCollectionAdd { /** * The underlying Set implementation. @@ -113,6 +112,11 @@ public final class BeanSet extends AbstractBeanCollection implements Set getActualEntries() { + return set; + } + /** * Returns the underlying set. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java index d6632d05f..23eae6644 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java @@ -970,11 +970,14 @@ public class BeanDescriptor implements MetaBeanInfo { public void cachePutMany(BeanPropertyAssocMany many, BeanCollection bc, Object parentId) { BeanDescriptor targetDescriptor = many.getTargetDescriptor(); - Collection actualDetails = bc.getActualDetails(); + ArrayList idList = new ArrayList(); + + // get the underlying collection of beans (in the List, Set or Map) + Collection actualDetails = bc.getActualDetails(); for (Object bean : actualDetails) { - Object id = targetDescriptor.getId(bean); - idList.add(id); + // Collect the id values + idList.add(targetDescriptor.getId(bean)); } CachedManyIds ids = new CachedManyIds(idList); cachePutCachedManyIds(parentId, many.getName(), ids); diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/ManyType.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/ManyType.java index 59e375c8a..d1dd4843b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/ManyType.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/ManyType.java @@ -38,6 +38,10 @@ public class ManyType { } } + public boolean isMap() { + return Underlying.MAP.equals(underlying); + } + /** * Return the matching Query type. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java b/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java index 8e6f44896..52e434c00 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java +++ b/src/main/java/com/avaje/ebeaninternal/server/persist/DefaultPersister.java @@ -788,7 +788,8 @@ public final class DefaultPersister implements Persister { // check that the list is not null and if it is a BeanCollection // check that is has been populated (don't trigger lazy loading) - Collection collection = getDetailsIterator(details); + // For a Map this is a collection of Map.Entry objects and not beans + Collection collection = getActualEntries(details); if (collection == null) { // nothing to do here @@ -1262,7 +1263,7 @@ public final class DefaultPersister implements Persister { * Return the details of the collection or map taking care to avoid * unnecessary fetching of the data. */ - private Collection getDetailsIterator(Object o) { + private Collection getActualEntries(Object o) { if (o == null) { return null; } @@ -1271,7 +1272,9 @@ public final class DefaultPersister implements Persister { if (!bc.isPopulated()) { return null; } - return bc.getActualDetails(); + // For maps this is a collection of Map.Entry, otherwise it + // returns a collection of beans + return bc.getActualEntries(); } if (o instanceof Map) { @@ -1281,8 +1284,7 @@ public final class DefaultPersister implements Persister { } else if (o instanceof Collection) { return ((Collection) o); } - String m = "expecting a Map or Collection but got [" + o.getClass().getName() + "]"; - throw new PersistenceException(m); + throw new PersistenceException("expecting a Map or Collection but got [" + o.getClass().getName() + "]"); } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/DefaultOrmQueryEngine.java b/src/main/java/com/avaje/ebeaninternal/server/query/DefaultOrmQueryEngine.java index fa2d92708..aa684baf3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/DefaultOrmQueryEngine.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/DefaultOrmQueryEngine.java @@ -83,7 +83,7 @@ public class DefaultOrmQueryEngine implements OrmQueryEngine { Collection c = result.getActualDetails(); for (T bean : c) { descriptor.cachePutBeanData(bean); - } + } } if (!result.isEmpty() && query.isUseQueryCache()){ diff --git a/src/test/java/com/avaje/tests/model/map/MpRole.java b/src/test/java/com/avaje/tests/model/map/MpRole.java new file mode 100644 index 000000000..2c35064c2 --- /dev/null +++ b/src/test/java/com/avaje/tests/model/map/MpRole.java @@ -0,0 +1,28 @@ +package com.avaje.tests.model.map; + +import javax.persistence.*; + +@Entity +public class MpRole { + + @Id + private Long id; + + private Long organizationId; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getOrganizationId() { + return organizationId; + } + + public void setOrganizationId(Long organizationId) { + this.organizationId = organizationId; + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/tests/model/map/MpUser.java b/src/test/java/com/avaje/tests/model/map/MpUser.java new file mode 100644 index 000000000..48f9dc47e --- /dev/null +++ b/src/test/java/com/avaje/tests/model/map/MpUser.java @@ -0,0 +1,45 @@ +package com.avaje.tests.model.map; + +import java.util.HashMap; +import java.util.Map; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.*; + +@Entity +public class MpUser { + + @Id + private Long id; + + private String name; + + @OneToMany(cascade = CascadeType.ALL) + @MapKey(name = "id") + public Map roles = new HashMap(); + + 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 Map getRoles() { + return roles; + } + + public void setRoles(Map roles) { + this.roles = roles; + } +} diff --git a/src/test/java/com/avaje/tests/query/other/TestOneToManyAsMap.java b/src/test/java/com/avaje/tests/query/other/TestOneToManyAsMap.java new file mode 100644 index 000000000..8d86ee380 --- /dev/null +++ b/src/test/java/com/avaje/tests/query/other/TestOneToManyAsMap.java @@ -0,0 +1,42 @@ +package com.avaje.tests.query.other; + +import java.util.Map; + +import junit.framework.Assert; + +import org.junit.Test; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.EbeanServer; +import com.avaje.tests.model.map.MpRole; +import com.avaje.tests.model.map.MpUser; + +public class TestOneToManyAsMap extends BaseTestCase { + + @Test + public void test() { + + EbeanServer eServer = Ebean.getServer(null); + + MpUser u = new MpUser(); + eServer.save(u); + + MpUser u2 = eServer.find(MpUser.class, u.getId()); + Assert.assertNotNull(u2); + + u2.setName("Charlie Brown"); + MpRole ourl = new MpRole(); + ourl.setOrganizationId(47L); + u2.getRoles().put(ourl.getOrganizationId(), ourl); + eServer.save(u2); + + MpUser u3 = eServer.find(MpUser.class, u.getId()); + Assert.assertEquals("Charlie Brown", u3.getName()); + + Map listMap = u3.getRoles(); + Assert.assertEquals(1, listMap.size()); + + } + +} diff --git a/src/test/resources/ebean.properties b/src/test/resources/ebean.properties index 0af68ce63..0f731e463 100644 --- a/src/test/resources/ebean.properties +++ b/src/test/resources/ebean.properties @@ -24,8 +24,8 @@ ebean.autofetch.profiling.base=10 ebean.autofetch.traceUsageCollection=false -#ebean.ddl.generate=true -#ebean.ddl.run=true +ebean.ddl.generate=true +ebean.ddl.run=true ebean.debug.sql=true @@ -81,7 +81,7 @@ ebean.cacheWarmingDelay=-1 ## DataSources (If using default Ebean DataSourceFactory) ## ------------------------------------------------------------- -datasource.default=mysql +datasource.default=h2 datasource.h2.username=sa datasource.h2.password=