Fix for Issue 72 - Bug - models.Role cannot be cast to java.util.Map [error] at com.avaje.ebeaninternal.server.persist.DefaultPersister.saveAssocManyDetails(DefaultPersister.java:877)

This commit is contained in:
Rob Bygrave
2014-01-23 00:50:09 +13:00
parent 87a948223c
commit ee96c4afa6
12 changed files with 169 additions and 22 deletions
@@ -145,14 +145,19 @@ public interface BeanCollection<E> extends Serializable {
public boolean isEmpty();
/**
* Returns the underlying details as an iterator.
* <p>
* Note that for maps this returns the entrySet as we need the keys of the
* map.
* </p>
* Returns the underlying collection of beans from the Set, Map or List.
*/
public Collection<E> getActualDetails();
/**
* Returns the underlying entries so for Maps this is a collection of
* Map.Entry.
* <p>
* For maps this returns the entrySet as we need the keys of the map.
* </p>
*/
public Collection<?> getActualEntries();
/**
* Set to true if maxRows was hit and there are actually more rows available.
* <p>
@@ -106,6 +106,11 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
public Collection<E> getActualDetails() {
return list;
}
@Override
public Collection<?> getActualEntries() {
return list;
}
/**
* Returns the underlying list.
@@ -104,14 +104,23 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
}
/**
* Returns the map entrySet iterator.
* Returns the collection of beans (map values).
*/
public Collection<E> getActualDetails() {
return map.values();
}
/**
* Returns the map entrySet.
* <p>
* This is because the key values may need to be set against the details (so
* they don't need to be set twice).
* </p>
*/
public Collection<E> getActualDetails() {
return map.values();
public Collection<?> getActualEntries() {
return map.entrySet();
}
/**
@@ -12,8 +12,7 @@ import com.avaje.ebean.bean.BeanCollectionLoader;
/**
* Set capable of lazy loading.
*/
public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E>,
BeanCollectionAdd {
public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E>, BeanCollectionAdd {
/**
* The underlying Set implementation.
@@ -113,6 +112,11 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
return set;
}
@Override
public Collection<?> getActualEntries() {
return set;
}
/**
* Returns the underlying set.
*/
@@ -970,11 +970,14 @@ public class BeanDescriptor<T> implements MetaBeanInfo {
public void cachePutMany(BeanPropertyAssocMany<?> many, BeanCollection<?> bc, Object parentId) {
BeanDescriptor<?> targetDescriptor = many.getTargetDescriptor();
Collection<?> actualDetails = bc.getActualDetails();
ArrayList<Object> idList = new ArrayList<Object>();
// 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);
@@ -38,6 +38,10 @@ public class ManyType {
}
}
public boolean isMap() {
return Underlying.MAP.equals(underlying);
}
/**
* Return the matching Query type.
*/
@@ -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() + "]");
}
/**
@@ -83,7 +83,7 @@ public class DefaultOrmQueryEngine implements OrmQueryEngine {
Collection<T> c = result.getActualDetails();
for (T bean : c) {
descriptor.cachePutBeanData(bean);
}
}
}
if (!result.isEmpty() && query.isUseQueryCache()){
@@ -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;
}
}
@@ -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<Long, MpRole> roles = new HashMap<Long, MpRole>();
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<Long, MpRole> getRoles() {
return roles;
}
public void setRoles(Map<Long, MpRole> roles) {
this.roles = roles;
}
}
@@ -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<Long, MpRole> listMap = u3.getRoles();
Assert.assertEquals(1, listMap.size());
}
}
+3 -3
View File
@@ -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=