Search code examples
jpaoracle11geclipselinkjta

Oracle proxy authentication: Rollback of whole transaction


Using Glassfish 4.1, Eclipselink 2.5.1, Oracle 11g.

We're having a problem rolling back changes when persisting a one-to-many (parent-children) relationship using Oracle proxy authentication. If any exception is thrown when persisting one of the children, the parent will still be persisted to the DB (not rolled back as expected). We save to DB from a stateless EJB with a container-managed JTA entitymanager with:

entitymanager.persist(parent);

cascade = CascadeType.ALL is used on the relationship on the parent side.

Our persistence.xml contains <persistence-unit name="admin_war_1.0-SNAPSHOTPU" transaction-type="JTA"> and the problem at hand is the only one we have in the persistence layer (so far, everything else is working fine).

The parent entity is something like:

public class KornstoranalyseStd implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @Column(name = "KORNSTORANALYSE_STD_ID", nullable = false)
    private Integer kornstoranalyseStdId;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "kornstoranalyseStd", fetch = FetchType.EAGER)
    @OrderBy("maskestoerrelse DESC")
    private Collection<KornstoranalyseStdSigte> kornstoranalyseStdSigteCollection;
}

And the child entity is:

public class KornstoranalyseStdSigte implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @Basic(optional = false)
    @Column(name = "KORNSTORANALYSE_STD_SIGTE_ID", nullable = false)
    private Integer kornstoranalyseStdSigteId;

    @Basic(optional = false)
    @NotNull
    @Column(name = "MASKESTOERRELSE", nullable = false, precision = 12, scale = 8)
    private BigDecimal maskestoerrelse;

    @JoinColumn(name = "KORNSTORANALYSE_STD_ID", referencedColumnName = "KORNSTORANALYSE_STD_ID", nullable = false)
    @ManyToOne(optional = false, fetch = FetchType.EAGER)
    private KornstoranalyseStd kornstoranalyseStd;
}

The following code is for testing only:

    KornstoranalyseStd parent = new KornstoranalyseStd();
    parent.setKornstoranalyseStdId(1);
    List<KornstoranalyseStdSigte> children = new ArrayList<>();
    KornstoranalyseStdSigte child = new KornstoranalyseStdSigte();
    child.setKornstoranalyseStdSigteId(1);
    child.setMaskestoerrelse(new BigDecimal(11));
    child.setKornstoranalyseStd(parent);
    children.add(child);
    parent.setKornstoranalyseStdSigteCollection(children);
    getEjbFacade().create(parent);

This is the STSB:

@Stateless
public class KornstoranalyseStdFacade {
    @PersistenceContext(unitName = "admin_war_1.0-SNAPSHOTPU")
    private EntityManager em;

    private EntityManager getEntityManager() {
        return em;
    }

    public void create(KornstoranalyseStd entity) {
        getEntityManager().persist(entity);
    }
}

In all calls through the EJB, we proxy the entitymanager with

        getEntityManager().setProperty("eclipselink.oracle.proxy-type", 1);
        getEntityManager().setProperty("PROXY_USER_NAME", loginBean.getUsername());
        getEntityManager().setProperty("PROXY_USER_PASSWORD", loginBean.getPassword());
        getEntityManager().setProperty("eclipselink.jdbc.exclusive-connection.mode", "Always");
        getEntityManager().setProperty("eclipselink.jdbc.exclusive-connection.is-lazy", "true");

The above lines are in an @AroundInvoke-method, and will therefore be run no matter what EJB-method is accessed.

The problem is only present when proxying, not if we omit that part. It seems the problem is related to the jdbc-connections being autocommitted. We've tried all kinds of parameters in the Glassfish Connection pool; relaxAutoCommit=true, AutoCommit=false etc., but nothing has changed anything.

How can we ensure, that the parent is also rolled back, when persisting a child fails?


Solution

  • The quick solution seems to be:

    • End the ejb-method with em.flush();

    That'll make the parent roll back too - don't ask me why.

    Note that the exception thrown when persisting a child entity fails will be different (e.getCause() will be different, in case you show to the enduser, but it will still be wrapped in an EJBException).

    Another solution is:

    • Use RESOURCE_LOCAL and non-jta-data-source as written in other answer. You'll have to inject the EntityManagerFactory in the EJB and do the transaction handling yourself.

    I didn't find a way to configure me out of it.