Java tutorial
/** * Copyright (C) 2013 cherimojava (http://github.com/cherimojava/cherimodata) Licensed under the Apache License, Version * 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the * License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language governing permissions and limitations * under the License. */ package com.github.cherimojava.data.mongo.io; import static com.github.cherimojava.data.mongo.CommonInterfaces.CollectionEntity; import static com.github.cherimojava.data.mongo.CommonInterfaces.ComputedPropertyEntity; import static com.github.cherimojava.data.mongo.CommonInterfaces.EntityList; import static com.github.cherimojava.data.mongo.CommonInterfaces.ExplicitIdEntity; import static com.github.cherimojava.data.mongo.CommonInterfaces.NestedEntity; import static com.github.cherimojava.data.mongo.CommonInterfaces.PrimitiveEntity; import static com.github.cherimojava.data.mongo.CommonInterfaces.ReferencingEntity; import static com.github.cherimojava.data.mongo.entity.Entity.ID; import static com.github.cherimojava.data.mongo.entity.EntityFactory.instantiate; import static com.github.cherimojava.data.mongo.entity.EntityUtils.getCollectionName; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.core.AllOf.allOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; import java.io.StringWriter; import java.util.Date; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.lang3.StringUtils; import org.bson.BsonDocument; import org.bson.BsonString; import org.bson.Document; import org.bson.json.JsonReader; import org.bson.json.JsonWriter; import org.bson.types.ObjectId; import org.joda.time.DateTime; import org.junit.Test; import com.github.cherimojava.data.mongo.CommonInterfaces; import com.github.cherimojava.data.mongo.MongoBase; import com.github.cherimojava.data.mongo.entity.Entity; import com.github.cherimojava.data.mongo.entity.EntityFactory; import com.github.cherimojava.data.mongo.entity.EntityUtils; import com.github.cherimojava.data.mongo.entity.annotation.Final; import com.github.cherimojava.data.mongo.entity.annotation.Id; import com.github.cherimojava.data.mongo.entity.annotation.Reference; import com.github.cherimojava.data.mongo.entity.annotation.Transient; import com.google.common.collect.Lists; import com.mongodb.Block; import com.mongodb.DBRef; import com.mongodb.client.MongoCollection; import com.mongodb.client.model.UpdateOptions; @SuppressWarnings("unchecked") public class _DeEncoding extends MongoBase { /** * Tests if de/encoding a simple Entity with only primitive data types work */ @Test public void basicDeEncoding() { EntityCodec codec = new EntityCodec<>(db, EntityFactory.getProperties(PrimitiveEntity.class)); PrimitiveEntity pe = instantiate(PrimitiveEntity.class); pe.setString("value"); pe.setInteger(123); StringWriter swriter = new StringWriter(); JsonWriter jwriter = new JsonWriter(swriter); codec.encode(jwriter, pe, null); assertJson(sameJSONAs("{ \"Integer\" : 123, \"string\" : \"value\" }"), swriter); JsonReader jreader = new JsonReader(swriter.toString()); PrimitiveEntity read = decode(codec, jreader, PrimitiveEntity.class); assertEquals(pe.getString(), read.getString()); assertEquals(pe.getInteger(), read.getInteger()); } @Test public void basicDeEncodingDB() { PrimitiveEntity pe = factory.create(PrimitiveEntity.class); pe.setString("some string"); pe.setInteger(12); pe.save(); Document doc = db.getCollection(getCollectionName(PrimitiveEntity.class)).find(new Document()).limit(1) .iterator().next(); assertJson(sameJSONAs("{ \"Integer\" : 12, \"string\" : \"some string\" }").allowingExtraUnexpectedFields(), doc); PrimitiveEntity read = factory.load(PrimitiveEntity.class, doc.get("_id")); assertEquals(pe, read); assertEquals(read, pe.load(doc.get("_id"))); } @Test public void noDuplicateIdWritten() { ExplicitIdEntity eid = factory.readEntity(ExplicitIdEntity.class, "{\"_id\":\"explicit\"}"); assertEquals("explicit", eid.getName()); EntityCodec<ExplicitIdEntity> codec = new EntityCodec<>(db, EntityFactory.getProperties(ExplicitIdEntity.class)); StringWriter swriter = new StringWriter(); JsonWriter writer = new JsonWriter(swriter); codec.encode(writer, eid, null); assertEquals(1, StringUtils.countMatches(swriter.toString(), "explicit")); ExplicitIdEntity eid2 = factory.create(ExplicitIdEntity.class); eid2.setName("once"); swriter = new StringWriter(); writer = new JsonWriter(swriter); codec.encode(writer, eid2, null); assertEquals(1, StringUtils.countMatches(swriter.toString(), "once")); } @Test public void computedStoredCorrectly() { ComputedPropertyEntity cpe = factory.create(ComputedPropertyEntity.class); cpe.setString("some"); cpe.setInteger(1); cpe.save(); Document doc = db.getCollection(getCollectionName(ComputedPropertyEntity.class)).find(new Document()) .limit(1).iterator().next(); assertJson(sameJSONAs("{ \"string\":\"some\", \"Integer\":1, \"computed\":\"some1\"}") .allowingExtraUnexpectedFields(), doc); assertEquals(doc.get("_id"), cpe.get("_id")); } @Test public void explicitIdBasicDeEncodingDB() { ExplicitIdEntity ee = factory.create(ExplicitIdEntity.class); ee.setName("some"); ee.save(); Document doc = db.getCollection(getCollectionName(ExplicitIdEntity.class)).find(new Document()).limit(1) .iterator().next(); assertJson(sameJSONAs("{ \"_id\" : \"some\" }").allowingExtraUnexpectedFields(), doc); ExplicitIdEntity read = factory.load(ExplicitIdEntity.class, doc.get("_id")); assertEquals(ee, read); } @Test public void transientPropertyDeEncodingDB() { TransientEntity te = factory.create(TransientEntity.class); te.setIdentity("some"); te.setTransient("trans"); te.save(); Document doc = db.getCollection(getCollectionName(TransientEntity.class)).find(new Document("_id", "some")) .limit(1).iterator().next(); assertJson(sameJSONAs("{ \"_id\" : \"some\" }"), doc); TransientEntity read = factory.load(TransientEntity.class, doc.get("_id")); assertEquals(te.getIdentity(), read.getIdentity()); assertNull(read.getTransient()); } @Test public void transientPropertyNotDecoded() { String _id = "different"; getCollection(TransientEntity.class).updateOne(new Document(Entity.ID, _id), new BsonDocument("$set", new BsonDocument("transient", new BsonString("ignored"))), new UpdateOptions().upsert(true)); TransientEntity read = factory.load(TransientEntity.class, _id); assertEquals("different", read.getIdentity()); assertNull(read.getTransient()); } /** * tests if de/encoding a complex (nested) entity works */ @Test public void nestedDeEncoding() { EntityCodec codec = new EntityCodec<>(db, EntityFactory.getProperties(NestedEntity.class)); PrimitiveEntity pe = instantiate(PrimitiveEntity.class); pe.setString("value"); pe.setInteger(123); NestedEntity ce = instantiate(NestedEntity.class); ce.setString("outer"); ce.setPE(pe); StringWriter swriter = new StringWriter(); JsonWriter jwriter = new JsonWriter(swriter); codec.encode(jwriter, ce, null); assertJson(sameJSONAs("{ \"string\" : \"outer\", \"PE\" : { \"Integer\" : 123, \"string\" : \"value\" } }"), swriter); JsonReader jreader = new JsonReader(swriter.toString()); NestedEntity ceRead = decode(codec, jreader, NestedEntity.class); PrimitiveEntity peRead = ceRead.getPE(); assertEquals(pe.getInteger(), peRead.getInteger()); assertEquals(pe.getString(), peRead.getString()); assertEquals(ce.getString(), ceRead.getString()); } @Test public void referenceDeEncodingDB() { ReferencingEntity re = factory.create(ReferencingEntity.class); PrimitiveEntity pe = factory.create(PrimitiveEntity.class); pe.setString("nestedString"); re.setPE(pe); re.setString("some"); re.save(); Document reRead = db.getCollection(getCollectionName(ReferencingEntity.class)) .find(new Document(ID, re.get(ID))).limit(1).iterator().next(); ObjectId dbRef = (ObjectId) reRead.get("PE"); assertNotNull(dbRef); assertNotNull(pe.get(ID)); assertEquals(pe.get(ID), dbRef); // make sure object needs to be saved separately assertFalse(db.getCollection(getCollectionName(PrimitiveEntity.class)).find(new Document(ID, pe.get(ID))) .limit(1).iterator().hasNext()); pe.save(); // After explicit saving it should be available Document peRead = db.getCollection(getCollectionName(PrimitiveEntity.class)) .find(new Document(ID, pe.get(ID))).limit(1).iterator().next(); assertEquals(pe.get(ID), peRead.get(ID)); assertEquals(pe.getString(), peRead.get("string")); ReferencingEntity read = factory.load(ReferencingEntity.class, re.get(ID)); assertEquals("nestedString", read.getPE().getString());// This property is lazy loaded assertEquals(re, read); // direct equals should trigger lazy loading too read = factory.load(ReferencingEntity.class, re.get(ID)); assertEquals(re, read); } @Test public void dataLoadedLazy() { ReferencingEntity re = factory.create(ReferencingEntity.class); PrimitiveEntity pe = factory.create(PrimitiveEntity.class); pe.setString("nestedString"); re.setPE(pe); re.setString("some"); re.save(); pe.save(); String changedString = "StringNested"; ReferencingEntity load = (ReferencingEntity) re.load(re.get(ID)); db.getCollection(EntityFactory.getProperties(PrimitiveEntity.class).getCollectionName()).updateOne( new Document("_id", pe.get(ID)), new Document("$set", new Document("string", changedString))); assertEquals(changedString, load.getPE().getString()); changedString = "nestedString"; load = (ReferencingEntity) re.load(re.get(ID)); assertNotNull(load.get(Entity.ID)); db.getCollection(EntityFactory.getProperties(PrimitiveEntity.class).getCollectionName()).updateOne( new Document("_id", pe.get(ID)), new Document("$set", new Document("string", changedString))); assertEquals(changedString, load.getPE().getString()); } @Test public void testDBRefDeEncoding() { ReferencingEntity re = factory.create(ReferencingEntity.class); PrimitiveEntity pe = factory.create(PrimitiveEntity.class); pe.setString("nestedString"); re.setDBRef(pe); re.setString("some"); re.save(); Document reRead = db.getCollection(getCollectionName(ReferencingEntity.class)) .find(new Document(ID, re.get(ID))).limit(1).iterator().next(); ObjectId dbRef = (ObjectId) ((DBRef) reRead.get("dBRef")).getId(); assertNotNull(dbRef); assertNotNull(pe.get(ID)); assertEquals(pe.get(ID), dbRef); // make sure only available after explicit saving assertFalse(db.getCollection(getCollectionName(PrimitiveEntity.class)).find(new Document(ID, pe.get(ID))) .limit(1).iterator().hasNext()); pe.save(); Document peRead = db.getCollection(getCollectionName(PrimitiveEntity.class)) .find(new Document(ID, pe.get(ID))).limit(1).iterator().next(); assertEquals(pe.get(ID), peRead.get(ID)); assertEquals(pe.getString(), peRead.get("string")); ReferencingEntity read = factory.load(ReferencingEntity.class, re.get(ID)); assertEquals(re, read); } @Test public void ignoreUnknownProps() { PrimitiveEntity pe = factory.readEntity(PrimitiveEntity.class, "{\"string\":\"some\",\"Integer\":1,\"unknown\":\"dontfail\"}"); assertEquals(1, (int) pe.getInteger()); assertEquals("some", pe.getString()); } @Test public void ignoreUnknownSubtype() { PrimitiveEntity pe = factory.readEntity(PrimitiveEntity.class, "{\"unknown\": {\"string\":\"dontfail\"},\"string\":\"some\",\"Integer\":1}"); assertEquals(1, (int) pe.getInteger()); assertEquals("some", pe.getString()); } @Test public void loadedEntityCanBeSaved() { ReferencingEntity re = factory.create(ReferencingEntity.class); PrimitiveEntity pe = factory.create(PrimitiveEntity.class); pe.setString("nestedString"); re.setPE(pe); re.setString("some"); re.save(); ReferencingEntity read = factory.load(ReferencingEntity.class, re.get(ID)); read.setInteger(2).save(); pe.setInteger(4).save(); } @Test public void saveByFactory() { PrimitiveEntity pe = EntityFactory.instantiate(PrimitiveEntity.class); pe.setInteger(1); pe.setString("some"); factory.save(pe); PrimitiveEntity read = factory.load(PrimitiveEntity.class, pe.get(ID)); assertEquals(pe, read); } @Test public void persistAfterSave() { NestedEntity ne = factory.create(NestedEntity.class); PrimitiveEntity pe = factory.create(PrimitiveEntity.class); assertFalse(EntityUtils.isPersisted(ne)); assertFalse(EntityUtils.isPersisted(pe)); ne.setString("outer"); pe.setString("inner"); ne.setPE(pe); ne.save(); assertTrue(EntityUtils.isPersisted(ne)); assertTrue(EntityUtils.isPersisted(pe)); NestedEntity read = factory.load(NestedEntity.class, ne.get(ID)); assertTrue(EntityUtils.isPersisted(read)); assertTrue(EntityUtils.isPersisted(read.getPE())); } @Test public void finalIsFinalAfterSave() throws NoSuchMethodException { ExplicitIdEntity e = factory.create(ExplicitIdEntity.class); e.setName("some").save(); try { e.setName("change"); fail("should throw an exception"); } catch (IllegalStateException ise) { assertThat(ise.getMessage(), containsString("@Final property")); } } @Test public void finalIsFinalAfterLoad() { ExplicitIdEntity e = factory.create(ExplicitIdEntity.class); e.setName("t").save(); try { e.setName("different"); fail("should throw an exception"); } catch (IllegalStateException ise) { assertThat(ise.getMessage(), containsString("@Final property")); } } @Test public void collectionDeEncoding() { EntityCodec codec = new EntityCodec<>(db, EntityFactory.getProperties(CollectionEntity.class)); CollectionEntity ce = instantiate(CollectionEntity.class); ce.setArrayStrings(new String[] { "one", "two" }); ce.setStrings(Lists.newArrayList("three", "four")); StringWriter swriter = new StringWriter(); JsonWriter jwriter = new JsonWriter(swriter); codec.encode(jwriter, ce, null); assertJson(sameJSONAs("{ \"arrayStrings\": [\"one\",\"two\"],\"strings\":[\"three\",\"four\"] }"), swriter); JsonReader jreader = new JsonReader(swriter.toString()); CollectionEntity ceRead = decode(codec, jreader, CollectionEntity.class); assertJson(sameJSONAs("{ \"arrayStrings\": [\"one\",\"two\"],\"strings\":[\"three\",\"four\"] }"), ceRead); } @Test public void collectionDeEncodingDB() { EntityCodec codec = new EntityCodec<>(db, EntityFactory.getProperties(Listed.class)); Listed listed = factory.create(Listed.class); listed.setList(Lists.newArrayList(factory.create(PrimitiveEntity.class).setString("nested"))); StringWriter swriter = new StringWriter(); JsonWriter jwriter = new JsonWriter(swriter); codec.encode(jwriter, listed, null); assertJson(sameJSONAs("{ \"list\": [{\"string\": \"nested\"}]}"), swriter); JsonReader jsonReader = new JsonReader(swriter.toString()); Listed read = decode(codec, jsonReader, Listed.class); assertNotNull(read.getList()); assertEquals(listed.getList().get(0), read.getList().get(0)); listed.save(); Listed r = factory.load(Listed.class, listed.get(ID)); assertEquals(listed, r); } @Test public void enumDeEncoding() { EntityCodec codec = new EntityCodec<>(db, EntityFactory.getProperties(EnumEntity.class)); EnumEntity ee = instantiate(EnumEntity.class); ee.setCategory(EnumEntity.Category.Misc); StringWriter swriter = new StringWriter(); JsonWriter jwriter = new JsonWriter(swriter); codec.encode(jwriter, ee, null); assertJson(sameJSONAs("{ \"category\": \"Misc\" }"), swriter); JsonReader jreader = new JsonReader(swriter.toString()); EnumEntity eeRead = decode(codec, jreader, EnumEntity.class); assertEquals(ee, eeRead); } @Test public void enumDeEncodingUnknownEnum() { EntityCodec codec = new EntityCodec<>(db, EntityFactory.getProperties(EnumEntity.class)); JsonReader jreader = new JsonReader("{ \"category\": \"miscellaneous\" }"); try { decode(codec, jreader, EnumEntity.class); fail("Should throw an exception"); } catch (IllegalArgumentException e) { assertThat(e.getMessage(), containsString("doesn't match any declared enum value of")); } } @Test public void saveDrop() { PrimitiveEntity pe = factory.create(PrimitiveEntity.class); factory.create(PrimitiveEntity.class).setString("don't delete").save(); pe.setString("413").save(); final AtomicInteger count = new AtomicInteger(0); MongoCollection<PrimitiveEntity> coll = getCollection(PrimitiveEntity.class); coll.find(PrimitiveEntity.class).forEach(new Block<Entity>() { @Override public void apply(Entity entity) { count.getAndIncrement(); } }); assertEquals(2, count.get()); pe.drop(); count.compareAndSet(2, 0); coll.find(PrimitiveEntity.class).forEach(new Block<Entity>() { @Override public void apply(Entity entity) { count.getAndIncrement(); } }); assertEquals(1, count.get()); } @Test public void noFailOnSaveDropIfMongoGiven() { PrimitiveEntity pe = factory.create(PrimitiveEntity.class); pe.setString("some String"); pe.save(); pe.drop(); } @Test public void explicitIdMustBeSetOnSave() { ExplicitIdEntity pe = factory.create(ExplicitIdEntity.class); pe.setName("sme").setName(null);// else it won't try to save try { pe.save(); fail("should throw an exception"); } catch (NullPointerException e) { assertThat(e.getMessage(), containsString("An explicit defined Id ")); } pe.setName("some"); pe.save(); } @Test public void dateTimeDeEncoding() { DateTimeEntity e = factory.create(DateTimeEntity.class); e.setTime(DateTime.now()).setDate(new Date()).setId("1"); e.save(); assertEquals(e.toString(), factory.load(DateTimeEntity.class, "1").toString()); } @Test public void collectionEncodingEntity() { EntityList list = factory.create(EntityList.class); list.setId("1"); List<PrimitiveEntity> entities = Lists.newArrayList(); entities.add(factory.create(PrimitiveEntity.class).setString("one")); entities.add(factory.create(PrimitiveEntity.class).setString("two")); list.setList(entities); list.save(); assertEquals(list.toString(), factory.load(EntityList.class, "1").toString()); } @Test public void referenceListEntityEncoding() { ObjectId id = new ObjectId(); ReferencingEntity reference = factory.create(ReferencingEntity.class); reference.set(ID, id); reference.setString("happy"); List<PrimitiveEntity> entities = Lists.newArrayList(); PrimitiveEntity pone = factory.create(PrimitiveEntity.class).setString("one"); PrimitiveEntity ptwo = factory.create(PrimitiveEntity.class).setString("two"); entities.add(pone); entities.add(ptwo); // check what happens if nothing is set reference.save(); assertEquals(reference.toString(), factory.load(ReferencingEntity.class, id).toString()); // check what happens if null is set reference.setListedEntities(Lists.<PrimitiveEntity>newArrayList()); reference.save(); assertEquals(reference.toString(), factory.load(ReferencingEntity.class, id).toString()); // set some list reference.setListedEntities(entities); reference.save(); // only persisted after explicit call assertNull(factory.load(PrimitiveEntity.class, pone.get(ID))); assertNull(factory.load(PrimitiveEntity.class, ptwo.get(ID))); pone.save(); ptwo.save(); // and that the objects are stored separately too assertEquals(pone.toString(), factory.load(PrimitiveEntity.class, pone.get(ID)).toString()); assertEquals(ptwo.toString(), factory.load(PrimitiveEntity.class, ptwo.get(ID)).toString()); // check that the parent entity is correct assertEquals(reference.toString(), factory.load(ReferencingEntity.class, id).toString()); } @Test public void referenceListDBRefEntityEncoding() { ObjectId id = new ObjectId(); ReferencingEntity reference = factory.create(ReferencingEntity.class); reference.set(ID, id); reference.setString("happy"); List<PrimitiveEntity> entities = Lists.newArrayList(); PrimitiveEntity pone = factory.create(PrimitiveEntity.class).setString("one"); PrimitiveEntity ptwo = factory.create(PrimitiveEntity.class).setString("two"); entities.add(pone); entities.add(ptwo); // check what happens if nothing is set reference.save(); assertEquals(reference.toString(), factory.load(ReferencingEntity.class, id).toString()); // check what happens if null is set reference.setListedDBRefEntities(Lists.<PrimitiveEntity>newArrayList()); reference.save(); assertEquals(reference.toString(), factory.load(ReferencingEntity.class, id).toString()); // set some list reference.setListedDBRefEntities(entities); reference.save(); // make sure objects are only stored if they're explicitly saved assertNull(factory.load(PrimitiveEntity.class, pone.get(ID))); assertNull(factory.load(PrimitiveEntity.class, ptwo.get(ID))); pone.save(); ptwo.save(); // and that the objects are stored separately too assertEquals(pone.toString(), factory.load(PrimitiveEntity.class, pone.get(ID)).toString()); assertEquals(ptwo.toString(), factory.load(PrimitiveEntity.class, ptwo.get(ID)).toString()); // check that the parent entity is correct assertEquals(reference.toString(), factory.load(ReferencingEntity.class, id).toString()); } @Test public void autoboxingNoHarmFinal() { AutoboxedEntity ae = factory.create(AutoboxedEntity.class); ae.setInt(3).save(); assertEquals(3, (int) ae.load(ae.get(ID)).getInt()); Integer i = ae.getInt(); i = i++; ae.setString("some").save(); assertEquals(3, (int) ae.load(ae.get(ID)).getInt()); } /** * test that a recursion within entities detected and broken up (no change after first save) */ @Test public void cycleBreakerInternalReference() { RecursiveEntity outer = factory.create(RecursiveEntity.class); outer.setId("outer"); RecursiveEntity inner = factory.create(RecursiveEntity.class); inner.setId("inner"); outer.setInner(inner.setInner(outer)); outer.save();// will die here without cycle detection assertEquals(outer.toString(), factory.load(RecursiveEntity.class, "outer").toString()); } @Test public void implicitIdTypeVerification() { try { factory.create(ReferencingEntity.class).set(ID, "one"); fail("should throw an exception"); } catch (ClassCastException e) { assertThat(e.getMessage(), allOf(containsString("ObjectId"), containsString("String"))); } } @Test public void primitivesWorking() { CommonInterfaces.PrimitiveTypeEntity pte = factory.create(CommonInterfaces.PrimitiveTypeEntity.class); pte.setInt(1).save(); assertEquals(1, factory.load(CommonInterfaces.PrimitiveTypeEntity.class, 1).getInt()); } @Test public void cycleBreakerExternalReference() { ThingOne one = factory.create(ThingOne.class); ThingTwo two = factory.create(ThingTwo.class); one.setOther(two.setOther(one)); one.save();// should work } /** * verify that if no explicit id is defined, one will be written. */ @Test public void idImplicitEncoding() { Entity e = factory.create(Entity.class); assertNull(e.get(ID)); e.save(); assertNotNull(e.get(ID)); } @Test public void isMethodWorking() { CommonInterfaces.IsEntity ie = factory.create(CommonInterfaces.IsEntity.class); ie.setBoolean(true).save(); assertTrue(factory.load(CommonInterfaces.IsEntity.class, ie.get(Entity.ID)).isBoolean()); } private static interface ThingOne extends Entity<ThingOne> { @Reference public ThingTwo getOther(); public ThingOne setOther(ThingTwo two); } private static interface ThingTwo extends Entity<ThingTwo> { @Reference public ThingOne getOther(); public ThingTwo setOther(ThingOne two); } private static interface AutoboxedEntity extends Entity<AutoboxedEntity> { @Final public Integer getInt(); public AutoboxedEntity setInt(Integer i); public String getString(); public AutoboxedEntity setString(String s); } private static interface TransientEntity extends Entity { @Transient public String getTransient(); public TransientEntity setTransient(String trans); @Id public String getIdentity(); public TransientEntity setIdentity(String id); } private static interface EnumEntity extends Entity { public EnumEntity setCategory(Category cat); public Category getCategory(); public static enum Category { Misc, Other } } private interface Listed extends Entity<Listed> { public Listed setList(List<PrimitiveEntity> entities); public List<PrimitiveEntity> getList(); } private interface DateTimeEntity extends Entity<DateTimeEntity> { public String getId(); public DateTimeEntity setId(String id); public DateTime getTime(); public DateTimeEntity setTime(DateTime time); public Date getDate(); public DateTimeEntity setDate(Date date); } private <T extends Entity> MongoCollection<T> getCollection(Class<T> clazz) { return db.getCollection(getCollectionName(clazz)).withDocumentClass(clazz) .withCodecRegistry(EntityCodecProvider.createCodecRegistry(db, clazz)); } private interface RecursiveEntity extends Entity<RecursiveEntity> { public RecursiveEntity getInner(); public RecursiveEntity setInner(RecursiveEntity inner); public String getId(); public RecursiveEntity setId(String id); } }