[Lift] Re: Trouble with lift, GAE, JPA, adding child records
Let me look. To save some time, can you give me an order of operations that result in the error that you're seeing? Thanks, Derek On Tue, May 26, 2009 at 5:47 PM, Naftoli Gugenheim naftoli...@gmail.comwrote: I guess it's a DataNucleus thing. Here's my project. -- From: Derek Chen-Becker dchenbec...@gmail.com Sent: Tuesday, May 26, 2009 3:09 PM To: liftweb@googlegroups.com Subject: [Lift] Re: Trouble with lift, GAE, JPA, adding child records The merge method really should be able to handle this, so could you provide some code that shows how you're handling the entity in the two different requests? I'm not sure what you mean by transient, either. JPA entities are either in an attached or detached state; AFAIK there is no third state. Derek On Mon, May 25, 2009 at 2:04 PM, Naftoli Gugenheim naftoli...@gmail.comwrote: After corresponding on the google-appengine-java Google Group, (actually after reading something in another thread), I realized what's causing the problem. For some reason, committing the transaction puts the entity into detached state--but closing the EM makes it transient, so in the next request it's persisting a transient object, which means that it's creating a new record--but it chokes on the fact that the PK is set to a Key with the id already set. So really it boils down to a JPA question--how do I get it to stay in detached state? Or, if I switch to JDO (which has explicit detaching control), how much work is it to not have ScalaJPA (or to write a ScalaJDO)? -- From: Derek Chen-Becker dchenbec...@gmail.com Sent: Tuesday, May 19, 2009 5:43 PM To: liftweb@googlegroups.com Subject: [Lift] Re: Trouble with lift, GAE, JPA, adding child records I wonder if that's something specific to GAE. Typically what I do using the Hibernate EM is something like: val current = myAuthor // this entity has either been passed in or retrieved bind (author, xhtml, obj - hidden(() = authorVar(current)), ...) Then the actual instance is passed across the session instead of being retrieved each time. Obviously, if the entity is very large it's more efficient to do it by some other mechanism. Instead of doing a find I would do a load based on the id, since that will return a proxy instance that should track modifications. Derek On Tue, May 19, 2009 at 3:07 PM, ngug naftoli...@gmail.com wrote: I actually started my code by editing the AuthorOps snippet from ymnk's GAE version of it. He does reload the nature before editing it: // Hold a val here so that the id closure holds it when we re-enter this method bind(author, xhtml, id - SHtml.hidden(() = findAuthor(author) match { case Some(a) = authorVar(a) case None =}), name - SHtml.text(author.name, author.name=_), submit - SHtml.submit(?(Save), doAdd)) findAuthor looks up the the author based on the id in the authorVar RequestVar, and then sets that back into the authorVar. I wondered why he did that, and then thought I understood... Thanks! On May 19, 5:01 pm, Derek Chen-Becker dchenbec...@gmail.com wrote: On the first part, I could add a withTx method to LocalEM that would handle wrapping the tx for you, if that's OK. Something like: def withTx[T] (f : = T) Then you could do MyEM.withTx { prog } And it would handle the rollback. How does that sound? As for the second, if you're getting an exception on the merge I'd really like to see the Exception (and code, if possible). Off the top of my head the only time that should happen is if you have a constraint violation or something similar that would throw and exception even if you were operating on a non-detached entity. Take a look at the JPA Demo Library app. It does detached object merge all of the time for editing authors and books. Derek On Tue, May 19, 2009 at 2:42 PM, ngug naftoli...@gmail.com wrote: On May 19, 4:31 pm, Derek Chen-Becker dchenbec...@gmail.com wrote: It should already. The closeEM method on both LocalEM and JndiEM checks to see if the transaction has been marked rollback only and should handle committing/rollback there. If it doesn't then it's a bug. But we're talking about userTx==true. I mean this: private def transaction(prog: =Unit) { val tx = EntityManager.getTransaction try { tx.begin prog tx.commit } finally { if(tx.isActive) tx.rollback } } ... def get = transaction { val l = NatureLocationType.lookup(loc.id) l.name = stringValues(name) l.allowStreet = booleanValues(allowStreet) l.allowHospital = booleanValues(allowHospital) l.allowDoctor = booleanValues(allowDoctor) l.allowEquipment = booleanValues(allowEquipment
[Lift] Re: Trouble with lift, GAE, JPA, adding child records
The merge method really should be able to handle this, so could you provide some code that shows how you're handling the entity in the two different requests? I'm not sure what you mean by transient, either. JPA entities are either in an attached or detached state; AFAIK there is no third state. Derek On Mon, May 25, 2009 at 2:04 PM, Naftoli Gugenheim naftoli...@gmail.comwrote: After corresponding on the google-appengine-java Google Group, (actually after reading something in another thread), I realized what's causing the problem. For some reason, committing the transaction puts the entity into detached state--but closing the EM makes it transient, so in the next request it's persisting a transient object, which means that it's creating a new record--but it chokes on the fact that the PK is set to a Key with the id already set. So really it boils down to a JPA question--how do I get it to stay in detached state? Or, if I switch to JDO (which has explicit detaching control), how much work is it to not have ScalaJPA (or to write a ScalaJDO)? -- From: Derek Chen-Becker dchenbec...@gmail.com Sent: Tuesday, May 19, 2009 5:43 PM To: liftweb@googlegroups.com Subject: [Lift] Re: Trouble with lift, GAE, JPA, adding child records I wonder if that's something specific to GAE. Typically what I do using the Hibernate EM is something like: val current = myAuthor // this entity has either been passed in or retrieved bind (author, xhtml, obj - hidden(() = authorVar(current)), ...) Then the actual instance is passed across the session instead of being retrieved each time. Obviously, if the entity is very large it's more efficient to do it by some other mechanism. Instead of doing a find I would do a load based on the id, since that will return a proxy instance that should track modifications. Derek On Tue, May 19, 2009 at 3:07 PM, ngug naftoli...@gmail.com wrote: I actually started my code by editing the AuthorOps snippet from ymnk's GAE version of it. He does reload the nature before editing it: // Hold a val here so that the id closure holds it when we re-enter this method bind(author, xhtml, id - SHtml.hidden(() = findAuthor(author) match { case Some(a) = authorVar(a) case None =}), name - SHtml.text(author.name, author.name=_), submit - SHtml.submit(?(Save), doAdd)) findAuthor looks up the the author based on the id in the authorVar RequestVar, and then sets that back into the authorVar. I wondered why he did that, and then thought I understood... Thanks! On May 19, 5:01 pm, Derek Chen-Becker dchenbec...@gmail.com wrote: On the first part, I could add a withTx method to LocalEM that would handle wrapping the tx for you, if that's OK. Something like: def withTx[T] (f : = T) Then you could do MyEM.withTx { prog } And it would handle the rollback. How does that sound? As for the second, if you're getting an exception on the merge I'd really like to see the Exception (and code, if possible). Off the top of my head the only time that should happen is if you have a constraint violation or something similar that would throw and exception even if you were operating on a non-detached entity. Take a look at the JPA Demo Library app. It does detached object merge all of the time for editing authors and books. Derek On Tue, May 19, 2009 at 2:42 PM, ngug naftoli...@gmail.com wrote: On May 19, 4:31 pm, Derek Chen-Becker dchenbec...@gmail.com wrote: It should already. The closeEM method on both LocalEM and JndiEM checks to see if the transaction has been marked rollback only and should handle committing/rollback there. If it doesn't then it's a bug. But we're talking about userTx==true. I mean this: private def transaction(prog: =Unit) { val tx = EntityManager.getTransaction try { tx.begin prog tx.commit } finally { if(tx.isActive) tx.rollback } } ... def get = transaction { val l = NatureLocationType.lookup(loc.id) l.name = stringValues(name) l.allowStreet = booleanValues(allowStreet) l.allowHospital = booleanValues(allowHospital) l.allowDoctor = booleanValues(allowDoctor) l.allowEquipment = booleanValues(allowEquipment) } (continued below) Also, for your first question: (I take that to mean that setting entity properties does not require a transaction? I thought that in JPA entities are generally monitored for modifications? Is that a mistake?) In the context of a form processing function, the JPA entity is in a detached state at the point that its members are being updated because the session that it was loaded in (when the form rendered) is now
[Lift] Re: Trouble with lift, GAE, JPA, adding child records
Sorry for the delay in responding. My question was not about transaction API, my question was a practical one: If form processing is split into a bunch of little anonymous functions, how can you put them in a try/finally? On May 15, 1:59 pm, Derek Chen-Becker dchenbec...@gmail.com wrote: EntityManager has a getTransaction method that you can use along with the constructor I mentioned previously to explicitly begin and end transactions in your code. derek On Fri, May 15, 2009 at 11:45 AM, ngug naftoli...@gmail.com wrote: One entity group per transaction is a GAE requirement. But my question about handling transactions was more specific - because form processing is specified piecemeal in separate closures, you can't use the normal try/finally. It seems a little too much effort to create a hidden field before and after every form/subform. Thanks. P.S. Sorry about the triple post. Opera Mobile (well, Google) didn't tell me the post was successful like it usually does, so I thought it didn't go through. (I thought the list is moderated and you would filter the extras just in case.) --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: Trouble with lift, GAE, JPA, adding child records
Well, typically the form callbacks are just setting values. If they're more complex than that then you would need a try/catch in each function. I don't think that the current form processing code has any facility for a per-form exception handler, although that might be a useful feature. Derek On Tue, May 19, 2009 at 1:09 PM, ngug naftoli...@gmail.com wrote: Sorry for the delay in responding. My question was not about transaction API, my question was a practical one: If form processing is split into a bunch of little anonymous functions, how can you put them in a try/finally? On May 15, 1:59 pm, Derek Chen-Becker dchenbec...@gmail.com wrote: EntityManager has a getTransaction method that you can use along with the constructor I mentioned previously to explicitly begin and end transactions in your code. derek On Fri, May 15, 2009 at 11:45 AM, ngug naftoli...@gmail.com wrote: One entity group per transaction is a GAE requirement. But my question about handling transactions was more specific - because form processing is specified piecemeal in separate closures, you can't use the normal try/finally. It seems a little too much effort to create a hidden field before and after every form/subform. Thanks. P.S. Sorry about the triple post. Opera Mobile (well, Google) didn't tell me the post was successful like it usually does, so I thought it didn't go through. (I thought the list is moderated and you would filter the extras just in case.) --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: Trouble with lift, GAE, JPA, adding child records
Thanks for the fast response! Okay, a couple of things: 1. Well, typically the form callbacks are just setting values. If they're more ... (I take that to mean that setting entity properties does not require a transaction? I thought that in JPA entities are generally monitored for modifications? Is that a mistake?) Look at my code for setting the location. def bindLocation(loc: NatureLocationType) = { var l = loc bind(location, chooseTemplate(each, location, xhtml), id - SHtml.hidden(()= l = NatureLocationType.lookup (loc.id)), name - SHtml.text(l.name, l.name = _), allowStreet - SHtml.checkbox(l.allowStreet, l.allowStreet = _), allowHospital - SHtml.checkbox(l.allowHospital, l.allowHospital = _), allowDoctor - SHtml.checkbox(l.allowDoctor, l.allowDoctor = _), allowEquipment - SHtml.checkbox(l.allowEquipment, l.allowEquipment = _), removeBtn - SHtml.submit(?(Remove), ()=removeLocation(l)) ) } Because the entity manager is closed after each request, the location has to be reloaded in order to be modified. So loading it has to be done in a transaction, but can I end the transaction before I set the properties? 2. In the mean time I made my own whole-form processor. Unfortunately it's a little verbose to use. Here's the code (currently only supports text boxed and check boxes, and string values and boolean values, but it's very easy to extend): import scala.xml.{Group, Elem, NodeSeq} import scala.collection.mutable.{Map = mMap} import net.liftweb.http.SHtml import net.liftweb.util.{Helpers, Full, Empty} import Helpers._ abstract class FormProcessor(prefix: String) { def text(attrs: (String, String)*)(init: String, action: String=Unit) = SHtml.text(init, action, attrs:_*) def text: (String,String=Unit)=NodeSeq = text()_ def checkbox(attrs: (String, String)*)(init: Boolean, action: Boolean=Unit) = SHtml.checkbox(init, action, attrs:_*) def checkbox: (Boolean, Boolean=Unit)=NodeSeq = checkbox()_ val stringValues: mMap[String, String] = mMap.empty[String, String] val strings = mMap.empty[String, (String,String=Unit)=NodeSeq] val booleanValues = mMap.empty[String, Boolean] val booleans = mMap.empty[String, (Boolean,Boolean=Unit)=NodeSeq] def bind(xhtml: NodeSeq) = { def transform(node: NodeSeq): NodeSeq = { put node match { case Elem(`prefix`, label, _, _, _*) if strings.keys contains label = strings(label)(stringValues(label), stringValues(label) = _) case Elem(`prefix`, label, _, _, _*) if booleans.keys contains label = booleans(label)(booleanValues(label), booleanValues(label) = _) case other = other } } Helpers.bind(prefix, Full(transform _), Empty, xhtml) ++ Seq(SHtml.hidden(()=get)) } def put: Unit def get: Unit } And here's some usage: def bindLocation(loc: NatureLocationType) = { var l = loc val form = new lrbcol.FormProcessor(location) { strings += (name - text) booleans ++= Map(allowStreet-checkbox, allowHospital-checkbox, allowDoctor-checkbox, allowEquipment-checkbox) def put { stringValues(name) = l.name booleanValues ++= Map(allowStreet - l.allowStreet, allowHospital - l.allowHospital, allowDoctor - l.allowDoctor, allowEquipment - l.allowEquipment) } def get = transaction { val l = NatureLocationType.lookup(loc.id) l.name = stringValues(name) l.allowStreet = booleanValues(allowStreet) l.allowHospital = booleanValues(allowHospital) l.allowDoctor = booleanValues(allowDoctor) l.allowEquipment = booleanValues(allowEquipment) } } bind(location, form.bind(chooseTemplate(each, location, xhtml)), removeBtn - SHtml.submit(?(Remove), ()=removeLocation (NatureLocationType.lookup(loc.id))) ) } If you have a better way of implementing it, let me know. Feel free to include it in lift, unmodified or modified. 3. I'll put it in the next post because it's somewhat separate. On May 19, 3:20 pm, Derek Chen-Becker dchenbec...@gmail.com wrote: Well, typically the form callbacks are just setting values. If they're more complex than that then you would need a try/catch in each function. I don't think that the current form processing code has any facility for a per-form exception handler, although that might be a useful feature. Derek On Tue, May 19, 2009 at 1:09 PM, ngug naftoli...@gmail.com wrote: Sorry for the delay in responding. My question was not about transaction API, my question was a practical one: If form processing is split into a bunch of little anonymous functions, how can you put them in a try/finally? On May 15, 1:59 pm, Derek Chen-Becker
[Lift] Re: Trouble with lift, GAE, JPA, adding child records
בה using the FormProcessor, all my exceptions seem to have disappeared. Now I have a problem, that although seems to be a separate problem is very much intertwined with everything else and I think it's really one issue. The page basically looks like this: Nature Name: [text field] Locations: Name Allow St. Addr Allow Hosp. Addr Allow Doc. Allow Eqpmt Loc [text field] [X] [X] [ ] [ ][[Remove button]] [[Insert Row button]] [[Save button]] And you can add/remove locations, e.g. for nature Hospital Visitation you would have one location, called Location or Hospital which is only allowed to be a hospital (when you enter the request you'll get drop-downs). A Food delivery might have a From and a To, which can be street addresses, hospital rooms, or even a doctor's office, but it wouldn't let you select an equipment location. Now, this app is being rewritten, after I started it in PHP. In the PHP version (I didn't know how to use multiple submit buttons then) adding and removing rows was done via a link, so it discarded any modifications you made to locations. (I'm trying to stay away from relying on Javascript). Now, with Lift, they are buttons, and so any modifications you make to locations are saved to the datastore immediately upon pressing them, because the page needs to reload with these locations. But neither of these behaviors is optimal. Really, it should keep track of your changes but only send them to the datastore when you explicitly press Save. But the page can get reloaded via Remove/Insert Row in between. Okay, so I made another RequestVar to hold pending new rows and rows pending removal, and it shows the page as if they were added and removed. But what happens if you modify the properties of a location and then insert a row? Modifying the location that that field was mapped to is pointless, because we don't want to merge it yet, but we're going to have to reload it from the datastore in a future request cycle, before we are able to merge it, because it doesn't seem to be able to merge an entity that was detached the last time the entity manager was closed. What am I supposed to do? Store everything in a fake (never persisted) nature/locations and then copy all the differences to the managed entity when processing the Save submit button? I hope you understand my question, let me know what needs clarification. Thanks a ton! --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: Trouble with lift, GAE, JPA, adding child records
P.S. I wrote a method to handle the boilerplate of opening/closing/ checking whether to rollback transactions. Shouldn't ScalaJPA have such a method? Or does it? Thanks. --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: Trouble with lift, GAE, JPA, adding child records
It should already. The closeEM method on both LocalEM and JndiEM checks to see if the transaction has been marked rollback only and should handle committing/rollback there. If it doesn't then it's a bug. Also, for your first question: (I take that to mean that setting entity properties does not require a transaction? I thought that in JPA entities are generally monitored for modifications? Is that a mistake?) In the context of a form processing function, the JPA entity is in a detached state at the point that its members are being updated because the session that it was loaded in (when the form rendered) is now closed. The transaction is only required when you perform the merge of the detached object into a new session to save the entity data. Whether or not the entity is actually monitored is an implementation detail, since the spec only says that when the entity is merged it should save data back to the database, but it doesn't specify how that is done. Any exceptions related to JPA should only occur during the flush, or possibly the merge if the JPA provider does an eager flush to the database. This is why I added the mergeAndFlush, persistAndFlush, and removeAndFlush methods, so that you would have a definite place to wrap in a try/catch. If you're doing multiple ops then you'll need to merge your entire block of merge,persist,remove and flush methods in a try/catch. Does that make sense? I know that sometimes the lifecycle of these things can take a few tries to wrap your head around, so if this isn't clear let me know and I'll try to rephrase. Derek On Tue, May 19, 2009 at 2:07 PM, ngug naftoli...@gmail.com wrote: P.S. I wrote a method to handle the boilerplate of opening/closing/ checking whether to rollback transactions. Shouldn't ScalaJPA have such a method? Or does it? Thanks. --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: Trouble with lift, GAE, JPA, adding child records
On May 19, 4:31 pm, Derek Chen-Becker dchenbec...@gmail.com wrote: It should already. The closeEM method on both LocalEM and JndiEM checks to see if the transaction has been marked rollback only and should handle committing/rollback there. If it doesn't then it's a bug. But we're talking about userTx==true. I mean this: private def transaction(prog: =Unit) { val tx = EntityManager.getTransaction try { tx.begin prog tx.commit } finally { if(tx.isActive) tx.rollback } } ... def get = transaction { val l = NatureLocationType.lookup(loc.id) l.name = stringValues(name) l.allowStreet = booleanValues(allowStreet) l.allowHospital = booleanValues(allowHospital) l.allowDoctor = booleanValues(allowDoctor) l.allowEquipment = booleanValues(allowEquipment) } (continued below) Also, for your first question: (I take that to mean that setting entity properties does not require a transaction? I thought that in JPA entities are generally monitored for modifications? Is that a mistake?) In the context of a form processing function, the JPA entity is in a detached state at the point that its members are being updated because the session that it was loaded in (when the form rendered) is now closed. The transaction is only required when you perform the merge of the detached object into a new session to save the entity data. Whether or not the entity is actually monitored is an implementation detail, since the spec only says that when the entity is merged it should save data back to the database, but it doesn't specify how that is done. Any exceptions related to JPA should only occur during the flush, or possibly the merge if the JPA provider does an eager flush to the database. This is why I added the mergeAndFlush, persistAndFlush, and removeAndFlush methods, so that you would have a definite place to wrap in a try/catch. If you're doing multiple ops then you'll need to merge your entire block of merge,persist,remove and flush methods in a try/catch. Does that make sense? I know that sometimes the lifecycle of these things can take a few tries to wrap your head around, so if this isn't clear let me know and I'll try to rephrase. I understand you, and that's what I thought too, but I got an exception when I tried to merge a detached entity! If I could get an entity in one request, close the EM, and in the next request just modify it and merge it it would be great, but so far I haven't managed to. If you can't think of any explanation offhand I'll try to reproduce it in an isolated snippet. Thanks! Derek On Tue, May 19, 2009 at 2:07 PM, ngug naftoli...@gmail.com wrote: P.S. I wrote a method to handle the boilerplate of opening/closing/ checking whether to rollback transactions. Shouldn't ScalaJPA have such a method? Or does it? Thanks. --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: Trouble with lift, GAE, JPA, adding child records
On the first part, I could add a withTx method to LocalEM that would handle wrapping the tx for you, if that's OK. Something like: def withTx[T] (f : = T) Then you could do MyEM.withTx { prog } And it would handle the rollback. How does that sound? As for the second, if you're getting an exception on the merge I'd really like to see the Exception (and code, if possible). Off the top of my head the only time that should happen is if you have a constraint violation or something similar that would throw and exception even if you were operating on a non-detached entity. Take a look at the JPA Demo Library app. It does detached object merge all of the time for editing authors and books. Derek On Tue, May 19, 2009 at 2:42 PM, ngug naftoli...@gmail.com wrote: On May 19, 4:31 pm, Derek Chen-Becker dchenbec...@gmail.com wrote: It should already. The closeEM method on both LocalEM and JndiEM checks to see if the transaction has been marked rollback only and should handle committing/rollback there. If it doesn't then it's a bug. But we're talking about userTx==true. I mean this: private def transaction(prog: =Unit) { val tx = EntityManager.getTransaction try { tx.begin prog tx.commit } finally { if(tx.isActive) tx.rollback } } ... def get = transaction { val l = NatureLocationType.lookup(loc.id) l.name = stringValues(name) l.allowStreet = booleanValues(allowStreet) l.allowHospital = booleanValues(allowHospital) l.allowDoctor = booleanValues(allowDoctor) l.allowEquipment = booleanValues(allowEquipment) } (continued below) Also, for your first question: (I take that to mean that setting entity properties does not require a transaction? I thought that in JPA entities are generally monitored for modifications? Is that a mistake?) In the context of a form processing function, the JPA entity is in a detached state at the point that its members are being updated because the session that it was loaded in (when the form rendered) is now closed. The transaction is only required when you perform the merge of the detached object into a new session to save the entity data. Whether or not the entity is actually monitored is an implementation detail, since the spec only says that when the entity is merged it should save data back to the database, but it doesn't specify how that is done. Any exceptions related to JPA should only occur during the flush, or possibly the merge if the JPA provider does an eager flush to the database. This is why I added the mergeAndFlush, persistAndFlush, and removeAndFlush methods, so that you would have a definite place to wrap in a try/catch. If you're doing multiple ops then you'll need to merge your entire block of merge,persist,remove and flush methods in a try/catch. Does that make sense? I know that sometimes the lifecycle of these things can take a few tries to wrap your head around, so if this isn't clear let me know and I'll try to rephrase. I understand you, and that's what I thought too, but I got an exception when I tried to merge a detached entity! If I could get an entity in one request, close the EM, and in the next request just modify it and merge it it would be great, but so far I haven't managed to. If you can't think of any explanation offhand I'll try to reproduce it in an isolated snippet. Thanks! Derek On Tue, May 19, 2009 at 2:07 PM, ngug naftoli...@gmail.com wrote: P.S. I wrote a method to handle the boilerplate of opening/closing/ checking whether to rollback transactions. Shouldn't ScalaJPA have such a method? Or does it? Thanks. --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: Trouble with lift, GAE, JPA, adding child records
I actually started my code by editing the AuthorOps snippet from ymnk's GAE version of it. He does reload the nature before editing it: // Hold a val here so that the id closure holds it when we re-enter this method bind(author, xhtml, id - SHtml.hidden(() = findAuthor(author) match { case Some(a) = authorVar(a) case None =}), name - SHtml.text(author.name, author.name=_), submit - SHtml.submit(?(Save), doAdd)) findAuthor looks up the the author based on the id in the authorVar RequestVar, and then sets that back into the authorVar. I wondered why he did that, and then thought I understood... Thanks! On May 19, 5:01 pm, Derek Chen-Becker dchenbec...@gmail.com wrote: On the first part, I could add a withTx method to LocalEM that would handle wrapping the tx for you, if that's OK. Something like: def withTx[T] (f : = T) Then you could do MyEM.withTx { prog } And it would handle the rollback. How does that sound? As for the second, if you're getting an exception on the merge I'd really like to see the Exception (and code, if possible). Off the top of my head the only time that should happen is if you have a constraint violation or something similar that would throw and exception even if you were operating on a non-detached entity. Take a look at the JPA Demo Library app. It does detached object merge all of the time for editing authors and books. Derek On Tue, May 19, 2009 at 2:42 PM, ngug naftoli...@gmail.com wrote: On May 19, 4:31 pm, Derek Chen-Becker dchenbec...@gmail.com wrote: It should already. The closeEM method on both LocalEM and JndiEM checks to see if the transaction has been marked rollback only and should handle committing/rollback there. If it doesn't then it's a bug. But we're talking about userTx==true. I mean this: private def transaction(prog: =Unit) { val tx = EntityManager.getTransaction try { tx.begin prog tx.commit } finally { if(tx.isActive) tx.rollback } } ... def get = transaction { val l = NatureLocationType.lookup(loc.id) l.name = stringValues(name) l.allowStreet = booleanValues(allowStreet) l.allowHospital = booleanValues(allowHospital) l.allowDoctor = booleanValues(allowDoctor) l.allowEquipment = booleanValues(allowEquipment) } (continued below) Also, for your first question: (I take that to mean that setting entity properties does not require a transaction? I thought that in JPA entities are generally monitored for modifications? Is that a mistake?) In the context of a form processing function, the JPA entity is in a detached state at the point that its members are being updated because the session that it was loaded in (when the form rendered) is now closed. The transaction is only required when you perform the merge of the detached object into a new session to save the entity data. Whether or not the entity is actually monitored is an implementation detail, since the spec only says that when the entity is merged it should save data back to the database, but it doesn't specify how that is done. Any exceptions related to JPA should only occur during the flush, or possibly the merge if the JPA provider does an eager flush to the database. This is why I added the mergeAndFlush, persistAndFlush, and removeAndFlush methods, so that you would have a definite place to wrap in a try/catch. If you're doing multiple ops then you'll need to merge your entire block of merge,persist,remove and flush methods in a try/catch. Does that make sense? I know that sometimes the lifecycle of these things can take a few tries to wrap your head around, so if this isn't clear let me know and I'll try to rephrase. I understand you, and that's what I thought too, but I got an exception when I tried to merge a detached entity! If I could get an entity in one request, close the EM, and in the next request just modify it and merge it it would be great, but so far I haven't managed to. If you can't think of any explanation offhand I'll try to reproduce it in an isolated snippet. Thanks! Derek On Tue, May 19, 2009 at 2:07 PM, ngug naftoli...@gmail.com wrote: P.S. I wrote a method to handle the boilerplate of opening/closing/ checking whether to rollback transactions. Shouldn't ScalaJPA have such a method? Or does it? Thanks. --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en
[Lift] Re: Trouble with lift, GAE, JPA, adding child records
One entity group per transaction is a GAE requirement. But my question about handling transactions was more specific - because form processing is specified piecemeal in separate closures, you can't use the normal try/finally. It seems a little too much effort to create a hidden field before and after every form/subform. Thanks. P.S. Sorry about the triple post. Opera Mobile (well, Google) didn't tell me the post was successful like it usually does, so I thought it didn't go through. (I thought the list is moderated and you would filter the extras just in case.) --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: Trouble with lift, GAE, JPA, adding child records
EntityManager has a getTransaction method that you can use along with the constructor I mentioned previously to explicitly begin and end transactions in your code. derek On Fri, May 15, 2009 at 11:45 AM, ngug naftoli...@gmail.com wrote: One entity group per transaction is a GAE requirement. But my question about handling transactions was more specific - because form processing is specified piecemeal in separate closures, you can't use the normal try/finally. It seems a little too much effort to create a hidden field before and after every form/subform. Thanks. P.S. Sorry about the triple post. Opera Mobile (well, Google) didn't tell me the post was successful like it usually does, so I thought it didn't go through. (I thought the list is moderated and you would filter the extras just in case.) --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: Trouble with lift, GAE, JPA, adding child records
In any case, how would I handle transactions manually? An SHtml.hidden before and after every form? It's confusing because SHtml lumps the get and set side by side when they happen in two different requests and the entity needs to be reloaded in between. In any case, GAE seems to require tighter control of transactions because two entity groups can't be modified (accessed?) in one transaction. --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: Trouble with lift, GAE, JPA, adding child records
In any case, how would I handle transactions manually? An SHtml.hidden before and after every form? It's confusing because SHtml lumps the get and set side by side when they happen in two different requests and the entity needs to be reloaded in between. In any case, GAE seems to require tighter control of transactions because two entity groups can't be modified (accessed?) in one transaction. --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: Trouble with lift, GAE, JPA, adding child records
In any case, how would I handle transactions manually? An SHtml.hidden before and after every form? It's confusing because SHtml lumps the get and set side by side when they happen in two different requests and the entity needs to be reloaded in between. In any case, GAE seems to require tighter control of transactions because two entity groups can't be modified (accessed?) in one transaction. --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: Trouble with lift, GAE, JPA, adding child records
I guess I should include the views too to make it easier to reproduce the problem. Here is edit.html: lift:surround with=default at=content head titlelift:loc lift:Natures.newOrEdit if:editEdit/if:edit if:newNew/if:new /lift:Natures.newOrEditnbsp;Nature /lift:loc/title /head h3lift:loc lift:Natures.newOrEdit if:editUpdate/if:edit if:newCreate/if:new /lift:Natures.newOrEditnbsp;Request Nature/lift:loc/h3 lift:Natures.edit form=GET nature:id / fieldsetlegendlift:locName/lift:loc/legend nature:name / /fieldset fieldsetlegendlift:locLocations/lift:loc/legend table thead tr thLocation name/th th colspan=4Allowed locations/th th/th /tr /thead tr td/td tdStreet/td tdHospital/td tdDoctor/td tdEquipment/td td/td /tr nature:locations each:location tr tdlocation:id /location:name //td tdlocation:allowStreet //td tdlocation:allowHospital //td tdlocation:allowDoctorallowDoctorChk/location:allowDoctor/ td tdlocation:allowEquipmentallowEquipmentChk/ location:allowEquipment/td tdlocation:removeBtnRemoveBtn/location:removeBtn/td /tr /each:location /nature:locations tr td colspan=5/td tdnature:newLocationinsertLocationBtn/nature:newLocation/ td /tr /table /fieldset nature:submit / /lift:Natures.edit /lift:surround And here is list.html: lift:surround with=default at=content head titlelift:locNatures List/lift:loc/title /head h3Request Natures/h3 table thead tr thlift:locName/lift:loc/th thlift:locLocations/lift:loc/th th/th /tr /thead lift:Natures.list tr tdnature:name //td tdnature:locations //td tdnature:edit //td tdnature:remove //td /tr /lift:Natures.list a href=editlift:locNew/lift:loc/a /table /lift:surround --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: Trouble with lift, GAE, JPA, adding child records
I took a quick look and nothing is jumping out at me as wrong. When you get these errors do you at least get a stack trace? Having that would help narrow down where it's happening. Derek On Tue, May 12, 2009 at 5:40 PM, ngug naftoli...@gmail.com wrote: I made the beginnings of a lift app for GAE. It's for a volunteer medical assistance organization, to manage requests that they get. Every request has a nature, e.g., transportation, hospital visitation, etc. Each nature has a OneToMany association with a NatureLocationType, which means that when you enter the request you have a different set of location fields depending on the nature. One screen lets you manage the natures. On the same screen that you edit the name of the nature you can add, remove, and edit location types for that nature. I'm new to lift, GAE, and JPA, although familiar with Scala. So far I'm only working with the dev server. I started with the lift JPA library demo's (snippet) code Anyway, after a while I got it pretty functional. (After discovering that after each request, when it processes the input from the previous request the same entity object is still in the JVM, but it has become detached and there's no way to reattach it other than re-looking it up via its id---why does the entity manager have a shorter lifespan than the entity?) However, every now and then I get an error that the object is dirty but no field are, and sometimes an error that a request completed without closing the (which?) transaction. Could someone look at the source code and help me? Thanks, it's really appreciated. Here's the source for the snippet class (models below; note that EntityManager is my name for what the library demo calls Model: object EntityManager extends LocalEMF(transactions-optional) with RequestVarEM): package liftgae1.snippet import javax.persistence.{EntityExistsException, PersistenceException} import scala.collection.jcl.Conversions.convertList import scala.xml.{NodeSeq, Text} import net.liftweb.http.{RequestVar, SHtml, S, StatefulSnippet} import S._ import net.liftweb.util.{Helpers, Log} import Helpers._ import lrbcol.model.{EntityManager, Nature, NatureLocationType} class Natures /*extends StatefulSnippet*/ { /* val dispatch: DispatchIt = { case list = list case newOrEdit = newOrEdit case edit = edit }*/ // Set up a requestVar to track the nature object for edits and adds private object natureVar extends RequestVar[Nature](new Nature) def nature: Nature = natureVar.is def nature_=(nature: Nature) = natureVar(nature) def hasNature = nature.id != null def reloadNature = if(hasNature) try {nature = Nature.lookup (nature.id).get} catch {case e = println(e)} def list(xhtml: NodeSeq): NodeSeq = { println(In list) Nature.all foreach println NatureLocationType.all foreach {l=println(l + for + l.nature)} Nature.all.flatMap { n = println(Binding with + n + , contained = + EntityManager.contains(n)) bind(nature, xhtml, name -Text(n.name), locations - Text(n.natureLocationTypes map {_.name} mkString ,), edit -SHtml.link(edit, () = nature = n, Text(?(Edit)) ), remove - SHtml.link(list, () = {nature = n; reloadNature; EntityManager.removeAndFlush(nature)}, Text(?(Remove)) ) ) } } def newOrEdit(xhtml: NodeSeq) = hasNature match { case false = chooseTemplate(if, new, xhtml) case _ = chooseTemplate(if, edit, xhtml) } def edit(xhtml: NodeSeq): NodeSeq = { def doSave() { println(In doSave with %s format(nature)) if (nature.name.isEmpty) { error(emptyNature, The nature's name cannot be blank) } else try { EntityManager.mergeAndFlush(nature) redirectTo(list) } catch { case ee: EntityExistsException = error(That nature already exists.) case pe: PersistenceException = error(Error adding nature); Log.error(Nature add failed, pe) } } def insertLocation() { val l = new NatureLocationType nature.natureLocationTypes += l l.nature = nature EntityManager.mergeAndFlush(if(hasNature) l else nature) } def removeLocation(l: NatureLocationType) { try { EntityManager.removeAndFlush(l) } catch { case e = e.printStackTrace } } println(In edit with nature %s. format(nature)) def bindLocation(loc: NatureLocationType) = { var l = loc bind(location, chooseTemplate(each, location, xhtml), id - SHtml.hidden(()= l = NatureLocationType.lookup (loc.id)), name - SHtml.text(l.name, l.name = _),
[Lift] Re: Trouble with lift, GAE, JPA, adding child records
Thanks for looking. The stack traces aren't very helpful because they concern the state which is caused by the previous sequence of events. Anyway, why is there a transaction taking place in the first place? --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---
[Lift] Re: Trouble with lift, GAE, JPA, adding child records
JPA requires that a transaction be in place before *any* persistence operations occur. The ScalaJPA stuff is supposed to handle it for you, so that's why I was wondering about stack traces. In particular, the RequestVarEM trait should start a new transaction when the request begins and close the transaction on exit. Derek On Wed, May 13, 2009 at 2:14 PM, ngug naftoli...@gmail.com wrote: Thanks for looking. The stack traces aren't very helpful because they concern the state which is caused by the previous sequence of events. Anyway, why is there a transaction taking place in the first place? --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups Lift group. To post to this group, send email to liftweb@googlegroups.com To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/liftweb?hl=en -~--~~~~--~~--~--~---