Skip to content

Instantly share code, notes, and snippets.

@magro
Created January 5, 2012 15:00
Show Gist options
  • Save magro/1565605 to your computer and use it in GitHub Desktop.
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.
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