Oracle 代理身份验证:回滚整个事务

Oracle proxy authentication: Rollback of whole transaction

使用 Glassfish 4.1、Eclipselink 2.5.1、Oracle 11g。

我们在使用 Oracle 代理身份验证 保持 one-to-many (parent-children) 关系时回滚更改时遇到问题。如果在持久化 children 之一时抛出任何异常,parent 仍将持久化到数据库(不会按预期回滚)。我们使用 container-managed JTA entitymanager 从无状态 EJB 保存到 DB:

entitymanager.persist(parent);

cascade = CascadeType.ALL用在parent这边的关系上。

我们的 persistence.xml 包含 <persistence-unit name="admin_war_1.0-SNAPSHOTPU" transaction-type="JTA"> 并且手头的问题是我们在持久层中遇到的唯一问题(到目前为止,其他一切都工作正常)。

parent 实体类似于:

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;
}

而 child 实体是:

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;
}

以下代码仅供测试:

    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);

这是 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);
    }
}

在通过 EJB 的所有调用中,我们使用

代理实体管理器
        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");

以上行在 @AroundInvoke-method 中,因此无论访问什么 EJB-method,都将是 运行。

这个问题只在代理时出现,如果我们省略那部分则不会。看来问题与自动提交的 jdbc-connections 有关。我们在 Glassfish 连接池中尝试了各种参数; relaxAutoCommit=true、AutoCommit=false 等,但没有任何改变。

当 child 持久化失败时,我们如何确保 parent 也回滚?

我在将 entitymanager 与应用程序服务器中定义的数据源一起使用时遇到了类似的问题。为了解决问题,我这样做了:

1- 我在 persistence.xml 中设置了事务类型="RESOURCE_LOCAL" 并定义了持久性单元

2- 我使用了非 jta 数据源 属性,其值为 myDatasourceName

我意识到异常的根源是jta!

快速解决方案似乎是:

  • em.flush();
  • 结束 ejb 方法

这也会让父级回滚 - 不要问我为什么。

请注意,保留子实体失败时抛出的异常会有所不同(e.getCause() 会有所不同,以防您向最终用户展示,但它仍将包含在 EJBException 中)。

另一个解决方案是:

  • 使用 RESOURCE_LOCAL 和其他答案中所写的非 jta 数据源。您必须在 EJB 中注入 EntityManagerFactory 并自行处理事务。

我没有找到配置我的方法。