【发布时间】:2022-01-24 21:46:58
【问题描述】:
我想要一个基于 Java 的事务管理器,它可以在 Web 环境中处理嵌套的数据库事务(来自不同类的数据库调用可以共享一个事务,并且线程安全)。
我相信这是可能的,因为我记得 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
}
}
}
}
在上面的例子中,我希望说明我练习代码的意图,“Person 和 Hobbies 必须都成功完成事务,否则回滚他们的更改。Hobbies 应该能够看到 Person 插入的数据因为它首先发生在交易中。”重要的是,这些发生在不同的类或方法中,但由父级管理,而无需传递连接或事务对象。
我的应用程序当前在 Tomcat 上运行,并使用 Oracle 池连接(OracleDataSource 类)创建数据库连接。它不使用 Tomcat 的数据库上下文。所有连接都使用一个用户/架构/登录名。
实际上,我只有 Java 库可供使用,因为请求新库需要几个月的时间,而且请求经常被拒绝。我现在确实有 Oracle 和 Tomcat,但将来可能会改变,所以我的解决方案必须是纯 Java 的。没有添加 Spring 或 Hibernate 的计划。
这是应用程序中 Java 类结构的简约版本:
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
}
}
}
}
我的目标是在不更改 Data 类的情况下实现这一点(重写 Data 基类很好)。这个想法是让代码类似于 C# 中的事务工作方式(但我也欢迎提出更好的解决方案)。类似于以下内容:
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 ...
}
我目前的思路包括在每次创建事务对象时存储一个静态的 List 或 Map,并在调用 transaction.commit() 时将它们弹出(或者对象被释放并且 AutoCommit 开启)。但是,这只适用于单线程应用程序(如许多桌面应用程序)。我可以通过使用 Map
对于每个范围/代码堆栈都需要唯一事务列表的 Web 应用程序,我不确定如何解决这个问题。我不相信有任何方法可以在当前范围内请求实例化对象(包括父类)。
池连接使这更加复杂,我不能保证任何两个调用共享同一个连接。但是,如果我能找到一个多线程解决方案,我就可以单独担心池连接。
【问题讨论】:
标签: java c# database multithreading transactions