如何在groovy中回滚事务

时间:2012-07-03 10:20:23

标签: sql spring groovy transactions

我不能在我的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。

2 个答案:

答案 0 :(得分:14)

好的,对于那些一直在敲击键盘的人来说,准备好尖叫。它是:使用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等常见应用服务器开发应用程序的人来说,发生了很多事情。所以,不要使用基本的JDBC代码来创建与此硬编码引用的连接,当你只使用一个硬编码或更好的是,属性文件存储或数据库存储的JNDI数据源名称。你现在要做什么?您似乎必须使用DataSource对象,而不享受任何现代RDBMS的最常见和最有价值的功能,即事务。

这是解决方法,这里你真的需要准备好尖叫,因为它完全没有感性,但它有效。您需要像上面一样创建一个DataSource对象,然后在Sql对象的构造函数而不是DataSource对象中使用它的Connection对象。这是荒谬的,这是修复。例如:

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仍然可以运行。我推测只要sqlobj在范围内,VM就足够聪明,可以使sqlobj保持活动状态。这是有道理的,因为dsobj的Connection对象无疑被视为服务器管理的资源,因此任何附加到它的对象都保持活动状态,直到引用封闭订阅对象(即sqlobj)的所有其他对象最终都超出范围。只是一个猜测。

那么,这个问题的吸烟枪在哪里?就在这里:

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

我引用:

<强>提交 public void commit()       抛出java.sql.SQLException如果此SQL对象是使用Connection创建的,则此方法将提交连接。 如果此SQL对象是从DataSource创建的,则此方法不执行任何操作。 抛出:    java.sql.SQLException - 如果发生数据库访问错误

<强>回滚 public void rollback()       抛出java.sql.SQLException如果此SQL对象是使用Connection创建的,则此方法将回滚连接。 如果此SQL对象是从DataSource创建的,则此方法不执行任何操作。 抛出:   java.sql.SQLException - 如果发生数据库访问错误

在生活中思考的另一个谜......希望这会有所帮助。

答案 1 :(得分:0)

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

这意味着您肯定会在Sql.withTransactionSql.cacheConnection内的关闭中获得交易感知,但。我正在尝试使用groovy.sql.Sql在Web服务中协调我们的DAO,并在Web服务调用的整个线程期间使用事务感知来实现Spring或JEE事务管理。

如果我理想地拥有泽西端点和组件,我不应该将我的事务范围限制在DAO层特定块中,因为这是我们的groovy.sql.Sql实例所在的位置;因此,为什么我发现依赖Sql.withTransaction不足以解决交易意识/回滚能力的问题。

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

换句话说,如果您有事务管理处理此连接,那么在其他地方使用no-op实现closeResources;否则,不要覆盖实现。