简体   繁体   中英

Unable to refresh JPA 2 Entity injected into a JSF 2 Managed Bean

I have a JSF managed bean in session scope that contains an entity to trace, for example, the authenticated user:

@ManagedBean
@SessionScoped
public class LoginController implements Serializable {
  User user;

  public User getUser() {
    return this.user;
  }

  public void setUser(User user) {
    this.user = user;
  }
  /* ... */
}

In another bean I have to inject the user to use it to retrieve a list of roles in association with it, like this:

@ManagedBean
@ViewScoped
public class AnotherController implements Serializable {

  List<Role> roles;      

  @ManagedProperty(value="#{loginController.user}")
  User user;

  public someMethod() {
    /* Some stuff that insert other roles into database, referring the user as owner */
    roles = user.getRolesList();
  }
}

If I update the page with ajax using someMethod , the roles list still not reload. If I insert em.refresh(user) before user.getRolesList I get this error:

Can not refresh not managed object: model.User[ id=1 ].

Can anyone help me to understand this? Why session scoped entity get not managed if injected into another bean? How can I fix this? Thank you.

In order for the entitiy to be able to refresh, it needs to be managed, but you know that already. For it to be mananged, it needs to be either

  • refetched
  • merged and then refreshed
  • remain managed with an extended persistence context

First two options require a transaction.

Since neither @ManagedBean nor @ViewScoped imply any kind of transaction management, the entities in these beans will always be detached, so the behaviour you are experiencing is the expected JPA behaviour.

For the first two options you can pass your request to a transaction-enabled EJB in the backend, which will either merge and update the entity or return a freshly fetched one. If you are not using a Java EE application server, you can use a UserTransaction .

For the third option, you could use an extended persistence context, which does not get closed after each transaction, so the entities remain managed across transaction borders.

EDIT The simplest option for fixing this, using UserTransaction and assuming dependency injection.

@Inject
UserTransaction tx;

    //merging and refreshing
    tx.begin();
    User managedUser = em.merge(user);
    em.refresh(managedUser);
    tx.commit();
    user = managedUser;

    //refetching
    tx.begin();
    user = em.find(User.class, user.getId);
    tx.commit();

First of all, you shall separate your concerns, which basically means that your backing bean should not be performing any business logic directly (which is implied by your comment /* Some stuff that insert other roles into database, referring the user as owner */ ). Instead, you could inject a UserService class in your managed bean with @EJB annotation and call its methods via actions of your commandComponents.

Next, when you get your entity from a database and detach it from persistence context (read: persistence context is closed at the end of the transaction) it means that the entity is not managed by your persistence service anymore and changes to your entity will not be reflected in the database. If you would want to do so, you will need to call EntityManager.merge() , to make the entity persistent. You will need to do so when you want the canges in your LoginController.user object to be persisted to the database.

So, what Kostja says is when you want to get an up-to-date correspondence between your user object and the row in the database you should make the object managed by your EntityManager , by following one of the ways that he proposed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM