Error: detached entity passed to persist - try to persist complex data (Play-Framework)

后端 未结 4 1749
无人共我
无人共我 2021-02-04 12:57

I have a problem with persisting data via play-framework. Maybe it\'s not possible to achive that result, but it would be really nice if it would work.

Simple

相关标签:
4条回答
  • 2021-02-04 13:11

    According to Play documentation you should provide a query string like the following:

    ?shop.addresses[0].id=123
    &shop.addresses[1].id=456
    &shop.addresses[2].id=789
    

    I am not sure whether you provide it correctly. Try this:

    <input type="hidden" name="shop.addresses[${address_index - 1}].id" value="${address.id}"/>
    
    0 讨论(0)
  • 2021-02-04 13:28

    This will not make you happy. I've stubmled over the same error, was about to pose the same question on SO and saw yours. This thing doesn't work, it's a major bug. In the docs I've found "since it could be tedious to explicitly call save() on a large object graph, the save() call is automatically cascaded to the relationships annotated with the cascade=CascadeType.ALL attribute." but it simply doesn't work.

    I've even debugged the SQL, what happends to your (and my) associations is that they are deleted and re-associated to the parent, but they're never updated with the new values. It's something like this:

    //Here's it's updating a LaundryList where I've modified the address and one of the laundry items
    //it first updates the simple values on the LaundryList:
    update LaundryList set address=?, washDate=? where id=?
    binding parameter [1] as [VARCHAR] - 123 bong st2
    binding parameter [2] as [DATE] - Fri Mar 11 00:00:00 CET 2011
    binding parameter [3] as [BIGINT] - 413
    
    //then it deletes the older LaundryItem:
    delete from LaundryList_LaundryItem where LaundryList_id=?
    binding parameter [1] as [BIGINT] - 413
    binding parameter [2] as [BIGINT] - 407
    
    //here it's associating the laundry list to a different laundry item
    insert into LaundryList_LaundryItem (LaundryList_id, laundryItems_id) values (?, ?)
    binding parameter [1] as [BIGINT] - 413
    binding parameter [2] as [BIGINT] - 408
    
    //so where did you issue the SQL that adds the updated values to the associated LaundryItem?? 
    

    I saw your workaround and I honestlly appreciate your effort, and the fact that you took the trouble to post it (as it will help people who are stuck with this) but this defeats the purpose of Rapid Development and ORM. If I'm forced to do some operations that normally should be automatic (or not even necessary, why is it deleting the old assication and associating the parent to a new one instead of simply updating that association in one go?) then it's not really "rapid" nor valid "object relational mapping".

    As far as I'm concerned, they've took a perfectlly working framework (JPA) and turned it into something useless by modifiying its behaviour. In this case this refferes to the fact that JPA's merge call no longer does what it's supposed to, they tell you they've added they're own method called "save" which helps with that (why???) and that thing doesn't work the way it's described in the docs and examples on their site (more on this in this question I've posted.

    UPDATE:

    Well, here's my workaround as well:

    I now simply ignore to send in the ids of the updated associations to the controller, this way Play will think they're new entities to be added to the db, and when calling merge(...) and save() on their parent entity all daya will be saved correctlly. This however gets you another error: Since now, each time you modify some associations and save the parent, those associations are treated as new entities to be created (they have id=null), and thus the old ones are orphaned from their parent when saving all this, which either leaves you with a big bunch of orphaned, useless entities in the db, or forces you to write even more workaround code to clear those orphaned associated entities on a parent entity you're about to save.

    UPDATE 2:

    At this point, I think it's best to wait for Play 2.0, which is currentlly in Beta and will be launched soon. This is not too cool, as, to quote monsieur Bort (from memory) "you will not be able to migrate your Play 1.x projects to Play 2 directlly, but some light code copy-paste should suffice to transfer them". Copy/paste your code for the win! And to think how much time other framework makers spend making their new product version backwards compatible ! At any rate, in their article introducing the Play 2.0 roadmap, they say that they'll replace Hibernate/JPA with some other ORM framework, and at the same time admit that they've hacked the standard JPA implementation done by Hibernate in order to achieve... well, we all saw what that achieved. Here's the quote:

    "Today, the preferred way for a Play Java application to access an SQL database is the Play Model library that is powered by Hibernate. But since it's annoying in a stateless Web framework like Play to manage stateful entities such as the ones defined in the official JPA specification, we have provided a special flavor of JPA keeping things as stateless as possible. This forced us to hack Hibernate in a way that is probably not sustainable in the long term. To address this, we plan to move to an existing implementation of stateless JPA called EBean."

    This could be potential good news, as the new ORM seems more suited to their requirements, and is more likely to avoid the bad things they have now. I wish them all the luck.

    0 讨论(0)
  • 2021-02-04 13:37

    When you update a complex instance in Hibernate you need to make sure it is coming from the database (fetch it first, then update that same instance) to avoid this 'detached instance' problem.

    I generally prefer to always fetch first and then only update specific fields I'm expecting from the UI.

    You can make your code a bit more generic using

    (T)Model.Manager.factoryFor(relation.getClass()).findById(relation.id);
    
    0 讨论(0)
  • 2021-02-04 13:37

    Not sure this is the answer, because I don't know about Play, but the Shop-Address bidirectional association is not correct: The shop side must be marked as the inverse of the other side, using @OneToMany(mappedBy="shop", ...).

    Also, if save and merge corrspond to Session.save and Session.merge respectively, doing a save after a merge makes no sense. Save is used to insert a new, transient entity into the session. If merge has been called, it's already persistent at the time save is called.

    0 讨论(0)
提交回复
热议问题