Created
January 5, 2012 15:00
-
-
Save magro/1565605 to your computer and use it in GitHub Desktop.
Test that shows a issue of morphia with an inheritance stucture of @Embedded objects.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import static org.junit.Assert.assertEquals; | |
import org.junit.Before; | |
import org.junit.Test; | |
import com.google.code.morphia.Morphia; | |
import com.google.code.morphia.annotations.Converters; | |
import com.google.code.morphia.annotations.Embedded; | |
import com.google.code.morphia.annotations.Entity; | |
import com.google.code.morphia.annotations.Id; | |
import com.google.code.morphia.converters.SimpleValueConverter; | |
import com.google.code.morphia.converters.TypeConverter; | |
import com.google.code.morphia.mapping.MappedField; | |
import com.google.code.morphia.mapping.MappingException; | |
import com.mongodb.BasicDBObjectBuilder; | |
import com.mongodb.DBCollection; | |
import com.mongodb.DBObject; | |
import com.mongodb.DefaultDBDecoder; | |
import com.mongodb.DefaultDBEncoder; | |
/** | |
* This test shows an issue with an <code>@Embedded</code> class A inheriting from an | |
* <code>@Embedded</code> class B that both have a Converter assigned (A has AConverter, | |
* B has BConverter). | |
* <p> | |
* When an object (here MyEntity) has a property/field of type A and is deserialized, | |
* the deserialization fails with a | |
* "com.google.code.morphia.mapping.MappingException: No usable constructor for A". | |
* </p> | |
*/ | |
public class MorphiaEmbeddedInheritanceTest { | |
@Entity(noClassnameStored = true) | |
static class MyEntity { | |
@Id | |
private Long id; | |
@Embedded | |
private A a; | |
public MyEntity() { | |
} | |
public MyEntity(final Long id, final A a) { | |
this.id = id; | |
this.a = a; | |
} | |
@Override | |
public int hashCode() { | |
final int prime = 31; | |
int result = 1; | |
result = prime * result + ((id == null) ? 0 : id.hashCode()); | |
result = prime * result + ((a == null) ? 0 : a.hashCode()); | |
return result; | |
} | |
@Override | |
public boolean equals(final Object obj) { | |
if (this == obj) { | |
return true; | |
} | |
if (obj == null) { | |
return false; | |
} | |
if (getClass() != obj.getClass()) { | |
return false; | |
} | |
final MyEntity other = (MyEntity) obj; | |
if (id == null) { | |
if (other.id != null) { | |
return false; | |
} | |
} else if (!id.equals(other.id)) { | |
return false; | |
} | |
if (a == null) { | |
if (other.a != null) { | |
return false; | |
} | |
} else if (!a.equals(other.a)) { | |
return false; | |
} | |
return true; | |
} | |
} | |
@Converters(B.BConverter.class) | |
@Embedded | |
static class B { | |
static class BConverter extends TypeConverter implements SimpleValueConverter { | |
public BConverter() { | |
this(B.class); | |
} | |
public BConverter(final Class<? extends B> clazz) { | |
super(clazz); | |
} | |
@SuppressWarnings({ "rawtypes" }) | |
@Override | |
public B decode(final Class targetClass, final Object fromDBObject, final MappedField optionalExtraInfo) | |
throws MappingException { | |
if (fromDBObject == null) { | |
return null; | |
} | |
final Long source = (Long) fromDBObject; | |
return create(source); | |
} | |
protected B create(final Long source) { | |
return new B(source); | |
} | |
@Override | |
public Long encode(final Object value, final MappedField optionalExtraInfo) { | |
if (value == null) { | |
return null; | |
} | |
final B source = (B) value; | |
return source.value; | |
} | |
} | |
private final long value; | |
public B(final long value) { | |
super(); | |
this.value = value; | |
} | |
@Override | |
public int hashCode() { | |
final int prime = 31; | |
int result = 1; | |
result = prime * result + (int) (value ^ (value >>> 32)); | |
return result; | |
} | |
@Override | |
public boolean equals(final Object obj) { | |
if (this == obj) { | |
return true; | |
} | |
if (obj == null) { | |
return false; | |
} | |
if (getClass() != obj.getClass()) { | |
return false; | |
} | |
final B other = (B) obj; | |
if (value != other.value) { | |
return false; | |
} | |
return true; | |
} | |
@Override | |
public String toString() { | |
return getClass().getSimpleName() + " [value=" + value + "]"; | |
} | |
} | |
@Converters(A.AConverter.class) | |
@Embedded | |
static class A extends B { | |
static final class AConverter extends B.BConverter { | |
public AConverter() { | |
super(A.class); | |
} | |
@Override | |
protected A create(final Long source) { | |
return new A(source); | |
} | |
} | |
public A(final long value) { | |
super(value); | |
} | |
} | |
private Morphia morphia; | |
@Before | |
public void setup() { | |
morphia = new Morphia(); | |
morphia.map(MyEntity.class); | |
morphia.map(B.class); | |
morphia.map(A.class); | |
} | |
/** | |
* This test is green when {@link MyEntity#a} is annotated with <code>@Property</code>, | |
* as in this case the field is not serialized at all. However, the bson encoder would | |
* fail to encode the object of type A (as shown by {@link #testFullBSONSerialization()}). | |
*/ | |
@Test | |
public void testDBObjectSerialization() { | |
final MyEntity entity = new MyEntity(1l, new A(2)); | |
final DBObject dbObject = morphia.toDBObject(entity); | |
assertEquals(BasicDBObjectBuilder.start("_id", 1l).add("a", 2l).get(), dbObject); | |
// fails with a | |
// com.google.code.morphia.mapping.MappingException: No usable constructor | |
// for InheritanceTest$A | |
final MyEntity actual = morphia.fromDBObject(MyEntity.class, dbObject); | |
assertEquals(entity, actual); | |
} | |
/** | |
* This test shows the full serialization, including bson encoding/decoding. | |
*/ | |
@Test | |
public void testFullBSONSerialization() { | |
final MyEntity entity = new MyEntity(1l, new A(2)); | |
final DBObject dbObject = morphia.toDBObject(entity); | |
final byte[] data = new DefaultDBEncoder().encode(dbObject); | |
final DBObject decoded = new DefaultDBDecoder().decode(data, (DBCollection)null); | |
// fails with a | |
// com.google.code.morphia.mapping.MappingException: No usable constructor | |
// for InheritanceTest$A | |
final MyEntity actual = morphia.fromDBObject(MyEntity.class, decoded); | |
assertEquals(entity, actual); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment