Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle persistence context (EntityManager) with jax-rs sub-resource locators?

I am using jax-rs restful web service in my application with sub-resource locators. However after passing entityManager to sub-resource I cannot persist any new objects in this sub-resource.

The entityManager lets me however to query it for data.

This is my main resource:

@Path("/registrations")
@Stateless
public class RegistrationsResource {

    @Context
    private UriInfo context;

    @PersistenceContext(unitName="pctx")
    private EntityManager em;

    public RegistrationsResource() {
    }

    //POST method ommited

    @Path("{regKey}")
    public RegistrationResource getRegistrationResource(@PathParam("regKey")
    String regKey) {
        return RegistrationResource.getInstance(regKey, em);
    }

}

And this is my sub-resource:

public class RegistrationResource {

    private String regKey;
    private EntityManager em;

    private RegistrationResource(String regKey, EntityManager em) {
        this.regKey = regKey;
        this.em = em;
    }

    @Path("securityQuestion")
    @GET
    public String getQuestion() {
        return "iamahuman"+regKey;
    }

    @Path("securityQuestion")
    @POST
    public void postSecurityAnswer(String answer) {
        if(!answer.equals("iamahuman"+regKey)){
            throw new WebApplicationException(Status.BAD_REQUEST);
        }

        //Getting this information works properly
        List<RegistrationEntity> result = em.createNamedQuery("getRegistrationByKey")
            .setParameter("regKey", regKey).getResultList();

        switch(result.size()){
            case 0 :
                throw new WebApplicationException(Status.NOT_FOUND);
            case 1:
                break;
            default:
                throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
            }

            RegistrationEntity reg = result.get(0);
            UserEntity newUser = new UserEntity();

            newUser.setHashedPassword(reg.getPwHash(), reg.getSalt());
            newUser.setUsername(reg.getUsername());
            newUser.setName(reg.getName());
            newUser.setSurname(reg.getSurname());

            //CRASHES HERE
            em.persist(newUser);
    }
}

As you can see it takes registration object from database, creates new user for registration and tries to persist it. However, em.persist(newUser) throws TransactionRequiredException.

My question is: how should I pass EntityManager to sub-resource so it can properly persist new objects?

like image 902
MiKom Avatar asked Apr 20 '11 18:04

MiKom


People also ask

Is persistence context shared?

A PersistenceContext is essentially a Cache. It also tends to have it's own non-shared database connection.

What is EntityManager and persistence context?

An EntityManager instance is associated with a persistence context. A persistence context is a set of entity instances in which for any persistent entity identity there is a unique entity instance. Within the persistence context, the entity instances and their lifecycle are managed.

What does Subresource locator return?

A subresource locator returns an object that will handle an HTTP request. The method must not be annotated with a request method designator. You must declare a subresource locator within a subresource class, and only subresource locators are used for runtime resource resolution.


2 Answers

Sorry for digging this up again, but I suggest the following:

  • Annotate also the sub-resource as a @Stateless EJB
  • Place @EJB injection member fields into the parent resource class, like so:
        @EJB private RegistrationResource registrationResource;
    
  • in "getRegistrationResource()", do not call the constructor of the sub-resource, but return the injected EJB reference:
    public RegistrationResource getRegistrationResource() {
        return this.registrationResource;
    }

For this to work, however, you cannot pass the "@PathParam" as a constructor parameter. You would have to access it seperately in the subresource via the "@Context" or another @Path declaration.
This enables you to inject the EntityManager in the sub resource in exactly the same way as in the parent resource, you don't need to pass it on.

like image 140
Mirko Klemm Avatar answered Oct 01 '22 09:10

Mirko Klemm


Probably too late but anyway... When you return your sub resource you 'leave' the stateless bean. As the container manages the transaction the transaction is committed when you return from RegistrationsResource.

Jersey will then construct your sub resource but its not a stateless bean so you wont have a container managed transaction. Hence the exception.

I would advice you to put your business logic in a new class which you then make a stateless bean. Here you do all your database stuff which then is always handled in a container managed transaction.

like image 43
thehpi Avatar answered Oct 01 '22 10:10

thehpi