Reputation: 1264
I'm trying to use cayenne to interact with a PostgreSQL database. So far everything worked as expected but I'm now stuck with a problem I don't really understand (I'm new to cayenne).
Say I have a table "books" with usual fields (book_id, title, editor, price), a "table" authors (author_id, author_name) and a link table "books_authors" (book_id, author_id).
All the classes associated with the process of adding or editing a book share one DataContext.
I have a struts2 (I use velocity templates) form that lets you add a book and, using ajax, multiple authors associated to it. I react to certain event to fire an ajax request that calls a struts2 action that verifies if the author exists and then adds a Books_Authors object (class generated by cayenne) to the struts2 session (the action implements SessionAware).
The form looks like this:
<form action="AddOrEditBook.do">
<input type="hidden" name="book.book_id" value="$!book.book_id" />
<input type="text" name="book.title" value="$!book.title" />
<input type="text" name="book.editor" value="$!book.editor" />
<input type="number" name="book.price" value="$!book.price" />
<input type="text" name="author" /> <input type="button" name="addAuthor" value="Add" />
<ul>
<li id="author_2">Author 2 <input type="button" name="removeAuthor" value="Remove" /></li>
<li id="author_16">Author 16 <input type="button" name="removeAuthor" value="Remove" /> </li>
</ul>
<input type="submit" />
</form>
Once I submit the form to AddOrEditBook.do that has a setter and getter for book, struts2 creates the book object and fills it with the values of the form. That's nice.
This the thing that bothers me:
When I update a book, I didn't find a way to use the object created by struts to update the db. What I want to achieve is to somehow instruct cayenne to use the object created by struts to update the corresponding entity in the db.
I've messed a lot with things like object_id, PersistenceState, etc.:
Expression exp_book = ExpressionFactory.matchExp(Book.BOOK_ID_PROPERTY, book.getBook_id());
Book bookFromDb = RetrieveHelper.retrieveScalarByExpression(ctxt, new Book(), exp_book);
book.setObjectContext(ctxt);
book.setObjectId(bookFromDb.getObjectId());
ctxt.getObjectStore().registerNode(book.getObjectId(), book);
book.setPersistenceState(PersistenceState.MODIFIED);
What is the correct strategy when using cayenne to update db entities? Is it only possible to do that by making things like bookFromDb.setTitle(book.getTitle()); ?
Thanks!
Upvotes: 2
Views: 425
Reputation: 2563
struts2 creates the book object and fills it with the values of the form. That's nice.
I am no Struts2 expert, but from what I am reading in the docs about form object lifecycle, such object is created by Struts2 by calling a default constructor. Unless there is a way to override this behavior, replacing it with some factory that would give you a Cayenne object from the right DataContext, what you are doing here is the right thing - you are separating the value object attached to the form (and the action class) from a persistent object.
With other frameworks like Tapestry, etc. you can bind the form to a Cayenne persistent object directly. Maybe you can extend Struts2 in a similar fashion as well??
All the classes associated with the process of adding or editing a book share one DataContext.
An unrelated piece of advise. You must have a separate DataContext for each user at the minimum (e.g. place it in HttpSession). Or if you don't keep uncommitted state between requests - maybe even one DataContext per request. In other words - do not share DataContexts unless they are read-only. Otherwise your app is not thread-safe.
Upvotes: 1