2012-07-03 5 views
4

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]); 
} 
} 

내가 모든 시험 방법에 걸쳐있는 트랜잭션을 설정합니다.

// EDIT

나는 때문에 너무 작은 명성에 대답 할 수는 없지만에 TransactionAwareDataSourceProxy 내가 찾고 있던 것입니다.

답변

14

좋습니다. 키보드에서 머리를 대고있는이 사람을 읽으려면 소리지를 준비를하십시오. 그루비는 SQL 객체에서와 같이 데이터 소스를 사용하여 만든 : 여기있다 물론

DataSource dsobj = null; 
Sql sqlobj = null; 

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

의지는 당신에게 당신이 등으로 명령을 실행할 수있는 유효한 SQL 객체를 가져,하지만 당신은 어떤 트랜잭션 지원을 얻을 수 있습니다 . 그러나 ...

Connection 개체를 사용하여 Sql 개체를 만드는 경우 해당 트랜잭션 지원을 받게됩니다. 이제 은 sqlobj.getDataSource(). getConnection()을 사용하여 DataSource 객체에 포함 된 Connection 객체에 액세스 할 수 있기 때문에 왜 그런지 모르겠습니다. 그러나 아직이 작업을 수행하려고하면 :

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

그것이 제로 영향을 미칠 것입니다. 실행중인 명령은 마음에 드는지 여부에 관계없이 자동으로 커밋됩니다. 마찬가지로 전화 :

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

아무 것도하지 않습니다.

예를 들어, 서버에서 정의한 데이터 소스를 사용하는 앱 서버 관리 연결 풀에서 가져 오는 것이 조직의 표준을 가지고 있기 때문에 애플리케이션 서버에서 DB 연결을 얻어야하는 경우 어떻게해야합니까? 관리자? IBM WAS와 같은 일반적인 애플 리케이션 서버를위한 애플 리케이션을 개발하는 사람들에게 많은 일이 일어난다. 따라서 기본적인 JDBC 코드를 사용하여 하드 코드 된 참조를 가진 커넥션을 생성하고 하드 코드 된 더 나은 점은 JNDI 데이터 소스의 등록 정보 파일 저장 또는 데이터베이스 저장 이름입니다. 이제 어떻게 할거 니? 당신은 DataSource 객체를 사용해야 만하고 현대적인 RDBMS의 가장 보편적이고 가치있는 기능인 트랜잭션을 즐기지 않는 것처럼 보입니다.

여기에 해결 방법이 있으며 여기서는 완전히 비관적이기 때문에 실제로 비명을 지을 준비가 필요합니다. 위와 같이 DataSource 개체를 만들어야하지만 DataSource 개체 대신 Sql 개체의 생성자에서 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() 또는 sqlobj.getConnection(). rollback()을 수행 할 수 있습니다.

또한 dsobj가 sqlobj의 직접 범위를 벗어날 수 있으며 sqlobj가 여전히 작동 할 것으로 보입니다. 필자는 sqlobj가 범위 내에있는 한 VM이 sqlobj를 활성 상태로 유지할만큼 충분히 똑똑하다고 추측합니다. dsobj의 Connection 객체는 의심의 여지없이 서버 관리 자원으로 취급되므로 첨부 된 객체 (즉, sqlobj)를 참조하는 다른 모든 객체가 마침내 범위를 벗어날 때까지 활성 객체로 유지됩니다. . 그냥 추측.

그럼이 문제에 대한 총은 어디 있습니까? 그것은 여기 :

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

내가 인용 :

공공 무효가 확약() 는 java.sql.SQLExceptionIf는이 방법은 연결을 커밋이 SQL 객체가 연결 만들었습니다 발생합니다. 이 SQL 객체가 DataSource에서 생성 된 경우이 메서드는 아무 작업도 수행하지 않습니다. 예외 : 은 java.sql.SQLException - 데이타베이스 액세스 에러가이 SQL 객체가 연결하여 만든 이 java.sql.SQLExceptionIf를 throw()

롤백 공공 무효 롤백이 메소드는 롤백가 발생했을 경우 연결. 이 SQL 객체가 DataSource에서 생성 된 경우이 메서드는 아무 작업도 수행하지 않습니다. 데이타베이스 액세스 에러가 생활에서 숙고

그냥 다른 신비를 발생하면 ... 희망이 도움이 - 은 java.sql.SQLException을 : 예외.

0

내가 찾은 매트에 의해 답변 됨. 유일한주의 사항은 Sql.closeResources(Connection)이 쿼리 호출과 메소드 실행 후 호출되며 Sql.withTransaction 또는 Sql.cacheConnection 내에 있지 않으면 연결을 종료한다는 것입니다.

은 이것이 의미하는 당신은 확실히 Sql.withTransaction 또는 Sql.cacheConnection 내에서 실행 폐쇄 내에서 트랜잭션 인식하지만 를 얻을 수 있다는 것입니다. Spring이나 JEE 트랜잭션 관리가하는 웹 서비스 호출의 전체 스레드 중에 트랜잭션 인식을 사용하여 groovy.sql.Sql을 사용하는 웹 서비스에서 DAO를 조정하려고합니다.

저지 엔드 포인트와 이상적인 구성 요소가있는 경우 트랜잭션 범위를 DAO 계층의 특정 블록으로 제한해서는 안되며, 이는 groovy.sql.Sql 인스턴스가있는 위치입니다. 따라서 Sql.withTransaction에 의존하는 것이 트랜잭션 인식/롤백 기능에 대한 불충분 한 솔루션이라고 생각하는 이유는 무엇입니까? 참고로

: 실행 전용 연결 if (cacheConnection) 닫는 것을 건너 closeResources를 호출 한 후에는 Github Sql.java source 참고 예 executeUpdate위한 많은 방법을 알 것이다; 따라서 withTransaction 또는 cacheTransaction을 통해 Closure 블록을 실행하는 경우에만 commit()rollback()을 사용할 수 있습니다. 이는 저자의 의도라고 생각합니다. 웹 서비스 요청의 전체 범위와 일치하는 트랜잭션 시작 및 종료의 제한 사항을 제외하면 이는 괜찮습니다. 이 경고, 내 솔루션은 closeResources 재정의 할이 클래스의 작성자로 아낌없이 overridable 만든. 트랜잭션 활동에 따라 연결을 닫지 않도록 재정의하십시오.

다른 말로하면이 연결을 처리하는 트랜잭션 관리가있는 경우 아무 곳에서나 closeResources을 no-op; 그렇지 않으면 구현을 재정의하지 마십시오.

관련 문제