Derek, this is fantastic (and confirms a lot of guesses on my part). 
I'll read the docs and will get back to you.

I'm definitely using JPA/Hibernate in several large projects I'm working 
on right now (and need to get online ASAP), so I'll help out in any way 
I can. (Warning: I still do not really grok Scala or FP in general. 
Brain stubbornly thinks in objects.)

Chas.

Derek Chen-Becker wrote:
> OK, let me try to tackle a few of these things at once. First of all, 
> the hidden element is really just creating a function that is going to 
> be run when the form is submitted. If you look at the HTML that's 
> generated you'll see it like this:
> 
> <input name="F977465725002233_OGX" type="hidden" value="true" />
> 
> Basically what happens is that Lift processes form fields as functions. 
> It looks up the function "F977...OGX" (in this case) and executes it. I 
> believe (but am not 100% positive) that forms are executed in the 
> sequence that they're submitted, which is usually the order in which 
> they're defined. Anyways, back to the code at hand:
> 
> "id" -> SHtml.hidden({category.id <http://category.id/> = currentId})
> 
> It's going to call the category method (which if you're following the 
> template is tied to a RequestVar). Since there isn't one already defined 
> in the request, the default value of the RequestVar is constructed (new 
> Category()) and returned. That's a big part of the problem with the 
> current template (and something I'm fixing right now in my wip branch): 
> we're not really editing the same object, we're making a brand new 
> object and copying values. The issue is that, as you've found, any 
> fields that you're not explicitly copying are reset to defaults. What we 
> probably want to do instead is this:
> 
> val ctgy = currentCategory // instead of currentId
> ...
> "id" -> SHtml.hidden(categoryVar(ctgy))
> 
> or even
> 
> "id" -> SHtml.hidden(categoryVar(Model.merge(ctgy))) // This 
> automatically re-attaches the category to the entity manager
> 
> and then all you'd be required to do in your submit is a Model.flush(). 
> Technically, the flush is implicit once the transaction closes at the 
> end of the session, but if you have any constraints that you would want 
> to handle violations on (i.e. a duplicate name, deleting something 
> that's referenced as an FK), you need to do the flush at that point so 
> that the exceptions get thrown. Otherwise, the exception is thrown as 
> Lift is unwinding and your user gets a pretty stack trace.
> 
> As for how Hibernate handles object lifecycle, I highly recommend 
> reading this section of the hibernate EM docs:
> 
> http://www.hibernate.org/hib_docs/entitymanager/reference/en/html/objectstate.html
> 
> In particular, look at sections 3.1 and 3.6. Most of what we're doing 
> here is working with detached objects. The merge method is what 
> reattaches an object.
> 
> I realize I'm still waving my hands a bit here and sweeping some 
> plumbing details under the rug, but I hope this makes more sense now. 
> Please feel free to ask questions. I'm actually working on the JPA 
> chapter of the book right now, so you're helping me see where I need to 
> clarify and explain things better :). I also need to refine the JPA 
> demo, since the pattern that I'm currently using is non-optimal and even 
> wrong in cases like yours.
> 
> Derek
> 
> 
> On Mon, Oct 13, 2008 at 6:38 PM, Charles F. Munat <[EMAIL PROTECTED] 
> <mailto:[EMAIL PROTECTED]>> wrote:
> 
> 
>     Yeah, I'm really unclear on how this is being passed around and how the
>     continuation works. For example, what does this line do:
> 
>     "id" -> SHtml.hidden({category.id <http://category.id> = currentId})
> 
>     I can see that it adds a hidden field in the form with the value true
>     and the name of the field some sort of code. I can only presume that
>     that code tells the snippet, upon submission of the form, that it should
>     set the category.id <http://category.id> to currentId, thus handing
>     the object back to the
>     snippet. But how does this really work? What creates the code and what
>     interprets it and recreates the object? I have been unable to figure
>     this out.
> 
>     Also, I'm not entirely clear on hibernate. My understanding is that
>     there is only one copy of any object, so if I pass the id of an object
>     that already exists to my category, then I am manipulating the real
>     object. Is that true? So when I "merge," I'm merging changes to that
>     object into the persisted one?
> 
>     A problem I'm having because I don't really understand how this works is
>     that I have a "createdAt" field in my objects which is a timestamp
>     that's set to now() when the object is created. But everytime I update
>     the object, it resets that field to a new "now()". And that is true for
>     other fields as well. I can only guess that the object I'm merging isn't
>     the same as the object in the database, and the null fields in my "new"
>     object cause the fields in the database to be changed to the default
>     values. That's not what I want, but I can't figure out how to get around
>     it, and I'm not sure how to load the object in the database before
>     manipulating it. Everything seems to work by magic, and I'm a lousy
>     magician.
> 
>     Does any of this make any sense? I've got all this working at the
>     moment, but thanks to magic. I really am not sure *why* or *how* it is
>     working. I'd love to have a better understanding of this, but it's an
>     uphill battle especially given how much I need to get done in how little
>     time (so not much time for research).
> 
>     Thanks, BTW, for all your help, Derek. You have basically saved my ass
>     on this and many other projects and if I can do the same for you anytime
>     please let me know.
> 
>     Chas.
> 
>     Derek Chen-Becker wrote:
>      > When you go to edit, are you passing the Category to be edited
>     into the
>      > categoryVar RequestVar? For instance, in your listing code say
>     you had
>      > something like:
>      >
>      > def list (xhtml : NodeSeq) : NodeSeq = {
>      >   Model.createQuery[Category]("from
>     Category").findAll.flatMap({category =>
>      >     <tr>
>      >       <td>{category.name <http://category.name>
>     <http://category.name>}</td>
>      >       <td>{Shtml.link("/admin/categories/add", () =>
>      > categoryVar(category), Text("Edit"))}</td>
>      >     </tr>
>      > }
>      >
>      > Otherwise I don't see in your code where the categoryVar is being
>      > populated other than the default (new Category()).
>      >
>      > Derek
>      >
>      >
>      > On Sat, Oct 11, 2008 at 1:35 PM, Charles F. Munat <[EMAIL PROTECTED]
>     <mailto:[EMAIL PROTECTED]>
>      > <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>>> wrote:
>      >
>      >
>      >     OK, I tried that. Still no luck. I also went back to just
>     using bind:
>      >
>      >              <lift:CategoryOps.editor form="POST">
>      >                <category:id />
>      >                <category:name />
>      >                <category:submit />
>      >              </lift:CategoryOps.editor>
>      >
>      >     And
>      >
>      >     object categoryVar extends RequestVar(new Category())
>      >     def category = categoryVar.is
>      >
>      >     def editor(xhtml : NodeSeq) : NodeSeq = {
>      >       def addOrUpdate() = {
>      >         if (category.name.length == 0) {
>      >           error("emptyCategory", "The category's name cannot be
>     blank")
>      >         } else {
>      >           Model.merge(category)
>      >           redirectTo("/admin/categories/")
>      >         }
>      >       }
>      >
>      >       val currentId = category.id <http://category.id>
>     <http://category.id>
>      >
>      >
>      >       bind("category", xhtml,
>      >            "id" -> SHtml.hidden({category.id <http://category.id>
>     <http://category.id> =
>      >     currentId}),
>      >            "name" -> SHtml.text(category.name
>     <http://category.name> <http://category.name>,
>      >     category.name <http://category.name> <http://category.name> = _),
>      >            "submit" -> SHtml.submit("Save", addOrUpdate))
>      >     }
>      >
>      >     Everything works just fine on add, but on update the form is not
>      >     populated because category is a new Category, not the one
>     from the
>      >     database (I checked).
>      >
>      >     Any ideas?
>      >
>      >     Chas.
>      >
>      >
>      >     Derek Chen-Becker wrote:
>      >      > OK, one thing I see in your code is that your editor
>     snippet is
>      >     emitting
>      >      > the "form" tag. You really shouldn't do it that way. The
>     snippet tag
>      >      > should look like
>      >      >
>      >      > <lift:CategoryOps.editor form="POST" />
>      >      >
>      >      > Otherwise I don't think the action won't get correctly
>     populated. I
>      >      > don't know if that would cause the form elements to not be
>     bound as
>      >      > well, but let's start with that.
>      >      >
>      >      > Derek
>      >      >
>      >      >
>      >      > On Fri, Oct 10, 2008 at 5:36 PM, Charles F. Munat
>     <[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>
>      >     <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>>
>      >      > <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>
>     <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>>>> wrote:
>      >      >
>      >      >
>      >      >     For example, here is what I get when I go to the
>     categories
>      >     list page
>      >      >     (which lists them just beautifully):
>      >      >
>      >      >     Hibernate: select category0_.id as id2_,
>     category0_.CREATED_AT as
>      >      >       CREATED2_2_, category0_.name as name2_,
>      >     category0_.UPDATED_AT as
>      >      >       UPDATED4_2_ from CATEGORIES category0_ order by
>     category0_.name
>      >      >     WARN - Snippet Failure:
>     SnippetFailure(/admin/categories/ ->
>      >      >       ParsePath(List(admin, categories,
>      >      >       index),,true,true),Full(Menu.link),Stateful Snippet:
>      >     Dispatch Not
>      >      >       Matched)
>      >      >     WARN - Snippet Failure:
>     SnippetFailure(/admin/categories/ ->
>      >      >       ParsePath(List(admin, categories,
>      >      >       index),,true,true),Full(Menu.link),Stateful Snippet:
>      >     Dispatch Not
>      >      >       Matched)
>      >      >     INFO - Service request (GET) /admin/categories/ took 28
>      >     Milliseconds
>      >      >     INFO - Service request (GET) /ajax_request/liftAjax.js
>     took 1
>      >      >       Milliseconds
>      >      >
>      >      >     So there are two warnings for each page view.
>      >      >
>      >      >     Chas.
>      >      >
>      >      >     David Pollak wrote:
>      >      >      > You have to define a package for Category.scala
>      >      >      >
>      >      >      > On Fri, Oct 10, 2008 at 4:00 PM, Charles F. Munat
>      >     <[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>
>     <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>>
>      >      >     <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>
>     <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>>>
>      >      >      > <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>
>     <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>>
>      >     <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>
>     <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>>>>> wrote:
>      >      >      >
>      >      >      >
>      >      >      >     Thanks, Derek. Actually, I don't use .html or even
>      >     "add" (all
>      >      >     my html
>      >      >      >     files are called index.html -- I use
>     directories to sort
>      >      >     them). This
>      >      >      >     error came when I was experimenting to see if
>     that made a
>      >      >     difference.
>      >      >      >
>      >      >      >     That said, I get the error all the time. I
>     don't have val
>      >      >     dispatch... in
>      >      >      >     my snippets. But that's because I copied the
>     JPADemo over,
>      >      >     and your
>      >      >      >     AuthorOps and BookOps don't have the val dispatch
>      >     either. But the
>      >      >      >     JPADemo doesn't get this error.
>      >      >      >
>      >      >      >     Also, my edit functions don't work -- the forms are
>      >     not populated
>      >      >      >     properly. I thought I had it working a minute
>     ago, and
>      >     now it
>      >      >     isn't
>      >      >      >     again.
>      >      >      >
>      >      >      >     I switched the snippets around a bit and
>     dropped the bind
>      >      >     (because I
>      >      >      >     wanted to color code alternate rows and wasn't sure
>      >     how to do it
>      >      >      >     otherwise). Here is an example:
>      >      >      >
>      >      >      >     snippet/Category.scala:
>      >      >      >
>      >      >      >     import scala.xml.{NodeSeq,Text}
>      >      >      >
>      >      >      >     import net.liftweb.http.{RequestVar,S,SHtml}
>      >      >      >     import net.liftweb.util.Helpers
>      >      >      >     import S._
>      >      >      >     import Helpers._
>      >      >      >
>      >      >      >     import Model._
>      >      >      >
>      >      >      >     class CategoryOps {
>      >      >      >       val formatter = new
>     java.text.SimpleDateFormat("dd
>      >     MMM yyyy")
>      >      >      >
>      >      >      >       def getRowClass(r: Int) = {
>      >      >      >         if (r % 2 == 0) "even" else "odd"
>      >      >      >       }
>      >      >      >
>      >      >      >       def list: NodeSeq = {
>      >      >      >         <tr valign="top" align="left">
>      >      >      >           <th class="text">Name</th>
>      >      >      >           <th class="integer">Links</th>
>      >      >      >           <th class="blank"> </th>
>      >      >      >           <th class="blank"> </th>
>      >      >      >         </tr> ::
>      >      >      >
>      >      >      >
>      >      >
>      >    
>     
> Model.createNamedQuery[Category]("findAllCategories").getResultList().toList.zipWithIndex.flatMap(c
>      >      >      >     => <tr valign="top" class={getRowClass(c._2)}>
>      >      >      >           <td class="text">{c._1.name
>     <http://1.name> <http://1.name>
>      >     <http://1.name>
>      >      >     <http://1.name>}</td>
>      >      >      >           <td
>      >     class="integer">{SHtml.link("/links/search/", {() =>
>      >      >      >
>      >      >      WeblinkOps.resultVar(Model.createNamedQuery[Weblink](
>      >      >      >       "findWeblinksByCategory", "id" -> category.id
>     <http://category.id>
>      >     <http://category.id>
>      >      >     <http://category.id>
>      >      >      >     <http://category.id>).getResultList().toList)
>      >      >      >             },
>     Text(category.weblinks.size().toString))}</td>
>      >      >      >           <td class="edit">{SHtml.link("editor", () =>
>      >      >      >     categoryVar(category), Text("Edit"))}</td>
>      >      >      >           <td class="delete"> </td>
>      >      >      >         </tr>)
>      >      >      >       }
>      >      >      >
>      >      >      >       object categoryVar extends RequestVar(new
>     Category())
>      >      >      >       def category = categoryVar.is
>      >      >      >
>      >      >      >       def editor (xhtml : NodeSeq) : NodeSeq = {
>      >      >      >         def addOrUpdate () = {
>      >      >      >           if (category.name.length == 0) {
>      >      >      >            error("emptyCategory", "The category's name
>      >     cannot be
>      >      >     blank")
>      >      >      >           } else {
>      >      >      >            Model.merge(category)
>      >      >      >            redirectTo("/admin/categories/")
>      >      >      >           }
>      >      >      >         }
>      >      >      >
>      >      >      >         val currentId = category.id
>     <http://category.id> <http://category.id>
>      >     <http://category.id>
>      >      >     <http://category.id>
>      >      >      >
>      >      >      >         <form method="POST" action="">
>      >      >      >           <table class="editor" border="0"
>     cellpadding="3"
>      >      >     cellspacing="0">
>      >      >      >             <tr valign="top" align="left">
>      >      >      >               <th>Name</th>
>      >      >      >               <td>{SHtml.text(category.name
>     <http://category.name>
>      >     <http://category.name>
>      >      >     <http://category.name> <http://category.name>,
>      >      >      >     category.name <http://category.name>
>     <http://category.name>
>      >     <http://category.name> <http://category.name> =
>      >      >     _)}</td>
>      >      >      >             </tr>
>      >      >      >             <tr valign="top" align="left">
>      >      >      >               <th>{SHtml.hidden({category.id
>     <http://category.id>
>      >     <http://category.id> <http://category.id>
>      >      >     <http://category.id> =
>      >      >      >     currentId})} </th>
>      >      >      >               <td>{SHtml.submit("Save",
>     addOrUpdate)}</td>
>      >      >      >             </tr>
>      >      >      >           </table>
>      >      >      >         </form>
>      >      >      >       }
>      >      >      >     }
>      >      >      >
>      >      >      >     webapp/admin/categories/editor/index.html:
>      >      >      >
>      >      >      >     <lift:surround with="default" at="content">
>      >      >      >       <h2>Categories</h2>
>      >      >      >       <table class="list">
>      >      >      >         <lift:CategoryOps.list/>
>      >      >      >       </table>
>      >      >      >     </lift:surround>
>      >      >      >
>      >      >      >     And in Boot.scala:
>      >      >      >
>      >      >      >     Menu(Loc("categories", List("admin", "categories",
>      >     "index"),
>      >      >      >         "Categories", LocGroup("admin")),
>      >      >      >       Menu(Loc("categories_add", List("admin",
>     "categories",
>      >      >     "editor",
>      >      >      >         "index"), "Add a New Category", Hidden))
>      >      >      >     ),
>      >      >      >
>      >      >      >     ANY suggestions for how to do any of this
>     better greatly
>      >      >     appreciated. Do
>      >      >      >     I need to add the val dispatch... part? If so,
>     why is this
>      >      >     different
>      >      >      >     from the JPADemo?
>      >      >      >
>      >      >      >     Thanks!
>      >      >      >     Chas.
>      >      >      >
>      >      >      >
>      >      >      >     Derek Chen-Becker wrote:
>      >      >      >      > It means that the dispatch function on whatever
>      >     Stateful
>      >      >     Snippet is
>      >      >      >      > being called isn't matching what you're
>     asking it to
>      >      >     provide. For
>      >      >      >      > instance, if your snippet tag looks like
>      >      >      >      >
>      >      >      >      > <lift:MySnippet.add>
>      >      >      >      >
>      >      >      >      > Then the dispatchPf in the MySnippet stateful
>      >     snippet has
>      >      >     to have a
>      >      >      >      > dispatch function like:
>      >      >      >      >
>      >      >      >      > val dispatch: DispatchIt = {
>      >      >      >      >     case "add" => <some function here> _
>      >      >      >      >   }
>      >      >      >      >
>      >      >      >      > See
>      >      >      >      >
>      >      >      >
>      >      >
>      >    
>     
> http://scala-tools.org/mvnsites-snapshots/liftweb/lift-webkit/scaladocs/index.html
>      >      >      >      > for more details.
>      >      >      >      >
>      >      >      >      > I also know that David has recently /strongly/
>      >     recommended not
>      >      >      >     putting
>      >      >      >      > ".html" on the ends of things.
>      >      >      >      >
>      >      >      >      > Derek
>      >      >      >      >
>      >      >      >      > On Fri, Oct 10, 2008 at 3:15 PM, Charles F.
>     Munat
>      >      >     <[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>
>     <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>>
>      >     <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>
>     <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>>>
>      >      >      >     <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>
>     <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>>
>      >     <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>
>     <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>>>>
>      >      >      >      > <mailto:[EMAIL PROTECTED]
>     <mailto:[EMAIL PROTECTED]> <mailto:[EMAIL PROTECTED] <mailto:[EMAIL 
> PROTECTED]>>
>      >     <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>
>     <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>>>
>      >      >     <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>
>     <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>>
>      >     <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>
>     <mailto:[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>>>>>> wrote:
>      >      >      >      >
>      >      >      >      >
>      >      >      >      >     What exactly does this mean? I get a lot
>     of these.
>      >      >      >      >
>      >      >      >      >     WARN - Snippet Failure:
>      >      >     SnippetFailure(/admin/users/add.html ->
>      >      >      >      >       ParsePath(List(admin, users,
>      >     add),html,true,false),
>      >      >      >      >       Full(Menu.link),
>      >      >      >      >       Stateful Snippet: Dispatch Not Matched)
>      >      >      >      >
>      >      >      >      >     What is failing here and what are the
>     probable
>      >     causes?
>      >      >      >      >
>      >      >      >      >     Thanks.
>      >      >      >      >
>      >      >      >      >     Chas.
>      >      >      >      >
>      >      >      >      >
>      >      >      >      >
>      >      >      >      >
>      >      >      >      > >
>      >      >      >
>      >      >      >
>      >      >      >
>      >      >      >
>      >      >      >
>      >      >      > --
>      >      >      > Lift, the simply functional web framework
>     http://liftweb.net
>      >      >      > Collaborative Task Management http://much4.us
>      >      >      > Follow me: http://twitter.com/dpp
>      >      >      > Git some: http://github.com/dpp
>      >      >      >
>      >      >      > >
>      >      >
>      >      >
>      >      >
>      >      >
>      >      > >
>      >
>      >
>      >
>      >
>      > >
> 
> 
> 
> 
> > 

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Lift" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/liftweb?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to