Question

My goal here is to make a very simplistic Object<->Entity mapping here is what I have done so far, this is recursive:

/**
     *
     * Create Entity objects that can be persisted into the GAE datastore,
     * including its Parent-Child relationships (if necessary).
     *
     * @param parent parent of the generated Entity or Entities
     * @param instance
     * @return
     */
    public Entity createEntityFrom(Key parent, Object instance){
        Preconditions.checkNotNull(instance, "Object should not be null");
        if (stack == null){ // List<Entity> stack;
            stack = new LinkedList<Entity>();
        }
        stack.clear();
        Key key = createKeyFrom(parent, instance); // inspect kind and create key
        Map<String,Object> props = new LinkedHashMap<String, Object>();
        List<Entity> target = null;
        Entity e = new Entity(key);
        Field[] fields = instance.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (target == null){
                target = new LinkedList<Entity>();
            }
            String fieldName = field.getName();
            if(field.isAnnotationPresent(Id.class)){
                // skip
                continue;
            }
            try {
                boolean isAccessible = field.isAccessible();
                field.setAccessible(true);
                Class<?> clazz = field.getType();
                Object fieldValue = field.get(instance);

                if (fieldValue == null){
                    e.setProperty(fieldName, null);
                } else if (fieldValue instanceof String) {
                    setProperty(e, fieldName, fieldValue);
                } else if(fieldValue instanceof Number
                        || fieldValue instanceof Long
                        || fieldValue instanceof Integer
                        || fieldValue instanceof Short) {
                    setProperty(e, fieldName, fieldValue);
                } else if(fieldValue instanceof Boolean) {
                    setProperty(e, fieldName, fieldValue);
                } else if(fieldValue instanceof Date) {
                    setProperty(e, fieldName, fieldValue);
                } else if(fieldValue instanceof User) { // GAE support this type
                    setProperty(e, fieldName, fieldValue);
                } else if(fieldValue instanceof List) {
                    LOG.debug( "Processing List valueType");
                    if (field.isAnnotationPresent(Embedded.class)){
                        setProperty(e, fieldName, createEmbeddedEntityFromList(parent, (List) fieldValue));
                    } else {
                        // TODO
                        List<Object> list = (List<Object>) fieldValue;
                        List<Entity> result = new LinkedList<Entity>();
                        for (Object o : list){
                            //result = createEntityFrom(e.getKey(), o);
                        }
                    }
                } else if(fieldValue instanceof Map){
                    LOG.debug( "Processing Map valueType");
                    if (field.isAnnotationPresent(Embedded.class)){
                        setProperty(e, fieldName, createEmbeddedEntityFromMap(parent, (Map) fieldValue));
                    } else {
                        Entity mapEntity = new Entity(KeyStructure.createKey(e.getKey(), "kind", "key"));
                        Map map = (Map) fieldValue;
                        Iterator it = map.entrySet().iterator();
                        while(it.hasNext()){
                            Map.Entry entry = (Map.Entry) it.next();
                            Object entryKey = entry.getKey();
                            Object entryVal = entry.getValue();
                            validateKey(entryKey);
                            if(entryKey instanceof String){
                                //createEntityFrom()
                            } else if (entryKey instanceof Long){

                            } else {

                            }
                        }
                    }
                } else {
                    // For primitives
                    if (clazz.equals(int.class)){
                        int i = (Integer) fieldValue;
                        setProperty(e, fieldName, i);
                    } else if (clazz.equals(boolean.class)){
                        boolean i = (Boolean) fieldValue;
                        setProperty(e, fieldName, i);
                    } else if (clazz.equals(byte.class)){
                        byte i = (Byte) fieldValue;
                        setProperty(e, fieldName, i);
                    } else if (clazz.equals(short.class)){
                        short i = (Short) fieldValue;
                        setProperty(e, fieldName, i);
                    } else if (clazz.equals(long.class)){
                        long i = (Long) fieldValue;
                        setProperty(e, fieldName, i);
                    } else if (clazz.equals(float.class)){
                        float i = (Float) fieldValue;
                        setProperty(e, fieldName, i);
                    } else if (clazz.equals(double.class)){
                        double i = (Double) fieldValue;
                        setProperty(e, fieldName, i);
                    } else { // POJO
                        if (field.isAnnotationPresent(Parent.class)){
                            // update key
                            Key oldKey = e.getKey();
                            Entity parentEntity = createEntityFrom(null, field); // then add to the stack
                            // TODO
                        } else if (field.isAnnotationPresent(Child.class)){
                            Entity childEntity = createEntityFrom(e.getKey(), field);
                            setProperty(e, fieldName, childEntity.getKey());
                        }
                        Key parentKey = e.getKey();
                        Entity child = createEntityFrom(parentKey, fieldValue);
                        setProperty(e, fieldName, child.getKey());
                        stack.add(e);
                    }
                }
                field.setAccessible(isAccessible);
            } catch(IllegalAccessException ex){
                ex.printStackTrace();
            }
        }
        return e;
    }

Test

Here's how it its used:

@Test
public void test(){
    Friend f = new Friend(); // one Entity
    f.setName("TestUser");
    f.setAge(25);
    f.setNewAddress(new Address("Test City")); // one Entity, @Child
    f.setOldAddress(new Address("Old Test City")); // not included, @Embedded
    List<Entity> stack = new LinkedList<Entity>();
    ObjectMapper mapper =  new ObjectMapper();
    mapper.createEntityFrom(null, f);
    assertEquals(2, stack.size());
}

Question is there any existing Object-to-Entity mapping code? I'm not really looking for full pledged ORM framework, just a simple POJO to Entity converter code. And is this code bloated in any way?

Was it helpful?

Solution 2

This is the GaeMarshaller that worked for me.

OTHER TIPS

You're on the painful way of writing yet another persistence library. Be careful, a "pure" object model hardly matches with datastore entities. At some point you're going to make compromises to match the underlying entity structure, and at that time you will realize that a better library was doing just that all along.

For example : you're providing your entity's parent key, but where is the key of that entity if you just want to update it? How will you handle lazy loading, if at some point you don't want to load all your object graph for one small query? How do you serialize keys? What about Blobs and byte[] ? How do you handle transcient attributes ?

Do yourself a favor before you spend weeks of work on something that already exists, use a proven persistence tool before you stumble on a problem that is too big for one man. If you need to keep your pure object model (for example if you're using GWT on the front end), then map persistent classes to your POJOs.

And if you really don't need something like, say, Objectify, it probably means you'll be better off with the flexibility of the low level API. It does not take that long to map entities to your classes.

The bottom line is that by writing your own "framework", you're missing out on both the flexibility of the manual-&-boring low level API and the rigid-but-solid tool that does it all better than you.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top