Objectify loads object behind Ref<?> even when #Load is not specified - java

I have an account object which references a user object.
#Cache
#Entity
public final class Account {
#Id Long id;
#Index private Ref<User> user;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public User getUser() {
return user.get();
}
public void setUser(User user) {
this.user = Ref.create(user);
}
}
I have hidden the Ref as recommended here: http://code.google.com/p/objectify-appengine/wiki/Entities - please note the Ref does not have the #Load annotation.
When I call my Google Cloud Endpoint from an Android client, it looks like Objectify delivers the account object with the embedded user, even though #Load is not specified.
#ApiMethod(name = "account.get")
public Account getAccount(
#Named("id") final Long id
) {
return ofy().load().type(Account.class).id(id).now();
}
When I query the account directly using Apis Explorer, I also get both, account with the user embedded:
200 OK
{
"id": "5079604133888000",
"user": { "id": "5723348596162560",
"version": "1402003195251",
"firstName": "Karl" },
"kind": "api#accountItem",
"etag": "\"30khohwUBSGWr00rYOZuF9f4BTE/Q31EvnQCQ6E9c5YXKEZHNsD_mlQ\""}
This raises three questions:
Does Appengine always return embedded Refs natively and does Objectify always pass on objects which it already knows?
What exactly is #Load for and is there a way to control this behavior? Load Groups?
Have I missed something? Why isn't #Load obeyed?

In your example code, you are not specifying #Load which means that loading the account will not fetch the User. However, your #ApiMethod is serializing the account back to the client, so the user property is been accessed, thus a separate fetch is issued to load the user object. That's why you are getting the information of the user when calling the method.
Not specifying #Load doesn't mean that you won't get a User back. It means that you are not going to retrieve a User unless you specifically ask for it later.
Ref works like this:
I'm a reference, so by default I won't fetch the data.
If you ask for me, then I will first load the data, then answer you.
Oh, if you tell me to #Load myself, then I will fetch the data initially and have it ready for you.
So this is working fine in your code... but then your #ApiMethod is serializing your Account object back to the client. The serialization process is going through every property in your Account object, including the user property. At this point, the Ref<User> is being accessed, so the data will get fetched from the Datastore and then returned to the client.
This is making your code very inefficient, since the Account objects are loaded without the User information, but then you always access the User info later (during serialization), issuing a separate fetch. Batching gets from the Datastore is way more efficient than issuing separate gets.
In your case, you can do either of two things:
Add #Load to the user property, so the Account object is fetched efficiently.
Make your #ApiMethod return a different Account object without the user property (thus avoiding fetching the user if you don't need it).
Option 2 above is quite useful since you can abstract your internal Datastore structure from what the client sees. You'll find yourself using this patter quite often.

Related

Why is an entity being automatically saved without calling persist when using foreign generation strategy in bidirectional one-to-one mapping?

I have been practicing one-to-one mapping in hibernate and don't understand this particular case. I have to say, the program is working fine and as I intended, but apparently I can omit a perist() call and it still works smoothly. The fact that it's working is good, but I want to know exactly why the call is optional. Let me write some details:
This is the user class, which is supposed to be the owning side of the mapping:
#Data
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE)
private Long id;
private String name;
#OneToOne
private Ticket ticket;
public User() {}
public User(String name) {
this.name=name;
}
}
And this is the ticket class that's supposed to be the dependent one:
#Data
#Entity
public class Ticket {
#Id
#GeneratedValue(generator="foreignGenerator")
#GenericGenerator(name="foreignGenerator", strategy="foreign",
parameters=#org.hibernate.annotations.Parameter(name="property", value="user"))
private Long id;
#OneToOne(optional = false, mappedBy="ticket")
#PrimaryKeyJoinColumn
private User user;
public Ticket() {
}
public Ticket(User user) {
this.user=user;
}
}
I am trying to test the "shared primary key" strategy in one-to-one mapping. As you can see I have set up the generator with foreign strategy, which is supposed to make Ticket's id the same as it's corresponding User's id.
#Bean
CommandLineRunner loadData() {
return args->{
EntityManager em=emf.createEntityManager();
em.getTransaction().begin();
User user=new User("Test User");
Ticket ticket=new Ticket(user);
//em.persist(user);
user.setTicket(ticket);
em.persist(ticket);
em.getTransaction().commit();
em.close();
//We don't have to call persist on user
};
}
}
This program runs perfectly. Uncommenting the line which calls persist on user makes no difference. I am assuming that persisting ticket, which has it's user property set, automatically saves the user as well. Therefore, the reason it makes no difference is that no matter if user is getting saved or not, it will get persisted when we call ticket.
I want to know if my assumption is correct and any additional links to articles/documentation would be greatly appreaciated. Especially I am wondering about this part that I said above-"I am assuming that persisting ticket, which has it's user property set, automatically saves the user as well." I couldn't find anything that would confirm or deny this. I know that the "shared primary key" approach in one-to-one mapping is the only use case of "foreign" generation strategy, so there are not a lot of posts about it, and whatever posts are there are getting overshadowed by "foreign key" during the search.
Any help regarding this or any other issue that might be wrong with the code provided above would be appreciated. Thanks for taking your time to read this
The JPA specification states this behavior is wrong:
Looking at the 3.0 release:
section "3.2.2. Persisting an Entity Instance" implies user is unmanaged after your persist (you can check with the em.contains method).
Section "3.2.4. Synchronization to the Database" covers the flush/commit which states:
• If X is a managed entity, it is synchronized to the database.
..
◦ For any entity Y referenced by a relationship from X, where the relationship to Y has not been annotated with the cascade element value cascade=PERSIST or cascade=ALL:
▪ If Y is new or removed, an IllegalStateException will be thrown by the flush operation (and the transaction marked for rollback) or the transaction commit will fail.
User is new, so this should be resulting in an exception. That it works might be a glitch in how Hibernate is handling the #PrimaryKeyJoinColumn annotation (speculation on my part) and custom "foreignGenerator".
This is not a pattern I'd suggest you rely on, and should instead call persist to avoid inconsistencies with the behavior on other mapping setups.

Ignore fields from being passed on in Json Object

I have a POJO called User which is also being used for inserting documents in MongoDb.
#Data
#Document(collection = Constants.COLLECTION_USERS)
public class User {
public ObjectId _id;
public String userID;
public String email;
public String name;
public String sex;
public String dob;
public String photo;
//have more variables
}
I have a simple application where a user registers by giving in a subset of data listed in the User class. The signature of the register method in my controller is as follows.
public GenericResponse registerUser(#RequestBody User userRegistrationRequest)
It can be noticed that I am using the same POJO for the registration request. Until now everything is fine. I can persist the data user object just fine.
This registration API is just used to persist a small set of a user's data. There would be other information as well in the MongoDb document, which would be accessed/persisted from some other APIs.
Suppose a user has registered with the basic information and also has persisted other information via APIs other than the registration one.
How would I make an API which can just get me some selective data from the User document again using the same User Pojo? If I call the repository to give data for a specific userID, it will give me the whole document mapped to the User class. I don't want my API to give all the information stored in the document.
One approach is to make another POJO with the details I want, and map the information selectively using a Converter. But, I want to avoid this approach, as I want to use the same class.
Second approach: Modify the Mongo query to return data selectively as given in the docs. But here I would have to specify all the fields I want in the result set. This would again be a length query.
Is there a better way to filter out data from the object?
How would I make an API which can just get me some selective data from the User document again using the same User Pojo?
How would I go off-road with a car I would like to take me girl to the restaurant at the evening? I would not - if I would have the same car for everything I would look stupid next to the restaurant, coming out in a suite or I would stuck in a swamp.
The biggest Java advantage is object creation time - you should not be afraid of it. Just create another model for registration, another as DTO for saving data, another for front-end presentation etc
Never mix responsibility of objects. You will finish with something like
#Entity
class ThePileOfShit {
#Id
private Long id;
#my.audit.framework.Id
private String anotherId;
#JsonIgnore
// just a front-end flag ignore
private boolean flag;
#Column
// not a field but getter because of any-weird-issue-you-want-to-put-here
public String getWeirdStuff() { ... }
// Useless converters
public ModelA getAsModelA() { ... }
public ModelB getAsModelB() { ... }
// etc
// etc
}
Four frameworks, five technologies - nobody knows what's going on.
If you are afraid of converting stuff use ModelMapper or another tool but keep your POJOs as simple as possible
You can use Gson's #Expose annotation only on the fields you want to return in the API.
To serialize the data, use:
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
String json = gson.toJson(userData);

PUT method (RESTful) doesn't work as a way to update resources

According to this article(http://restcookbook.com/HTTP%20Methods/put-vs-post/), PUT is supposed to work as a method to update resources.
However, practicing RESTful with JAX_RS 2.0 and Jersey 2.0, I don't think it updates a particular resource.
(I.e. I'm studying RESTful with JAX_RS 2.0 and Jersey 2.0)
Here is a resouce like this.
<customer>
<name>Before</name>
<postcode>111</postcode>
</customer>
What I'm trying to do is to update (perhaps I should say "replace") this resource.
ClientConfig config = new ClientConfig();
Client client = ClientBuilder.newClient(config);
WebTarget target = client.target("http://xxx/yyy/zzz/end.cust");
Customer cust = new Customer();
cust.setName("After");
cust.setPostcode(222);
target.path("Before").request().put(Entity.xml(cust));
#Id annotation is set to "Name" in the "Customer" class, so the path "Before" is supposed to work as the ID and the first resource (named "Before") should be replaced with the second resource (named "After").
However, after the coding above is executed, the "Before" resource still remains, and there is a new "After" resrouce.
It seems that the PUT method worked to create a new resource, instead of updating something.
(i.e. There are both "Before" and "After" resources, and nothing has been updated)
I tested a POST method in order to create a new resource, and it created a new resource as I expected.
If you see anything I'm doing wrong or what needs to be done, could you please give some advice?
edit
I'll add the server side code. The method annotated with #PUT is like this.
#PUT
#Path("{id}")
#Consumes({"application/xml", "application/json"})
public void edit(#PathParam("id") String id, Customer entity) {
super.edit(entity);
}
This is inside a class called CustomerFacadeREST.java, automatically created after I created a "RESTful service from Database".
According to NetBeans' document, super.edit() method is originally like this.
public void edit(T entity) {
getEntityManager().merge(entity);
}
In the "Customer" class, #Id is set to the "name" value in this way.
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 80)
#Column(name = "Name")
private String name;
// Other fields, such as Postcode...
public Customer() {
}
// Other constructors and methods...
}
The idea behind "HTTP Verbs" like PUT, GET, POST, DELETE are just a matter of protocol semantics. Just performing an HTTP PUT operation doesn't do anything magical. It's just proper semantics we as developers should understand, while developing, as these semantics are known to all (that's why protocols exist). If no one followed these semantics, the world would be somewhere between the Great Depression and the Apocalypse.
That being said, these verbs (semantics) are a sort of guarantee (or maybe assurance is a better word) to the client performing the request with a certain verb will have some know semantics to it. One major factor is the idea of idempotence. Idempotence is the idea that no matter how many times I make a request, the result will be the same (or have the same effect).
Certain HTTP verbs are said to be idempotent, such as PUT, DELETE, GET. No matter how many times be make the exact same request, the general idea is that the result/effect should be the same. POST on the other hand is said to not be idempotent, as the exact same POST request may produce different results, for example submit an order, wrongfully, again, or creating a new customer twice.
If we want to make the world a better place, and do our part in saving the world from a complete meltdown, we should learn these semantics and be good citizens by following them. There's a lot more to learn about the verb semantics, than just idempotence, but understanding that much, is a good start. I'd suggest maybe picking up a good book on REST to learn some good practices. Or if you want you want to be a cool kid, take time to read the bible (actually the Fielding Dissertation).
All that being said, it's our job as developers to create the code to follow these semantics. The reason your method is creating a new resource, is probably because you are creating a new resource with your code. Maybe something like this would seem more appropriate:
#PUT
#Path("/customers/{id}")
#Consumes(MediaType.APPLICATION_JSON)
public Response updateCustomer(#PathParam("id") long id,
Customer updateCustomer) {
Customer customer = customerService.getCustomerById(id);
if (customer == null) {
throw new WebApplicationException("Can't find it", 404);
}
customer.setFirstName(updateCustomer.getFirstName());
customer.setLastName(updateCustomer.getLastName());
...
return Response.noContent().build();
}
So we are just update the customer that already exists in our database. Normally with a PUT request to update, the particular customer resource URI should be known. So say the client makes a request to http://blah.com/api/customers/1234, our service will look up the customer with the id 1234. If it can't be found, we return a 404 status code, as the resource doesn't exist. If it does exist, then we update the customer with the customer data provided in the request. If you wanted to create a new customer, where the URI is not known, then POST would be correct, and you'd send a customer representation to http://blah.com/api/customers.
Also keep just an FYI: in many cases a case like this, what happens is that the client requests (GET) a resource, say a customer, and updates that customer representation, then send it back as PUT request with the updated customer. On the sever it should use that information to update the particular customer's data, as you can see from the example above.
UPDATE
Per your edit. You are completely missing the point of how this is supposed to work.
Customer cust = new Customer();
cust.setName("After");
cust.setPostcode(222);
target.path("Before").request().put(Entity.xml(cust));
What's wrong with this is that with the new Customer, you are setting the identifier to "After", which is different from the identifier in the request path, you are using "Before". So the path variable {id} is "Before". With this request URI you are saying that you want to access the customer with id "Before". As seen in my code, it's your duty to check if a customer with the id "Before" exists in the database. If not, you should return back a 404 Not Found. The name (id) you set for the new Customer should be the id expected in the database. So if you want to update the customer with id in the databse "After". then you should put "After" in the path, instead of "Before". We should not try and change the identifier.
Like I said, when we want to update a resource, we normally, GET the resource, update some field (but not the identifier), and send it back. A sequence might look something like
final String PATH = "http://hello.com/api/customers"
WebTarget target = client.target(PATH);
Customer customer = target.path("1234").request().get(Customer.class);
// where 1234 is the id (or in your case `name` of the customer.
// I would avoid using the name as the DB id, that's why my example uses numbers
customer.setPostalCode(...);
target = client.target(PATH).path(customer.getName()); // getName should be 1234
Response response = target.request().put(Entity.xml(customer));
We are using the same id as we were provided with, in the path, because that is the how the resource is identified in the server.

Is Spring Data Mongodb, how can I reference one document from another without going via IDs

I'm using MongoDB with Spring Data. I'd like to have one document reference another (a user in fact), but I end up with having to do extra work. E.g.
class Watch {
String id;
User user;
}
That's nice, but I seem to end up with the whole user embedded in the document, so I do this:
class Watch {
String id;
String userId;
}
But then I want to use it in some JSTL, and I want to do this:
${watch.user.email}
But I have to add some mapping code.
Use #DBRef annotation on user.
You need to save user separately (no cascading), but you probably want to do that.
Beware that user will be loaded eagerly.

Objectify + JSP: displaying 1:N relationships

My bean looks like that:
#Entity
public class Fattura {
#Id
Long id;
#NotEmpty
String numero;
#Min(value=0)
Double importo;
Key<User> utente;
// gets & sets....
}
The "utente" property is the key of another bean I created: a "Fattura" can have only one "User", one "User" can have many "Fattura"s
My Spring MVC controller will manage a request for a list of Fattura and display them in a simple jsp:
#RequestMapping( value = "/fatture" , method = RequestMethod.GET )
public ModelAndView leFatture() {
ModelAndView mav = new ModelAndView("fatture");
mav.addObject("fatture",fatturaService.listFatture());
return mav;
}
the code of the jsp is really simple: only a foreach cycle in a table
My question is:
how can I display the "utente"?
The only thing I have is its key, but I'd like to do something like ${fattura.utente.firstName} in my JSP, how can I do it?
Unfortunately you would have to manually fetch "utente" in your DAO class. There is no automatic fetching in Objectify like in Twig. In my POJOs I have following fields
#Transient private Organization sender; // Pickup location (for client RPC)
transient private Key<Organization> senderKey; // Pickup location (for Datastore)
I load entity from Datastore and then load manually Organization using senderKey.
In new Objectify4 you'll be able to do what you want like this:
class Beastie {
#Parent
#Load
ParentThing parent;
#Id Long id;
#Load({"bigGroup", "smallGroup"})
SomeThing some;
#Load("bigGroup")
List<OtherThing> others;
#Load
Ref<OtherThing> refToOtherThing;
Ref<OtherThing> anotherRef; // this one is never fetched automatically
}
Here is evolving design document of new version.
Update at Nov 17, 2011: This is big news. Twig author, John Patterson, joined Objectify project today.
I know it sounds annoying that you have to manually fetch the two objects, but it's actually very useful to know that you're doubling your work and time to do this - each "get" call take a while and the second won't start until the first is complete. It a typical NoSQL environment, you shouldn't often need to have two separate entities - is there a reason that you do?
There are only two reasons I can easily think of:
The class references another object of the same type - this is the example in the Objectify documentation, where a person has a reference to their spouse, who is also a person.
The class that you're embedding the other into ("Fattura" in your case) has masses of data in it that you don't want fetched at the same time as you want to fetch the "User" - and you need the user on it's own more often than you need the "Fattura" and the "User". It would need to be quite a lot of data to be worth the extra datastore call when you DO want the "Fattura".
You don't necessarily have to use temporary field for just getting a object.
This works:
public User getUtente() {
Objectify ofy = ObjectifyService.begin();
return ofy.get(utenteKey);
}
This will of course do a datastore get() each time the getter is called. You can improve this by using #Cached on your User entity, so they turn into memcache calls after the first call. Memcache is good, but we can do a little better using the session cache:
public User getUtente() {
Objectify ofy = myOfyProvider.get();
return ofy.get(utenteKey);
}
The key thing here is that you need to provide (through myOfyProvider) an instance of Objectify that is bound to the current request/thread, and that has the session cache enabled. (ie, for any given request, myOfyProvider.get() should return the same instance of Objectify)
In this setup, the exact same instance of User will be returned from the session cache each time the getter is called, and no requests to the datastore/memcache will be made after from the initial load of this Entity.

Categories