GAE (Java) endpoints with objectification - how to model partial data (for client)?

I have the following objects:

@Entity
public class Person {
  @Id public Long id;
  public String name;
  public Ref<Picture> picture;
  public String email;
  public byte age;
  public short birthday; // day of year
  public String school;
  public String very_long_life_story;
  ... some extra fields ...
}

@Entity
public class Place {
  @Id public Long id;
  public String name;
  public String comment;
  public long createdDateMS;
  public long visitors;

  @Load public List<Ref<Person>> owners;
}

      

A few notes: (A) The maximum size of the owners, in the subject of the place, is 4 (~) (B) The class of the person is presumably very large, and when you request the place, I would like to show only a subset of the person's data. This optimization is aimed at both the interaction between the server and the client and with the server. Since objectify (gae actually) loads / saves whole objects, I would like to do the following:

@Entity
pubilc class PersonShort {
  @Id public Long id;
  public Ref<Picture> picture;
  public String name;
}

      

and inside Place, I would like (instead of owners):

@Load public List<PersonShort> owners;

      

(C) The problem with this approach is that I now have duplication inside the data store. While this isn't a bad thing, the real problem is that Person will try to save the new picture or change the name; Not only will I have to update it in my Person class, but also search for every location that has a PersonShort with the same ID and update it.

(D) So the question is, is there any solution? Or am I just forced to choose between options? (1) Loading several Person classes which are great when I only need a little information about it. (2) Duplicate data with many messages

If so, which one is better (I currently find it 1)?

EDIT How about loading the whole class (1), but only sending part of it?

@Entity
public class Person {
  @Id public Long id;
  public String name;
  public Ref<Picture> picture;
  public String email;
  public byte age;
  public short birthday; // day of year
  public String school;
  public String very_long_life_story;
  ... some extra fields ...
}

public class PersonShort {
  public long id;
  public String name;
}

@Entity
public class Place {
  @Id public Long id;
  public String name;
  public String comment;
  public long createdDateMS;
  public long visitors;

  // Ignore saving to datastore
  @Ignore
  public List<PersonShort> owners;

  // Do not serialize when sending to client
  @ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
  @Load public List<Ref<Person>> ownersRef;

  @OnLoad private void loadOwners() {
    owners = new List<PersonShort>();
    for (Ref<Person> r : ownersRef) {
      owners.add(nwe PersonShort(r.get()));
    }
  }
}

      

+3


source to share


3 answers


No definite answer, but some suggestions to look at:

  • Lifecycle callbacks . When you place your object Person

    , you can have a handler @OnSave

    to automatically save your new object PersonShort

    . The advantage of this is that it is transparent to the caller, but obviously you are still dealing with two objects instead of 1.

    You may also find that you need to get two objects; initially you can get PersonShort

    , and then later you will need part of the parts in the corresponding Person

    . Remember that Objectify caching can shorten your trips to the datastore: it might be better to have a larger, cached entity than two separate entities (which means two RPCs).

  • Save your main properties (the ones in PersonShort

    ) as separate properties in your class Person

    , and then add the extended properties as a single JSON string that you can deserialize with Gson .

    This has the advantage that you don't duplicate properties, but the disadvantage is that whatever you want to find cannot be in the JSON block.

  • Projection queries . You can tell Datastore to return only certain properties from your objects. The problem with this method is that you can only return indexed properties, so you will probably find that you need too many indexes for this to be viable.



Also use annotations @Load

with care. For example, in your class, Place

consider whether you really need all the owner information Person

when accepting owners. Perhaps you only need one of them? that is, instead of getting Place

and 4 Person

every time you fetch Place

, maybe you are better off just loading the required Person

(s) when you need them? (This will depend on your application.)

+1


source


It looks like you are optimizing prematurely. Do you know you have a performance problem?



Unless you're talking hundreds of K, don't worry about the size of your Person object in advance. There is no practical value for hiding a few extra fields unless the size is serious - in which case you should extract the large fields into some meaningful entity (PersonPicture or whatever).

+3


source


It is good practice to return a different object to your client than the one you get from your database. This way you can create a ShortPerson or something that is only used as a return object in REST endpoints. It will take Person in its constructor and populate the properties you want to return to the client from this more complete object.

The benefit of this approach is actually less optimization and more that your server models will change over time, but the change can be completely independent of your API. In addition, you choose what data is publicly available, what you try to do.

As far as optimizations between db and server are concerned, I wouldn't bother with that until it's a problem.

0


source







All Articles