Java J2EE/JPA:控制事务隔离

Java J2EE/JPA:控制事务隔离,java,jakarta-ee,jpa,transactions,glassfish,Java,Jakarta Ee,Jpa,Transactions,Glassfish,我希望在J2EE环境中对相同的持久性上下文使用不同的事务隔离级别。例如: UserTransaction ut=...; EntityManagerFactory emf=...; EntityManager em=emf.createEntityManager; ut.begin(); em.joinTransaction(); => use RepeatableRead isolation here ... ut.commit(); ut.begin(); em.joinTrans

我希望在J2EE环境中对相同的持久性上下文使用不同的事务隔离级别。例如:

UserTransaction ut=...;
EntityManagerFactory emf=...;
EntityManager em=emf.createEntityManager;

ut.begin();
em.joinTransaction(); => use RepeatableRead isolation here
...
ut.commit();

ut.begin();
em.joinTransaction(); => use Serializable isolation here
...
ut.commit();
我没有办法做到这一点。当EM第一次获取DB连接时,它将从池中获取,并立即与当前XA事务一起登记。之后,再也不可能更改该DB事务的事务隔离

public class CustomDataSource extends MysqlXADataSource {

private static ThreadLocal<Integer> isolation=new ThreadLocal<Integer>(){
    protected Integer initialValue() {
        return Connection.TRANSACTION_REPEATABLE_READ;
    };
};

public static void setTransactionIsolation(int i){
    isolation.set(i);
}

private static class XAConnectionWrapper implements XAConnection {

    private XAConnection delegate;

    public XAConnectionWrapper(XAConnection delegate) {
        this.delegate = delegate;
    }

    ... delegate methods

    @Override
    public XAResource getXAResource() throws SQLException {
        return new XAResourceWrapper(delegate.getXAResource());
    }

    @Override
    public boolean equals(Object obj) {
        return delegate.equals(obj);
    }

    @Override
    public int hashCode() {
        return delegate.hashCode();
    }
}

private static class XAResourceWrapper implements XAResource {
    private XAResource delegate;
    private Field field;

    ... delegate methods ...

    public void start(Xid xid, int flags) throws XAException {
        try {
            ConnectionImpl connection = (ConnectionImpl) field
                    .get(delegate);
            connection
                    .setTransactionIsolation(isolation.get());
        } catch (IllegalArgumentException | IllegalAccessException
                | SQLException e) {
            throw new RuntimeException(e);
        }
        delegate.start(xid, flags);
    }

    public XAResourceWrapper(XAResource delegate) {
        try {
            field = MysqlXAConnection.class
                    .getDeclaredField("underlyingConnection");
            field.setAccessible(true);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        } catch (SecurityException e) {
            throw new RuntimeException(e);
        }
        this.delegate = delegate;
    }

    @Override
    public boolean equals(Object obj) {
        return delegate.equals(obj);
    }

    @Override
    public int hashCode() {
        return delegate.hashCode();
    }
}

@Override
public XAConnection getXAConnection() throws SQLException {
    XAConnection conn = super.getXAConnection();

    return new XAConnectionWrapper(conn);
}

@Override
public XAConnection getXAConnection(String u, String p) throws SQLException {
    return new XAConnectionWrapper(super.getXAConnection(u, p));
}
}

有什么想法吗?

我正在使用Glassfish/EclipseLink/MySQL。我找到的解决方案是定制MySQL数据源,以返回围绕XAResource的包装器,该包装器在启动XA事务之前设置事务隔离。这样,可以在启动事务之前设置所需的事务隔离

public class CustomDataSource extends MysqlXADataSource {

private static ThreadLocal<Integer> isolation=new ThreadLocal<Integer>(){
    protected Integer initialValue() {
        return Connection.TRANSACTION_REPEATABLE_READ;
    };
};

public static void setTransactionIsolation(int i){
    isolation.set(i);
}

private static class XAConnectionWrapper implements XAConnection {

    private XAConnection delegate;

    public XAConnectionWrapper(XAConnection delegate) {
        this.delegate = delegate;
    }

    ... delegate methods

    @Override
    public XAResource getXAResource() throws SQLException {
        return new XAResourceWrapper(delegate.getXAResource());
    }

    @Override
    public boolean equals(Object obj) {
        return delegate.equals(obj);
    }

    @Override
    public int hashCode() {
        return delegate.hashCode();
    }
}

private static class XAResourceWrapper implements XAResource {
    private XAResource delegate;
    private Field field;

    ... delegate methods ...

    public void start(Xid xid, int flags) throws XAException {
        try {
            ConnectionImpl connection = (ConnectionImpl) field
                    .get(delegate);
            connection
                    .setTransactionIsolation(isolation.get());
        } catch (IllegalArgumentException | IllegalAccessException
                | SQLException e) {
            throw new RuntimeException(e);
        }
        delegate.start(xid, flags);
    }

    public XAResourceWrapper(XAResource delegate) {
        try {
            field = MysqlXAConnection.class
                    .getDeclaredField("underlyingConnection");
            field.setAccessible(true);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        } catch (SecurityException e) {
            throw new RuntimeException(e);
        }
        this.delegate = delegate;
    }

    @Override
    public boolean equals(Object obj) {
        return delegate.equals(obj);
    }

    @Override
    public int hashCode() {
        return delegate.hashCode();
    }
}

@Override
public XAConnection getXAConnection() throws SQLException {
    XAConnection conn = super.getXAConnection();

    return new XAConnectionWrapper(conn);
}

@Override
public XAConnection getXAConnection(String u, String p) throws SQLException {
    return new XAConnectionWrapper(super.getXAConnection(u, p));
}
}
public类CustomDataSource扩展了MysqlXADataSource{
私有静态ThreadLocal隔离=新ThreadLocal(){
受保护的整数初始值(){
返回Connection.TRANSACTION\u REPEATABLE\u READ;
};
};
公共静态无效设置TransactionIsolation(int i){
隔离集(i);
}
私有静态类XAConnectionWrapper实现XAConnection{
私人连接代表;
公共XAConnectionWrapper(XAConnection委托){
this.delegate=委托;
}
…委托方法
@凌驾
public XAResource getXAResource()引发SQLException{
返回新的XAResourceWrapper(delegate.getXAResource());
}
@凌驾
公共布尔等于(对象obj){
返回delegate.equals(obj);
}
@凌驾
公共int hashCode(){
返回delegate.hashCode();
}
}
私有静态类XAResourceWrapper实现XAResource{
私人资源代表;
私人领域;
…委托方法。。。
公共void start(Xid-Xid,int标志)引发异常{
试一试{
ConnectionImpl连接=(ConnectionImpl)字段
.获得(代表);
连接
.setTransactionIsolation(隔离.get());
}捕获(IllegalArgumentException | IllegalAccessException
|SQLE(异常){
抛出新的运行时异常(e);
}
委托开始(xid,标志);
}
公共XAResourceWrapper(XAResource委托){
试一试{
field=MysqlXAConnection.class
.getDeclaredField(“基础连接”);
字段。setAccessible(true);
}捕获(无此字段例外){
抛出新的运行时异常(e);
}捕获(安全异常e){
抛出新的运行时异常(e);
}
this.delegate=委托;
}
@凌驾
公共布尔等于(对象obj){
返回delegate.equals(obj);
}
@凌驾
公共int hashCode(){
返回delegate.hashCode();
}
}
@凌驾
公共XAConnection getXAConnection()引发SQLException{
XAConnection conn=super.getXAConnection();
返回新的XAConnectionWrapper(conn);
}
@凌驾
公共XAConnection getXAConnection(字符串u、字符串p)引发SQLException{
返回新的XAConnectionWrapper(super.getXAConnection(u,p));
}
}

这里有一些有趣的信息给你:@Pat B:谢谢你的链接。该博客讨论了Hibernate/Spring解决方案。据我所见,它不能在JTA环境(如J2EE)中使用,因为事务是由数据源实现启动的,而不是由JPA提供者本身启动的。