如何在groovy中回滚事务

paw*_*ccb 4 sql groovy spring 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());
    }
}
Run Code Online (Sandbox Code Playgroud)

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]);
 }
}
Run Code Online (Sandbox Code Playgroud)

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

//编辑

我无法回答,因为声誉太小,但TransactionAwareDataSourceProxy是我一直在寻找的.

Mat*_*ell 14

好的,对于那些一直在敲击键盘的人来说,准备好尖叫.它是:使用DataSource创建的Groovy Sql对象,如:

DataSource dsobj = null;
Sql sqlobj = null;

try{
  dsobj = (...get your DataSource obj...);
  sqlobj = new Sql(dsobj);
} catch (...) {...}
Run Code Online (Sandbox Code Playgroud)

当然会为您提供一个有效的Sql对象,您可以使用等运行命令,但是您将得不到任何事务支持.然而...

如果使用Connection对象创建Sql对象,则将获得该事务支持.现在,这是为什么,我也没办法,因为你可以假想访问包含在与数据源对象的Connection对象:sqlobj.getDataSource()的getConnection().但是,如果你试图这样做:

sqlobj.getDataSource().getConnection().setAutoCommit(false);
Run Code Online (Sandbox Code Playgroud)

这将有为零的效果.无论您是否喜欢,您运行的任何命令都将自动提交.同样,呼吁:

sqlobj.getDataSource().getConnection().rollback();
Run Code Online (Sandbox Code Playgroud)

什么都不做

那么,如果您必须从应用程序服务器获取数据库连接,那么您会怎么做,因为您有组织标准,例如,坚持要求从服务器管理员定义的数据源的应用程序服务器管理的连接池中获取它们?对于像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 (...) {...}
Run Code Online (Sandbox Code Playgroud)

现在,您将使用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()

我引用:

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

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

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