views:

37

answers:

2

Hello,

I have entities: Post, User, Comment with bidirectional relationships:

---------   1     *  ---------
| Post  | <--------> |Comment|
---------            ---------

---------  1      *  ---------
| User  | <--------> |Comment|
---------            ---------

I have simple page which displays single post and all its comments and users who wrote them. At the bottom I have simple textarea when logged in user can post comment. When Submit is clicked method saveComment from backing bean is invoked. Now I type comment click Submit action saveComment navigate to the same page. saveComment looks like this:

String username = getLoginName();
if(username != null)
{
    User u = userBean.getUser(username);
    post = postBean.getPost(post.getId());
    comment.setPost(post);
    comment.setUser(u);
    commentBean.updateComment(comment);
    post = postBean.getPost(post.getId());
}
return "viewpost?faces-redirect=true";

viewpost looks like this:

<ui:define name="content">
    <h:outputText value="#{postController.post.title}"/>
    <br/>
    <h:outputText value="#{postController.post.body}"/>
    <br/>
    <h:dataTable value="#{postController.post.comments}" var="item">
        <h:column>
            <h:outputText value="#{item.body}"/>
            <br/>
            <h:outputText value="#{item.user.username}"/>
            <br/>
        </h:column>
    </h:dataTable>
    <h:form rendered="#{postController.loggedIn}">
    <h:inputTextarea value="#{postController.comment.body}"/>
    <h:commandButton value="Submit" action="#{postController.saveComment}"/>
    </h:form>
</ui:define>

However when I click Submit comment is saved to the database but its not rendered. Doesn't post = postBean.getPost(post.getId()) which is basically return em.find(Post.class, id) should reload post, so that new comment should be available?

There is also viewPost action in backing bean which does the same post = postBean.getPost(post.getId()), but again it seems like data is not loaded from database. I deleted comments from database but they still were listed in viewpost page.

A: 

Post can be cached. What is @ID in your Post? If there is Post with specified id in cache, it is taken from cache not from database. Try to disable cache.

I'm not sure, but I think

return "viewpost?faces-redirect=true";

won't work. JSF navigation handler takes this string, and checks if it is configured in faces-config.xml. If it is not there, it attaches default extension (probably .xhtml) and tries to redirect to such page. So it probably tries to redirect you to viewpost?faces-redirect=true.xhtml which obviously doesn't exist.

amorfis
`viewpost?faces-redirect=true` works.
l245c4l
@amorfis: This is part of JSF 2.0. Implicit navigation. No faces-config anymore. The `?faces-redirect=true` replaces the `<redirect/>` in old-style navigation case.
BalusC
How to disable cache?
l245c4l
What persistence provider are you using? E.g. in eclipselink you have to add to persistence.xml: <property name="eclipselink.query-results-cache" value="false"/> or on named query: setHint(QueryHints.CACHE_USAGE, CacheUsage.DoNotCheckCache). Look here: http://stackoverflow.com/questions/2809275/disable-caching-in-jpa-eclipselink
amorfis
+2  A: 

You shouldn't have to hit the database (with EntityManager#refresh(), EntityManager.find() won't hit the database if the post instance is already loaded in the persistence context) if you set the bidirectional associations appropriately.

The problem is that you don't and your object graph is broken after adding a comment, hence the need to reload the post from the database (which is actually a workaround of the real mistake).

Basically, your model is like this:

+-------+   1     *  +-------+  *      1  +-------+
| Post  | <--------> |Comment| <--------> |  User |
+-------+            +-------+            +-------+

So you need to set both sides of all bidirectional associations correctly when adding a comment:

String username = getLoginName();
if(username != null)
{
    User u = userBean.getUser(username);
    post = postBean.getPost(post.getId());

    post.getComments().add(comment);
    comment.setPost(post);

    u.getComments().add(comment);
    comment.setUser(u);

    commentBean.updateComment(comment);

    //post = postBean.getPost(post.getId()); // you should not have to do this
}
return "viewpost?faces-redirect=true";

"Setting both sides of the link of a bidirectional association" is usually done in helper methods on the entities, for example in Post:

@Entitysamples
public class Post {
    ...
    public void addToComments(Comment comment) {
        this.comments.add(comment);
        comment.setPost(this);
    }
}

This is less error prone, easier to use, easier to read.

Anyway, the key is to set both sides of your associations, you shouldn't have to force a reload from the database to "fix" your object graph.

Pascal Thivent