Search code examples
jsfglassfishejbcdijta

Rollback in ManagedBean on a project JSF + EJB + CDI + JAX-RS + GlassFish 4.1


I'm starting a project that combines "JSF + EJB + CDI + JAX-RS + GlassFish 4.1"

After reading some books and make some research, I managed to make the perfect integration of these technologies. Everything works fine except the transaction control.

To help the understanding of the problem, I created a test project https://github.com/douglasjunior/TestRollbackJsfEjbCdiRest

The problem is: In ManagedBean, is not running Rollback the transaction when an exception is thrown.

But the same question in a JAX-RS resource, works perfectly.

In my example, I am trying to insert a duplicate record in the database. Where should I receive the exception DuplicatedKey. Therefore, the transaction should be reversed and nothing should be inserted in the database.

But when this is done in ManagedBean, the exception does not rollback the transaction. The first record is committed.

GenericDao.java

@Stateless
public class GenericDao implements Serializable {

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

    public void persist(Object entity) {
        getEntityManager().persist(entity);
        getEntityManager().flush();
    }
}

User.java

@Entity
@Table(name = "table_user")
@XmlRootElement
public class User extends AbstractEntity<Long> {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(length = 100, unique = true)
    private String someText;

}

TestManagedBean.java (rollback not work)

@Named(value = "testManagedBean")
@ViewScoped
@TransactionManagement(TransactionManagementType.CONTAINER) // I tested also as BEAN
public class TestManagedBean extends AbstractManagedBean {

    @Inject
    private GenericDao dao;

    private User user;

    public TestManagedBean() {
        user = new User();
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRED) // I tested also as REQUIRES_NEW
    public String test() {
        if (user.getSomeText() == null || user.getSomeText().isEmpty()) {
            sendErrorMessage("Text is riquired!");
            return null;
        }
        dao.persist(user);
        User user2 = new User();
        user2.setSomeText(user.getSomeText()); // "someText" is unique on database
        dao.persist(user2); // org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "table_user_sometext_key"
        return null;
    }

    public User getUser() {
        return user;
    }    
}

TestResource.java (rollback works fine)

@Path("test")
@Stateless
public class TestResource {

    @Inject
    private GenericDao dao;

    @Context
    private UriInfo context;

    public TestResource() {
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public User getTeste(@QueryParam("someText") String someText) {
        if (someText == null || someText.isEmpty()) {
            throw new WebApplicationException("Text is riquired!", Response.Status.BAD_REQUEST);
        }
        System.out.println("someText: " + someText);
        User user = new User();
        user.setSomeText(someText);
        dao.persist(user);
        User user2 = new User();
        user2.setSomeText(user.getSomeText()); // "someText" is unique on database
        dao.persist(user2); // org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "table_user_sometext_key"
        return user;
    }
}

Solution

  • Change your class as below:

    @Transactional 
    public class TestManagedBean extends AbstractManagedBean {
    

    And your method as below:

    @Transactional
    public String test() {