使用 Guice 和 JDBC 进行事务 - 解决方案讨论

2024-01-10

在我的应用程序中,我需要将纯 JDBC 与 Guice 一起使用。但是,Guice 不提供任何内置支持来管理事务。 guice-persist只提供基于JPA的支持,我无法使用。

所以我尝试实现一个简单的解决方案来使用 Guice 和 JDBC 管理事务。这是第一个版本:

  1. 使用 TransactionHolder 存储每个线程的事务。

    公共类 JdbcTransactionHolder {

    private static ThreadLocal<JdbcTransaction> currentTransaction = new ThreadLocal<JdbcTransaction>();
    
    public static void setCurrentTransaction(JdbcTransaction transaction) {
        currentTransaction.set(transaction);
    }
    
    public static JdbcTransaction getCurrentTransaction() {
        return currentTransaction.get();
    }
    
    public static void removeCurrentTransaction() {
        currentTransaction.remove();
    }
    

    }

  2. 实现了 JDBC 的事务管理器,目前只有 begin()、getTransaction()、commit() 和 rollback() 方法:

    公共类 JdbcTransactionManager 实现 TransactionManager {

    @Inject
    private DataSource dataSource;
    
    @Override
    public void begin() throws NotSupportedException, SystemException {
        logger.debug("Start the transaction");
        try {
            JdbcTransaction tran = JdbcTransactionHolder.getCurrentTransaction();
            Connection conn = null;
            if(tran == null) {
                conn = dataSource.getConnection();
            }
            else {
                conn = tran.getConnection();
            }
    
            // We have to put the connection in the holder so that we can get later
            // from the holder and use it in the same thread
            logger.debug("Save the transaction for thread: {}.", Thread.currentThread());
            JdbcTransactionHolder.setCurrentTransaction(new JdbcTransaction(conn));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    
    }
    
    @Override
    public void commit() throws RollbackException, HeuristicMixedException,
            HeuristicRollbackException, SecurityException,
            IllegalStateException, SystemException {
        logger.debug("Commit the transaction");
        try {
            logger.debug("Get the connection for thread: {}.", Thread.currentThread());
            Transaction transaction = JdbcTransactionHolder.getCurrentTransaction();
            transaction.commit();
    
        }
        catch(Exception e) {
            throw new RuntimeException(e);
        }
        finally {
            JdbcTransactionHolder.removeCurrentTransaction();
        }
    }
    
    @Override
    public Transaction getTransaction() throws SystemException {
        logger.debug("Get transaction.");
        final JdbcTransaction tran = JdbcTransactionHolder.getCurrentTransaction();
        if(tran == null) {
            throw new DBException("No transaction is availble. TransactionManager.begin() is probably not yet called.");
        }
    
        return tran;
    }
    
    @Override
    public void rollback() throws IllegalStateException, SecurityException,
            SystemException {
        logger.debug("Rollback the transaction");
    
        try {
            logger.debug("Get the transaction for thread: {}.", Thread.currentThread());
            Transaction conn = JdbcTransactionHolder.getCurrentTransaction();
            conn.commit();
        }
        catch(Exception e) {
            throw new RuntimeException(e);
        }
        finally {
            JdbcTransactionHolder.removeCurrentTransaction();
        }
    }
    

    }

  3. 实现 DataSource 的包装器,如果事务已启动,它可以从事务持有者获取当前连接:

    公共类 JdbcDataSource 实现 DataSource {

    private final static org.slf4j.Logger logger = LoggerFactory.getLogger(JdbcDataSource.class);
    
    private DataSource dataSource;
    
    public JdbcDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    
    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return dataSource.getLogWriter();
    }
    
    @Override
    public int getLoginTimeout() throws SQLException {
    
        return dataSource.getLoginTimeout();
    }
    
    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
    
        return dataSource.getParentLogger();
    }
    
    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {
        this.dataSource.setLogWriter(out);
    }
    
    @Override
    public void setLoginTimeout(int seconds) throws SQLException {
        this.dataSource.setLoginTimeout(seconds);
    }
    
    @Override
    public boolean isWrapperFor(Class<?> arg0) throws SQLException {
        return this.isWrapperFor(arg0);
    }
    
    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
    
        return this.unwrap(iface);
    }
    
    @Override
    public Connection getConnection() throws SQLException {
        JdbcTransaction transaction = JdbcTransactionHolder.getCurrentTransaction();
        if(transaction != null) {
            // we get the connection from the transaction
            logger.debug("Transaction exists for the thread: {}.", Thread.currentThread());
            return transaction.getConnection();
        }
        Connection conn = this.dataSource.getConnection();
        conn.setAutoCommit(false);
        return conn;
    }
    
    @Override
    public Connection getConnection(String username, String password)
            throws SQLException {
        JdbcTransaction transaction = JdbcTransactionHolder.getCurrentTransaction();
        if(transaction != null) {
            // we get the connection from the transaction
            logger.debug("Transaction exists for the thread: {}.", Thread.currentThread());
            return transaction.getConnection();
        }
    
        return this.dataSource.getConnection(username, password);
    }
    

    }

  4. 然后创建一个 DataSourceProvider,以便我们可以使用 guice 将 DataSource 注入到任何 POJO:

    公共类 DataSourceProvider 实现 Provider {

    private static final Logger logger = LoggerFactory.getLogger(DataSourceProvider.class);
    
    private DataSource dataSource;
    
    
    public DataSourceProvider() {
    
        JdbcConfig config = getConfig();
        ComboPooledDataSource pooledDataSource = new ComboPooledDataSource();
    
        try {
            pooledDataSource.setDriverClass(config.getDriver());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    
        pooledDataSource.setJdbcUrl(config.getUrl());
        pooledDataSource.setUser(config.getUsername());
        pooledDataSource.setPassword(config.getPassword() );
        pooledDataSource.setMinPoolSize(config.getMinPoolSize());
        pooledDataSource.setAcquireIncrement(5);
        pooledDataSource.setMaxPoolSize(config.getMaxPoolSize());
        pooledDataSource.setMaxStatements(config.getMaxStatementSize());
        pooledDataSource.setAutoCommitOnClose(false);
    
        this.dataSource = new JdbcDataSource(pooledDataSource);
    }
    
    
    private JdbcConfig getConfig() {
    
        JdbcConfig config = new JdbcConfig();
        Properties prop = new Properties();
        try {
            //load a properties file from class path, inside static method
            prop.load(JdbcConfig.class.getResourceAsStream("/database.properties"));
    
            //get the property value and print it out
            config.setDriver(prop.getProperty("driver"));
            config.setUrl(prop.getProperty("url"));
            config.setUsername(prop.getProperty("username"));
            config.setPassword(prop.getProperty("password"));
    
            String maxPoolSize = prop.getProperty("maxPoolSize");
            if(maxPoolSize != null) {
                config.setMaxPoolSize(Integer.parseInt(maxPoolSize));
            }
    
            String maxStatementSize = prop.getProperty("maxStatementSize");
            if(maxStatementSize != null) {
                config.setMaxStatementSize(Integer.parseInt(maxStatementSize));
            }
    
            String minPoolSize = prop.getProperty("minPoolSize");
            if(minPoolSize != null) {
                config.setMinPoolSize(Integer.parseInt(minPoolSize));
            }
        } 
        catch (Exception ex) {
            logger.error("Failed to load the config file!", ex);
            throw new DBException("Cannot read the config file: database.properties. Please make sure the file is present in classpath.", ex);
        }
    
        return config;
    }
    
    @Override
    public DataSource get() {
        return dataSource;
    }
    
  5. 然后实现 TransactionalMethodInterceptor 来管理带有 Transactional 注解的方法的事务:

    公共类 TransactionalMethodInterceptor 实现 MethodInterceptor {

    private final static Logger logger = LoggerFactory.getLogger(TransactionalMethodInterceptor.class);
    
    @Inject
    private JdbcTransactionManager transactionManager;
    
    @Override
    public Object invoke(MethodInvocation method) throws Throwable {
    
        try {
            // Start the transaction
            transactionManager.begin();
            logger.debug("Start to invoke the method: " + method);
    
            Object result = method.proceed();
            logger.debug("Finish invoking the method: " + method);
            transactionManager.commit();
            return result;
        } catch (Exception e) {
            logger.error("Failed to commit transaction!", e);
            try {
                transactionManager.rollback();
    
            }
            catch(Exception ex) {
                logger.warn("Cannot roll back transaction!", ex);
            }
            throw e;
        }
    }
    

    }

  6. 最后,将所有代码放在一起以便 Guice 可以注入实例:

    bind(DataSource.class).toProvider(DataSourceProvider.class).in(Scopes.SINGLETON);
    
    bind(TransactionManager.class).to(JdbcTransactionManager.class);
    TransactionalMethodInterceptor transactionalMethodInterceptor = new TransactionalMethodInterceptor();
    requestInjection(transactionalMethodInterceptor);
    bindInterceptor(Matchers.any(), Matchers.annotatedWith(Transactional.class), transactionalMethodInterceptor);
    
    
    bind(TestDao.class).to(JdbcTestDao.class);
    bind(TestService.class).to(TestServiceImpl.class);
    

我使用 c3p0 作为数据源池。所以,它在我的测试中工作得很好。

我发现另一个相关问题:Guice、JDBC 和管理数据库连接 https://stackoverflow.com/questions/2347384/guice-jdbc-and-managing-database-connections

但到目前为止,除了 SpringFramework 中的一些方法之外,我还没有找到任何类似的方法。但即使是 Spring 中的实现也显得相当复杂。

我想问是否有人对此解决方案有任何建议。

thanks.


None

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 Guice 和 JDBC 进行事务 - 解决方案讨论 的相关文章

  • Java 中的 ExecuteUpdate sql 语句不起作用

    我正在学习如何将 SQL 与 Java 结合使用 我已成功安装 JDBC 驱动程序 并且能够从数据库读取记录并将其打印在屏幕上 我的问题发生在尝试执行更新或插入语句时 没有任何反应 这是我的代码 问题所在的方法 public static
  • ARJUNA012140:不允许添加多个最后资源

    我有 2 个数据源 在一个方法中 我需要从 2 个数据库中写入和读取 数据库是 PostgreSQL 我的 EAR 应用程序在 wildfly 16 上运行 我无法以同一方法使用 2 个数据库连接 好的 我知道 因为系统无法管理跨不同数据库
  • SQL Server:删除具有外键约束的行:事务可以覆盖约束吗?

    我有一些添加了外键约束的表 它们与代码生成一起使用 以在生成的存储过程中设置特定的联接 是否可以通过在事务中调用多个删除来覆盖这些约束 特别是 C 中的 TransactionScope 或者绝对需要级联删除吗 不要使用级联删除 这样可能会
  • oracle.jdbc.driver.OracleDriver ClassNotFoundException

    这是我收到错误的代码 我的classes12 jar已作为外部 jar 导入 import java io IOException import java io PrintWriter import java sql Connection
  • Spring Boot:Apache derby 池为空。 30 秒内无法获取连接

    运行一段时间后 我收到此错误 路径 上下文中 servlet dispatcherServlet 的 Servlet service 抛出异常 请求处理失败 嵌套异常是 org springframework orm jpa JpaSyst
  • bean 的 CDI @TransactionAttribute

    我正在尝试CDI在测试应用程序上 我有一个DAO它注入一个托管的容器JTA像这样的持久化上下文 public class TestDAO implements Serializable PersistenceContext private
  • Java 日期和 MySQL 时间戳时区

    我正在编辑一段代码 其基本功能是 timestamp new Date 然后坚持下去timestamp中的变量TIMESTAMPMySQL 表列 然而 通过调试我看到Date显示在正确时区的对象 GMT 1 当持久化在数据库上时 它是GMT
  • 使用 Java 连接到 MySql - SSL 连接

    我一直在尝试连接到 MySql 数据库 该数据库使用 ssl 连接与 java 并遇到麻烦 如果任何人可以帮助我 将会有很大的帮助 手动连接MySql 我们使用MySQL Workbench 参数 主机名 test db1 ro xxxxx
  • 当我在 JDBC 应用程序中调用PreparedStatement.cancel() 时,它实际上会在 Oracle 数据库中杀死它吗?

    我有针对 Oracle 10g 数据库运行的 Java JDBC 应用程序 我设置了一个PreparedStatement来执行查询 然后调用ps executeQuery 来运行它 有时查询需要很长时间 我需要终止它 我有另一个线程访问该
  • 如何将列表 插入数据库

    我是 Java 新手 我已经创建了产品类型的通用列表 如何将其添加到数据库中 该列表包含Products的对象 数据库中的列是Products类的字段 即使我通过 listvariable get 0 等分隔列表 我也会得到对象 而不是该对
  • 我想最小化@Transactional 的范围吗?

    不确定 范围 在这里是否是正确的术语 我使用 Spring 进行 JPA 事务管理 下面有 Hibernate 我执行数据库事务的方法是私有的 但是由于您只能在类或类上设置 Transactional公共方法 http static spr
  • 如何用 JSON 表示数据库中的图像

    我需要基于数据库中的 blob 创建 JSON 为了获取 blob 图像 我使用下面的代码并在 json 数组中显示之后 Statement s connection createStatement ResultSet r s execut
  • DB2 SQL 脚本:如何启动事务并在错误时回滚

    我正在为 DB2 数据库 V9 5 实现 SQL 脚本 以便添加列并重新组织数据 我想从linux命令行执行脚本 db2 vstf migration sql 所有语句在migration sql应该包含在交易中 如果一条语句失败 则必须回
  • com.mysql.jdbc.Driver 的类未找到异常不是类路径问题

    我已经为此工作了几个星期 但现在我只是在原地踏步 我收到运行时错误 在获取连接 com mysql jdbc Driver 时 在 class 中未找到类异常 连接函数如下 return a MySQL connection to the
  • 在 jdbc 程序中使用时,通过 SQL 客户端插入表中的记录未显示

    我使用 SQL 客户端和 JDBC 程序将几行插入到我的表 EMP 中 使用 SQL 客户端插入的记录不会显示在 Java 的输出控制台中 我正在使用 Java 8 和 oracle 11g 数据库来插入和读取数据库 除了通过 SQL 客户
  • 一个 Guice 就绪的安全框架?

    有没有人见过一个为与 Guice 一起工作而编写的框架 或者一个将现有安全系统 即 Acegi 与 Guice 集成的库 到目前为止我发现了以下内容 http code google com p warp security http cod
  • 通过 JDBC 将“daterange”字段值插入 PostgreSQL 表

    我在 PostgreSQL 9 3 有一个表日期范围 http www postgresql org docs 9 3 static rangetypes html字段类型 我可以像使用 JDBC 的字符串一样选择此字段 但无法将其插入表中
  • 将 SQL 数据中的一行映射到 Java 对象

    我有一个 Java 类 其实例字段 以及匹配的 setter 方法 与 SQL 数据库表的列名相匹配 我想优雅地从表中获取一行 到 ResultSet 中 并将其映射到此类的实例 例如 我有一个 Student 类 其中包含实例字段 FNA
  • SQL服务器事务

    我需要了解sql server事务 我浏览了谷歌上的一些文章 但我什么也没理解 谁能帮我 您可以通过写入显式启动事务BEGIN TRANSACTION 您可以通过运行来结束事务COMMIT TRANSACTION 之前COMMIT运行时 受
  • Java JDBC:更改表

    我希望对此表进行以下修改 添加 状态列 varchar 20 日期列 时间戳 我不确定该怎么做 String createTable Create table aircraft aircraftNumber int airLineCompa

随机推荐

  • 在 Spring Batch 中访问步骤范围之外的 Bean

    是否可以访问步骤范围之外定义的 bean 例如 如果我定义策略 strategyA 并将其传递到作业参数中 我希望 Value 解析为strategyA bean 这可能吗 我目前正在通过从 applicationContext 手动获取
  • NPM Start 未启动本地服务器

    我正在尝试使用 webpack 制作一个 React 应用程序 当我尝试运行 npm start 时 它应该加载http localhost 3333但它说无法访问网站 这是我的 webpack 配置 module exports entr
  • 滑动视图控制器,但不使用 UISwipeGestureRecognizer

    这是我的问题 我有 5 个视图控制器 我可以使用 UISwipeGestureRecognizer 类和 xcode 的故事板通过滑动在它们之间切换 所以这可行 但是 我不喜欢幻灯片效果 我喜欢以某种方式进行制作 这样您就可以通过拖动将视图
  • Windows IoT 和 DS3231 RTC 时钟

    对于我的项目 我需要当前时间和日期 不幸的是 当 RP2 关闭时 它就会失去一切 接下来的事情是 我将没有互联网连接来使用 NTP 为此 我需要实现 DS3231 RTC 模块 所有设备的通信都通过 I2C 运行 Raspberry Ard
  • Google Admob Android:仅在一台设备上运行

    我在我的 Android 应用程序中设置了一个 admob adview 清单
  • 修改成员时不调用 C# 对象 Setter

    我有以下包装类 public class Wrapper public int Member 在一个单独的班级中 我有以下内容 public class ContainerClass private Wrapper data public
  • 我的谷歌地图被切断了,我想知道为什么? JavaScript,V

    It s kinda hard to explain so I uploaded a screen shot of the issue 正如您所看到的 尽管地图上有 div 不动产 这是实际大小 但它只显示了地图的 1 6 这个小部件可以调
  • 如何在 Java 中设置标签的颜色(彩色文本)?

    如何设置标签文本的颜色 myLabel setText Text Color Red myLabel 我可以在一个标签上使用两种不同的颜色吗 例如这里 The Text Color 变黑并且 Red 变红 对于单色前景色 label set
  • 为什么 (1 == 2 != 3) 在 Python 中计算结果为 False?

    为什么 1 2 3 评估为False在Python中 同时两者 1 2 3 and 1 2 3 评估为True 这里使用什么运算符优先级 这是由于运营商的连锁现象 https docs python org 3 reference expr
  • 为什么我可以使用类型别名声明 const 引用?

    我有一个简单的问题 据我所知 我可以声明const指向某种数据类型的指针或指向常量数据类型的指针 但我只能声明对常量数据类型的引用 而不能声明对数据类型的常量引用 事实上 引用已经是常量 因为它不能反弹到另一个对象 所以当我尝试创建一个co
  • 当多个文件作为参数传递给 perl cli 时,Perl 中文件的行号

    In awk如果我给出多个文件作为参数awk 有两个特殊变量 NR 对应于所有文件中所有行的行号 FNR 当前文件的行号 我知道在 Perl 中 对应于NR 所有文件中的行中的当前行 有什么可以媲美的FNRPerl 中的 AWK 也有吗 假
  • PBSPro qsub 输出错误文件定向到名称中包含 jobid 的路径

    我正在使用 PBSPro 并尝试使用 qsub 命令行提交作业 但似乎无法按照我想要的方式命名输出和错误文件 目前使用 qsub N subjobname short o path o PBS JOBID e path e PBS JOBI
  • VSTS 持续集成触发器不起作用

    我很确定这个设置在某一时刻对我们来说是有效的 我对我们的构建进行了一些更改以反映一些操作更改 但现在 CI git 分支触发器不起作用 我正在尝试获取它 以便当 PR 合并到 master 时它会触发发布构建 我可以手动触发此构建 但在从
  • 从 csproj 引用 ASP.NET xproj

    我正在使用 Visual Studio 中的新 类库 NuGet 包 模板之一 并且我想为其创建一个 xUnit 测试库 问题是 当我创建新的 csproj 库并尝试引用 xproj 包时 Visual Studio 说 The refer
  • 使用 c++ 中的 boost 进程库输出

    我使用升压过程并使用默认代码主要教程页面 http www highscore de boost process process tutorials html 我已经运行了这段代码 但它没有打印任何输出 include
  • BlackBerry - 在位图字段上调用单击事件

    谁能帮我解决以下问题 我正在为黑莓制作一个应用程序 从一个位图字段我必须通过单击该位图字段来调用一个新屏幕 我想要相同的代码 如何通过单击位图字段来调用新屏幕 我正在使用黑莓 JDE 4 7 尝试使 BitmapField 可聚焦 Bitm
  • Excel 中具有多个条件的 CUBESET() 函数

    我正在尝试在 Excel 中创建 CUBESET 函数 但我不知道如何使用多个条件过滤它同一维度内 这就是我迄今为止所遵循的一个标准 示例1 CUBESET ThisWorkbookDataModel Facebook Bucket C A
  • 有效地找到大型数组中的最低有效设置位?

    我有一个巨大的内存块 位向量 其大小N一个内存页内的位 考虑N平均为 5000 即 5k 位来存储一些标志信息 在某个时间点 超频繁 关键 我需要找到整个大位向量中的第一个位集 现在我对每个 64 个单词执行此操作 即在 builtin c
  • 如何自定义实现asp.net身份的UpdateAsync方法?

    我正在执行自定义 asp net 身份 而不是使用 asp net 内置表 我已成功创建用户并实现自定义CreateAsync 现在我想用新的加密密码更新用户 所以我不知道如何提供自定义实现UpdateAsync method 这是我的桌子
  • 使用 Guice 和 JDBC 进行事务 - 解决方案讨论

    在我的应用程序中 我需要将纯 JDBC 与 Guice 一起使用 但是 Guice 不提供任何内置支持来管理事务 guice persist只提供基于JPA的支持 我无法使用 所以我尝试实现一个简单的解决方案来使用 Guice 和 JDBC