Let's say I have a ThingProxy which has a GizmoProxy (and other stuff):
interface ThingProxy {
GizmoProxy getGizmo();
void setGizmo(GizmoProxy gizmo);
... other getters and setters ...
}
I need an instance of ThingProxy everywhere so I keep one available.
At some point I need to update something in its GizmoProxy. And that's
when the fun starts...
I see two approaches and neither works. :-(
First approach:
ThingProxy thing = ...;
ThingRequest thingRequest = RequestBuilder.newThingRequest();
ThingProxy mutableThing = thingRequest.edit(thing);
GizmoProxy mutableGizmo = mutableThing.getGizmo();
mutableGizmo.setSomething(...);
thingRequest.persist(mutableThing).fire(... receiver ...);
Unfortunately, this doesn't save the changes in the GizmoProxy.
Apparently, RF sees that the ThingProxy hasn't changed so it does
nothing (at least, nothing with the GizmoProxy).
Second approach:
ThingProxy thing = ...;
GizmoRequest gizmoRequest = RequestBuilder.newGizmoRequest();
GizmoProxy mutableGizmo = gizmoRequest.edit(thing.getGizmo());
mutableGizmo.setSomething(...);
gizmoRequest.persist(mutableGizmo).fire(... receiver ...);
This works fine and saves the change in GizmoProxy. But now ThingProxy
is out of date and I can't call setGizmo because the ThingProxy is
frozen. I tried using a mutable ThingProxy but then I get errors later
on (object is being edited in a different context).
How am I supposed to do this?
Cheers,
Hilco
Sure, lots of them (user = ThingProxy, basicUser = GizmoProxy [the
level should change from 0 to 1 in basicUser]):
{
"F":"com.company.domain.request.api.DomainRequest$Builder",
"O":
[
{
"T":"MUhstLEy3myftqjcUSbJiXK7UTM=",
"P":
{
"balanceDelta":1000,
"currentBalance":1000,
"currencyType":
{
"T":"2SkAFKlbopWmIXRi$HD1bTQ3ppo=",
"S":"Mi4w"
},
"comment":"Level Up to Level 1.",
"user":
{
"T":"XsxUVV_IoQWMCnw5Bk9oWk4mU$g=",
"S":"NS4w"
}
},
"C":1,
"R":"1",
"O":"PERSIST"
},
{
"T":"XsxUVV_IoQWMCnw5Bk9oWk4mU$g=",
"V":"MS4w",
"P":
{
"basicUser":
{
"T":"KrJKdAdDPLkbzJh$UCC4PAyD9tQ=",
"S":"NS4w"
},
"friendsGroup":
{
"T":"dPtqVUr1qWygjnZOjOHwDI3E_6c=",
"S":"Ni4w"
}
},
"S":"NS4w",
"O":"UPDATE"
},
{
"T":"KrJKdAdDPLkbzJh$UCC4PAyD9tQ=",
"V":"MS4w",
"P":
{
"account":
{
"T":"Inde8rw1dKVFojfvX8WlUbtX84M=",
"S":"NS4w"
}
},
"S":"NS4w",
"O":"UPDATE"
},
{
"T":"Inde8rw1dKVFojfvX8WlUbtX84M=",
"V":"MS4w",
"S":"NS4w",
"O":"UPDATE"
},
{
"T":"dPtqVUr1qWygjnZOjOHwDI3E_6c=",
"V":"MS4w",
"P":
{
"groupType":
{
"T":"PZiZQZ4KxLWrDDpldtcde_S3dQE=",
"S":"MS4w"
}
},
"S":"Ni4w",
"O":"UPDATE"
},
{
"T":"PZiZQZ4KxLWrDDpldtcde_S3dQE=",
"V":"MS4w",
"S":"MS4w",
"O":"UPDATE"
}
],
"I":
[
{
"P":
[
{
"T":"MUhstLEy3myftqjcUSbJiXK7UTM=",
"R":"1",
"C":1
}
],
"O":"xWeECnR2JMgNVkoxRJcKT3orPAE="
}
]
}
For the record, my UserService *is* being called, just not the
BasicUserService. It really should be the other way around.
Because BasicUserProxy is part of UserProxy.
> It's hard to follow what you're expecting given that you switched from
> thing/gizmo/setSomething to [something-else]/user/basicUser/set[what?]
I wanted to keep it generic but see below.
> Could you post the actual code that led to the request whose payload you
> posted already?
Here you go:
final UserRequest userRequest =
domainRequestApi.getRequestBuilder().newUserRequest();
final UserProxy mutableUser =
userRequest.edit(domainRequestApi.getCache().getCurrentUser());
final BasicUserProxy mutableBasicUser = mutableUser.getBasicUser();
final int level = mutableBasicUser.getLevel();
final int basicCurrencyDelta = ...;
final int premiumCurrencyDelta = ...;
final int newLevel = level + 1;
final String comment = "Level Up to Level " + newLevel + ".";
mutableBasicUser.setLevel(newLevel);
final Receiver<Integer> levelUpReceiver = new LevelUpReceiver(...,
mutableUser, comment, basicCurrencyDelta, premiumCurrencyDelta);
userRequest.persist(mutableUser).fire(levelUpReceiver);
LevelUpReceiver's onSuccess simply fires some events, including one
that sends out the by then presumably updated UserProxy (hence the
mutableUser in the constructor).
Given that BasicUserProxy is part of UserProxy, I would expect its
changes to be stored, i.e. I expect the entire object graph that is
UserProxy to be stored (as necessary).
Just to be complete:
@ProxyForName(value = "com.company.domain.api.User", locator =
"com.company.domain.service.api.UserService")
public interface UserProxy extends EntityProxy
{
BasicUserProxy getBasicUser();
void setBasicUser(BasicUserProxy basicUser);
... a few others ...
}
@ProxyForName(value = "com.company.domain.api.BasicUser", locator =
"com.company.domain.service.api.BasicUserService")
public interface BasicUserProxy extends EntityProxy
{
int getLevel();
void setLevel(int level);
... a few others ...
}
In very short pseudo code, what I want to happen is the following:
User.BasicUser.Level := User.BasicUser.Level + 1;
Persist User (or BasicUser, I don't care);
Make sure that User.BasicUser.Level == BasicUser.Level, i.e. User's
BasicUser should be the newly updated one.
(I've tried persisting User but then BasicUser's changes are ignored;
persisting BasicUser works but then User references an outdated
BasicUser; I tried using the Editor framework but that didn't make any
difference either.)
On 30 September 2011 00:54, Thomas Broyer <t.br...@gmail.com> wrote:
> Why would BasicUserService ( = GizmoService?) be called if you invoke
> UserRequest#persist(UserProxy) ( = ThingRequest#persist(ThingProxy) ?)Because BasicUserProxy is part of UserProxy.
> It's hard to follow what you're expecting given that you switched from
> thing/gizmo/setSomething to [something-else]/user/basicUser/set[what?]I wanted to keep it generic but see below.
> Could you post the actual code that led to the request whose payload you
> posted already?Here you go:
final UserRequest userRequest =
domainRequestApi.getRequestBuilder().newUserRequest();
final UserProxy mutableUser =
userRequest.edit(domainRequestApi.getCache().getCurrentUser());
final BasicUserProxy mutableBasicUser = mutableUser.getBasicUser();
final int level = mutableBasicUser.getLevel();
final int basicCurrencyDelta = ...;
final int premiumCurrencyDelta = ...;
final int newLevel = level + 1;
final String comment = "Level Up to Level " + newLevel + ".";
mutableBasicUser.setLevel(newLevel);
final Receiver<Integer> levelUpReceiver = new LevelUpReceiver(...,
mutableUser, comment, basicCurrencyDelta, premiumCurrencyDelta);
userRequest.persist(mutableUser).fire(levelUpReceiver);
Given that BasicUserProxy is part of UserProxy, I would expect its
changes to be stored, i.e. I expect the entire object graph that is
UserProxy to be stored (as necessary).
:-) I apologise for "comment", that's being done by a separate event.
The JSON you see contains two proxies going to the server. I didn't
want to change the JSON as I was not clear on the exact dependencies.
I've wondered about the missing "level":1 as well. I know I've seen it
once but that may have been when I was persisting BasicUser directly.
>> Given that BasicUserProxy is part of UserProxy, I would expect its
>>
>> changes to be stored, i.e. I expect the entire object graph that is
>> UserProxy to be stored (as necessary).
>
> What do you mean exactly by "stored"? What RequestFactory will do on the
> server side is:
I mean persisting it in the database.
> get User and BasicUser (and others, as needed) by their ID
> "apply the diff" from the request to these objects, by calling the
> appropriate setters.
> call the service methods; here, call UserService#persist(User) with the User
> instance modified above
Yeah, I had assumed it would call BasicUserService#persist(BasicUser) as well.
> So actually persisting the "entire object graph" is up to the
> UserService#persist(User) method; as the doc says:
> “RequestFactory automatically sends the whole object graph in a single
> request. In this case, the implementation of Person.persist() on the server
> is responsible for persisting the related Address also, which may or may not
> happen automatically, depending on the ORM framework and how the
> relationship is defined.” (the part about @Embedded objects not being
> supported is no longer true since GWT 2.1.1 and the introduction of
> ValueProxy; but that doesn't apply here)
> — http://code.google.com/webtoolkit/doc/latest/DevGuideRequestFactory.html#relationships
All right, I guess I was expecting too much.
> So, is your problem that BasicUser is not persisted? or that the
> User#getBasicUser() passed to the persist() method doesn't have its level
> modified?
> Or is your issue that domainRequestApi.getCache().getCurrentUser() doesn't
> reflect the change? In that case, then unless you, somewhere, setCurrentUser
> with the "mutableUser", that's normal: immutable proxies are never updated,
> they're immutable snapshots.
I have User cached and User contains BasicUser. I want BasicUser
updated on the server and I want that change reflected in User (so
User.getBasicUser().getLevel() should yield the new level). This seems
impossible without rerequesting the entire User object graph from the
server. That's very expensive and totally unnecessary (in the sense
that all information is already available on the client). But I don't
see how I can "update" the cached User with the "new" BasicUser. I
can't call setBasicUser on User because it's frozen.
So given what you've told me above, my question becomes: how do I
update User after persisting BasicUser? Without rerequesting the whole
User object graph from the server, of course. This must be easy but I
don't see how to do it. :-(
:-) Yes, but then BasicUser doesn't get persisted which was the whole
point of the operation.
And server side [i.e. in UserService#persist(User)] I have no idea
what changed in User (only RF would know that). I really don't want to
have save the entire User graph just to update one integer.
What I've got working now is persisting BasicUser and requesting User
from the server afterwards. Very expensive and wasteful but at least
that works. Surely there must be a better way?