【问题标题】:How to rollback transaction in groovy如何在groovy中回滚事务
【发布时间】:2015-12-12 02:48:43
【问题描述】:

我不能在我的 TestCase 中使用 @Transactional 注释。我有解决方法 - 直接使用 TransactionalManager。不幸的是,当我基于 SpringContext 中的 DataSource 在 groovy 中创建 Sql 对象,然后在数据库中插入一行时,它不会回滚。

@ContextConfiguration(locations = [ "../dao/impl/ibatis/spring-data-context-config.xml"])
@RunWith(SpringJUnit4ClassRunner.class)
public class OrganizationTest {

@Autowired
DataSource dataSource;

@Autowired
DataSourceTransactionManager transactionManager;

private TransactionStatus transactionStatus;

@Before
public void setUp() {
    transactionStatus = transactionManager.getTransaction(new DefaultTransactionDefinition());
}
@After
public void tearDown() {
    transactionManager.rollback(transactionStatus);
    transactionStatus = null;
}


@Test
public void shallObtainSequenceNo() throws Exception {

    Connection connection = dataSource.getConnection();
    connection.setAutoCommit(false);
    Sql sql = new Sql(dataSource);

    //given
    Organization organization = new Organization("KongregatzionIX", "bisut000000000000001");
    //when
    organization.insert(sql);
    //then
    assertNotNull(organization.getId());
    }
}

SQL 查询如下所示:

public class Organization {

String name;
String id;
String parentId;

Organization(String name, String parentId){
    this.name = name;
    this.parentId = parentId;
}

public void insert(Sql sql){
    String createdBy = GlobalConstant.SABA_ADMIN_ID.getValue();
    String updatedBy = GlobalConstant.SABA_ADMIN_ID.getValue();
    String companyType = "2";
    String flags = "1000000000";

    id = sql.firstRow( "select 'bisut' || LPAD(TPT_COMPANY_SEQ.NEXTVAL,  15, '0') as id from dual ").id;

    def timeStamp = sql.firstRow("select  to_char(SYSTIMESTAMP, 'YYYYMMDDHH24MISSFF') as ts FROM DUAL ").ts;
    def nameIns = name;
    def today = new java.sql.Date(new Date().getTime());
    sql.executeInsert('''
                INSERT INTO TPT_COMPANY(ID, TIME_STAMP, CREATED_BY, CREATED_ON, UPDATED_BY, UPDATED_ON, CI_NAME, NAME, CI_NAME2, NAME2, COMPANY_TYPE, FLAGS, PARENT_ID)
                VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
                ''' ,
                [id, timeStamp, createdBy, today, updatedBy, today, nameIns.toLowerCase(), nameIns, nameIns.toLowerCase(), nameIns, companyType, flags, parentId]);
 }
}

当然,我想设置跨越所有测试方法的事务。

// 编辑

由于声誉太小,我无法回答,但 TransactionAwareDataSourceProxy 是我一直在寻找的。​​p>

【问题讨论】:

    标签: sql spring groovy transactions


    【解决方案1】:

    好的,对于任何读到这篇文章的人一直在用头撞键盘,准备好尖叫吧。这里是:使用 DataSource 创建的 Groovy Sql 对象,如下所示:

    DataSource dsobj = null;
    Sql sqlobj = null;
    
    try{
      dsobj = (...get your DataSource obj...);
      sqlobj = new Sql(dsobj);
    } catch (...) {...}
    

    当然会给你一个有效的 Sql 对象,你可以用它运行命令等,但你不会得到事务支持。但是...

    如果您使用 Connection 对象创建 Sql 对象,您将获得该事务支持。现在,为什么会这样,我不知道,因为您可以据说使用以下命令访问 DataSource 对象中包含的 Connection 对象:sqlobj.getDataSource().getConnection()。但是,如果您尝试这样做:

    sqlobj.getDataSource().getConnection().setAutoCommit(false);
    

    它将具有效果。无论您喜欢与否,您运行的任何命令都将自动提交。同样,调用:

    sqlobj.getDataSource().getConnection().rollback();
    

    什么都不做。

    那么,如果您必须从应用服务器获取数据库连接,因为您有组织标准,例如,坚持要求您从应用服务器管理的连接池中获取它们,并且数据源由服务器管理员定义,您会怎么做? ?对于为 IBM WAS 等常见应用程序服务器开发应用程序的人来说,发生了很多事情。因此,当您只能使用一个硬编码或更好的是,JNDI 数据源的属性文件存储或数据库存储名称。你现在要做什么?您似乎必须使用 DataSource 对象,而没有享受任何现代 RDBMS 中最常见和最有价值的特性,即事务。

    这里是变通方法,这里是你真正需要准备尖叫的地方,因为这完全没有意义,但它确实有效。您需要像上面一样创建一个 DataSource 对象,然后在 Sql 对象的构造函数中使用其 Connection 对象,而不是 DataSource 对象。尽管这很荒谬,但这是解决办法。示例:

    DataSource dsobj = null;
    Sql sqlobj = null;
    
    try{
      dsobj = (...get your DataSource obj...);
      sqlobj = new Sql(dsobj.getConnection());
    } catch (...) {...}
    

    现在,您将使用 sqlobj 来执行事务敏感的工作。您可以将 AutoCommit 设置为 false [sqlobj.getConnection().setAutoCommit(false)],运行更新,然后根据需要执行 sqlobj.getConnection().commit() 或 sqlobj.getConnection().rollback()。

    似乎 dsobj 可以超出 sqlobj 的直接范围,并且 sqlobj 仍将起作用。我推测 VM 足够聪明,只要 sqlobj 在范围内,就可以让 sqlobj 保持活动状态。这是有道理的,因为 dsobj 的 Connection 对象无疑被视为服务器管理的资源,因此任何附加到它的对象都会保持活动状态,直到引用封闭订阅对象(即 sqlobj)的所有其他对象最终超出范围.只是猜测。

    那么,这个问题的确凿证据在哪里?在这里:

    http://groovy.codehaus.org/api/groovy/sql/Sql.html#commit()

    我引用:

    提交 公共无效提交() 抛出 java.sql.SQLException 如果此 SQL 对象是使用 Connection 创建的,则此方法提交连接。 如果这个 SQL 对象是从数据源创建的,那么这个方法什么也不做。 抛出: java.sql.SQLException - 如果发生数据库访问错误

    回滚 公共无效回滚() 抛出 java.sql.SQLException 如果此 SQL 对象是使用 Connection 创建的,则此方法回滚连接。 如果这个 SQL 对象是从数据源创建的,那么这个方法什么也不做。 抛出: java.sql.SQLException - 如果发生数据库访问错误

    只是生活中另一个值得思考的谜团......希望这会有所帮助。

    【讨论】:

      【解决方案2】:

      我发现 Matt 的回答很有效。唯一需要注意的是,Sql.closeResources(Connection) 在执行方法和查询调用之后被调用,并且将关闭连接,除非您在 Sql.withTransactionSql.cacheConnection 内。

      这意味着您肯定会获得事务感知,但Sql.withTransactionSql.cacheConnection 内运行的闭包内。我正在尝试在 Web 服务调用的整个线程期间使用 groovy.sql.Sql 与事务感知在 Web 服务中协调我们的 DAO,就像 Spring 或 JEE 事务管理所做的那样。

      如果我有一个 jersey 端点和下面的组件,理想情况下,我不应该将我的事务范围限制在 DAO 层特定的块中,因为那是我们的 groovy.sql.Sql 实例所在的地方;因此,为什么我发现依赖 Sql.withTransaction 不足以解决事务感知/回滚能力。

      供参考: Github Sql.java source 您会注意到许多方法,例如 executeUpdate 执行后将调用 closeResources ,它只跳过关闭连接 if (cacheConnection);因此,只有通过withTransactioncacheTransaction 执行闭包块才能使用commit()rollback(),我相信这是作者的意图。这很好,除了启动和结束与 Web 服务请求的整个范围匹配的事务的限制。这是一个警告,我的解决方案是覆盖closeResources,因为这个类的作者慷慨地使它可以被覆盖。覆盖它以不关闭基于事务活动的连接。

      换句话说,如果您有事务管理以某种方式处理此连接,请在其他地方实现closeResources 无操作;否则,不要覆盖实现。

      【讨论】:

        猜你喜欢
        • 2013-05-17
        • 2014-11-29
        • 1970-01-01
        • 2017-06-14
        • 2017-09-30
        • 1970-01-01
        • 1970-01-01
        • 2010-10-04
        • 2021-10-17
        相关资源
        最近更新 更多