简体   繁体   English

Java 数据库事务管理

[英]Java Database Transaction Management

I would like a Java based transaction manager that can handle nested database transactions in a web environment (database calls from different classes can share a transaction, and thread safe).我想要一个基于 Java 的事务管理器,它可以在 web 环境中处理嵌套数据库事务(来自不同类的数据库调用可以共享事务,并且线程安全)。

I believe this is possible because I recall that C# has something roughly equivalent to this:我相信这是可能的,因为我记得 C# 大致相当于这个:

namespace ConsoleApp
{
    class Program
    {
        public static readonly String CONNECTION_STRING = "x";

        static void Main(String[] args)
        {
            using (var transaction = new TransactionScope())
            {
                (new Person()).insertRecords();
                (new Hobbies()).insertRelatedRecords(); // Can see Person records
                transaction.Dispose();
            }
            (new Hobbies()).insertRelatedRecords(); // Can't see Person records
        }
    }

    public class Person
    {
        public void insertRecords() {
            using (SqlConnection conn = new SqlConnection(Program.CONNECTION_STRING)) {
                conn.Open();
                // insert records
            }
        }
    }

    public class Hobbies
    {
        public void insertRelatedRecords() {
            using (SqlConnection conn = new SqlConnection(Program.CONNECTION_STRING)) {
                conn.Open();
                // insert records
            }
        }
    }
}

In the example above, I am hoping to illustrate my intent to exercise the code, "Person and Hobbies must both complete the transaction successfully, otherwise roll back both of their changes. Hobbies should be able to see the data inserted by Person because it happened first in the transaction."在上面的示例中,我希望说明我练习代码的意图,“Person 和 Hobbies 必须都成功完成事务,否则回滚他们的更改。Hobbies 应该能够看到 Person 插入的数据,因为它发生了交易中的第一名。” It is important that these are happening in different classes or methods, but managed from the parent without having to pass around a connection or transaction object.重要的是,这些发生在不同的类或方法中,但由父级管理,而无需传递连接或事务 object。

My application currently runs on Tomcat and creates database connections using an Oracle pooled connection (OracleDataSource class).我的应用程序当前在 Tomcat 上运行,并使用 Oracle 池连接(OracleDataSource 类)创建数据库连接。 It doesn't use Tomcat's database context.它不使用 Tomcat 的数据库上下文。 A single user/schema/login is used for all connections.单个用户/模式/登录用于所有连接。

Realistically I only have Java librairies available to use, as requesting new libraries takes months and requests are often denied.实际上,我只有 Java 库可供使用,因为请求新库需要几个月的时间,而且请求经常被拒绝。 I do have Oracle and Tomcat right now, but these may be changed in the future, so my solution must be in pure Java.我现在确实有 Oracle 和 Tomcat,但将来可能会改变,所以我的解决方案必须是纯 Java。 There are no plans to add Spring or Hibernate.没有计划添加 Spring 或 Hibernate。

Here is a minimalistic version of the Java class structure in the application:这是应用程序中 Java class 结构的简约版本:

public class Program {

    public static void main(String[] args) 
    {
        try {
            Program program = new Program();
            (program.new Person()).insertRecords();
            (program.new Hobbies()).insertRelatedRecords();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    // Core classes //
    
    public static class DbDataSource 
    {
        private static DataSource dataSource;
        
        public static synchronized DataSource getDataSource() throws SQLException {
            if (dataSource == null) {
                OracleDataSource ods = new OracleDataSource();
                ods.setDriverType("x");
                ods.setURL("x");
                ods.setUser("x");
                ods.setPassword("x");
                dataSource = ods;
            }
            return dataSource;
        }
    }
    
    public class DbResource implements AutoCloseable
    {
        private Connection connection;
        
        public synchronized Connection getConnection() throws SQLException {
            if (connection == null) {
                connection = DbDataSource.getDataSource().getConnection();
            }
            return connection;
        }

        @Override
        public void close() throws Exception {
            if (connection != null) {
                connection.close();
                connection = null;
            }
        }
    }
    
    // Data base classes //
    
    public class DbClass 
    {
        private DbResource db;
        
        protected synchronized DbResource getDatabaseResource() {
            if (db == null) {
                db = new DbResource();
            }
            return db;
        }
    }
    
    // Data classes //
    
    public class Person extends DbClass
    {
        public void insertRecords() throws Exception {
            try (DbResource db = this.getDatabaseResource()) {
                // insert records
            }
        }
    }
    
    public class Hobbies extends DbClass
    {
        public void insertRelatedRecords() throws Exception {
            try (DbResource db = this.getDatabaseResource()) {
                // insert records related to Person
            }
        }
    }

}

My goal is to implement this without changing the Data class (rewriting the Data base class is fine).我的目标是在不更改数据 class 的情况下实现这一点(重写数据库 class 很好)。 The idea would be to have code that resembles how transactions work in C# (but I'm also welcoming a better proposed solution).这个想法是让代码类似于 C# 中的事务工作方式(但我也欢迎提出更好的解决方案)。 Something like the following:类似于以下内容:

public class Program {

    public static void main(String[] args) {
        try {
            try (DbTransaction transaction = new DbTransaction()) {
                Program program = new Program();
                (program.new Person()).insertRecords();
                (program.new Hobbies()).insertRelatedRecords(); // Can see person records
                transaction.rollback();
            }
            (program.new Hobbies()).insertRelatedRecords(); // Can't see person records
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    // ... rest of the class ...
}

My current line of thinking include storing a static List or Map of transaction objects every time they are created, and pop them off when transaction.commit() is called (or the object is disposed and AutoCommit is on).我目前的思路包括在每次创建事务对象时存储一个 static 列表或 Map ,并在调用 transaction.commit() 时将它们弹出(或 object 被处理) However, this only works in a single threaded application (like many Desktop applications).但是,这只适用于单线程应用程序(如许多桌面应用程序)。 I could restrict it to the current web session, by using a Map<Session, List>, but this is still not thread safe within the session.我可以通过使用 Map<Session, List> 将其限制为当前的 web session,但这在 session 中仍然不是线程安全的。 If a user opens multiple pages while they are still loading, this would fail to isolate them.如果用户在加载时打开多个页面,这将无法隔离它们。

I'm not sure how to solve this for a web application that requires unique transaction lists for each scope/code stack.对于需要每个作用域/代码堆栈的唯一事务列表的 web 应用程序,我不确定如何解决此问题。 I don't believe there is any way to ask for instantiated objects within the current scope (that would include parent classes).我不相信有任何方法可以在当前 scope (包括父类)中请求实例化对象。

This is further complicated by pooled connections, where I cannot guarantee that any two calls share the same connection.池连接使这更加复杂,我不能保证任何两个调用共享相同的连接。 However, if I could figure out a multi-threaded solution, I can worry about pooled connections separately.但是,如果我能找到一个多线程解决方案,我就可以单独担心池连接。

The Oracle client already contains this functionality. Oracle 客户端已经包含此功能。 On your Oracle connection, run the .setTransactionIsolation(TRANSACTION_READ_UNCOMMITTED) method to allow dirty reads within a transaction.在您的 Oracle 连接上,运行.setTransactionIsolation(TRANSACTION_READ_UNCOMMITTED)方法以允许在事务中进行脏读。 Then, disable auto-commit mode to allow you to manually commit or rollback operations on the connection using setAutoCommit(false) .然后,禁用自动提交模式以允许您使用setAutoCommit(false)手动提交或回滚连接上的操作。 Alternatively, you could also use Savepoints.或者,您也可以使用保存点。

All of these are detailed in Oracle's JDBC tutorial .所有这些在Oracle 的 JDBC 教程中有详细说明。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM