问题
I have a simple Play applicaiton for test. I have 2 controllers, one is a "ListController" that displays a list of entries. Each entry has an "edit" link which goes to the "EditController". The "EditController" diplays the html form with the existing data for the entry, and the submit button posts that data towards another method in the "EditController", which saves the data to db and redisplays the newly modify data in the form. All this works ok. However once I modify an entry (which is redisplayed correctly in the edit page) when I return to the list, that entry still has the old values, and editing it still shows it with the old values. This happends both in test and dev environment.
Here's my setup:
- initialize data from data.yml file, via
@OnApplicationStart
annotated BootStrap class test environment configured with :
%test.db=fs
%test.jpa.ddl=create
- dev environment configured with:
db=postgres://postgres:postgre@localhost:5432/play
ListController:
List laundryLists = LaundryList.findAll();
-EditController
LaundryList updatedLaundryList = LaundryList.em().merge(laundryList);
I've added log output and sure enough, after calling the merge
method in the EditController, even if I add code to retrieve the updated entity from the database, it returns a bean with correctly updated values such as how I inserted them
However, even after this, in either test (play test) or run (play run) environmetns once I retype (or click the link) for the ListCOntroller, it still displays that (supposedly) modified entry with the values it had BEFORE the update happend in the EditController. And if I click to edit it again, the form shows me THAT initial-never-modified data for the entry.
My question is, what should I actually do to have the data persisted to the database?
回答1:
I found the solution but this is very disappointing.
In short, what I did is replace this line:
LaundryList.em().merge(laundryList);
with this:
LaundryList updateLaundryList = LaundryList.em().merge(laundryList);
updateLaundryList.save();
The long answer is: Play changed the way JPA actually works. In the above case I was in a transaction (this was clearly shown by the logs and the fact that if I tried to start a transaction an exception would be raised telling me I'm in fact in an already running transaction). However, even so, doing a em().merge
did not in fact do any inserts/updates towards the db, as show in the logs.
Yes, at some point on their site they do say something on the lines of "we don't like how JPA auto-synchronizez data when you're in a transaction so you don't need to call persist/merge, but you do need to call refresh of you infact want to undo your changes, and we don't like that, so we reversed it: now you have to save explicitly, or nothing gets updated".
They also say (and here I found the actual quote):
"During successive requests, retrieve the object from the database using the object ID, update it, and save it again."
But that's it. No code examples no nothing. Since they'd changed the behavior of a framework which is A LOT more known than Play (namely JPA), they should really specify this more clearly, or at least change the class names, make wrappers, say they made their own PlayPersistenceManager or something.
And no, personally I don't think their way is better. On the whole you are A LOT more likely to persist/update something than you are to undo some changes, so it's better if the persist/update happends automatically (with no boiler plate code) and you need to explicitlly do an undo.
来源:https://stackoverflow.com/questions/8142392/play-framework-merge-action-only-works-in-the-controller-that-triggered-it