Transactional TestNG 测试导致 Large Objects 可能无法在自动提交模式下使用
Transactional TestNG test causes Large Objects may not be used in auto-commit mode
我知道 Postgres 在处理大对象时需要一个事务。对我来说很奇怪的是,即使我在交易中,我也会收到这个错误——或者至少我认为我是。我是 运行 我在 TestNG 测试用例中的代码,如下所示:
public class MasterpriceFileRepositoryJdbcTest extends AbstractTransactionalTestNGSpringContextTests
这是我的测试方法:
public void insertAndRetrieve() throws Exception {
MasterpriceFile expectedMasterpriceFile = repository.save("A test file.txt", "text/plain", new ByteArrayInputStream("This is a test file".getBytes()));
MasterpriceFile actualMasterpriceFile = repository.getById(expectedMasterpriceFile.getId());
assertThat(actualMasterpriceFile).isEqualToComparingFieldByField(expectedMasterpriceFile);
}
这是我存储库中的保存方法(我想测试的那个):
public MasterpriceFile save(final String filename, final String contentType, final InputStream inputStream) throws IOException {
try {
// NOTE: Below code is postgres specific. Needs to be changed if DB vendor changes.
ConnectionHandle connectionHandle = (ConnectionHandle) getTemplate().getDataSource().getConnection();
PGConnection connection = (PGConnection) connectionHandle.getInternalConnection();
LargeObjectManager largeObjectManager = connection.getLargeObjectAPI();
long contentOid = largeObjectManager.createLO(LargeObjectManager.READWRITE);
LargeObject largeObject = largeObjectManager.open(contentOid);
IOUtils.copy(inputStream, largeObject.getOutputStream());
largeObject.close();
MasterpriceFile masterpriceFile = new MasterpriceFile();
masterpriceFile.setContentType(contentType);
masterpriceFile.setCreationDate(DateTime.now());
masterpriceFile.setContentOid(contentOid);
masterpriceFile.setFilename(filename);
masterpriceFile.setId(updateAndReturnSimpleKeyAsLong(INSERT, "id",
convert(masterpriceFile.getCreationDate()),
masterpriceFile.getContentOid(),
masterpriceFile.getFilename(),
masterpriceFile.getContentType()));
return masterpriceFile;
}
catch (SQLException e) {
throw getTemplate().getExceptionTranslator().translate("store masterprice file", null, e);
}
}
运行 测试给出了以下结果:
org.springframework.jdbc.UncategorizedSQLException: store masterprice file; uncategorized SQLException for SQL []; SQL state [25P01]; error code [0]; Large Objects may not be used in auto-commit mode.; nested exception is org.postgresql.util.PSQLException: Large Objects may not be used in auto-commit mode.
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:84)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at solve.scm.repository.jdbc.MasterpriceFileRepositoryJdbc.save_aroundBody4(MasterpriceFileRepositoryJdbc.java:73)
at solve.scm.repository.jdbc.MasterpriceFileRepositoryJdbc$AjcClosure5.run(MasterpriceFileRepositoryJdbc.java:1)
at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspecta73e96cproceed(AbstractTransactionAspect.aj:66)
at org.springframework.transaction.aspectj.AbstractTransactionAspect$AbstractTransactionAspect.proceedWithInvocation(AbstractTransactionAspect.aj:72)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspecta73e96c(AbstractTransactionAspect.aj:70)
at solve.scm.repository.jdbc.MasterpriceFileRepositoryJdbc.save(MasterpriceFileRepositoryJdbc.java:44)
at solve.scm.repository.jdbc.MasterpriceFileRepositoryJdbcTest.insertAndRetrieve_aroundBody0(MasterpriceFileRepositoryJdbcTest.java:28)
at solve.scm.repository.jdbc.MasterpriceFileRepositoryJdbcTest$AjcClosure1.run(MasterpriceFileRepositoryJdbcTest.java:1)
at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspecta73e96cproceed(AbstractTransactionAspect.aj:66)
at org.springframework.transaction.aspectj.AbstractTransactionAspect$AbstractTransactionAspect.proceedWithInvocation(AbstractTransactionAspect.aj:72)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspecta73e96c(AbstractTransactionAspect.aj:70)
at solve.scm.repository.jdbc.MasterpriceFileRepositoryJdbcTest.insertAndRetrieve(MasterpriceFileRepositoryJdbcTest.java:26)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:84)
at org.testng.internal.MethodInvocationHelper.runTestMethod(MethodInvocationHelper.java:200)
at org.springframework.test.context.testng.AbstractTestNGSpringContextTests.run(AbstractTestNGSpringContextTests.java:171)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.testng.internal.MethodInvocationHelper.invokeHookable(MethodInvocationHelper.java:212)
at org.testng.internal.Invoker.invokeMethod(Invoker.java:707)
at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:901)
at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1231)
at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:127)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:111)
at org.testng.TestRunner.privateRun(TestRunner.java:767)
at org.testng.TestRunner.run(TestRunner.java:617)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:334)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:329)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:291)
at org.testng.SuiteRunner.run(SuiteRunner.java:240)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1224)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1149)
at org.testng.TestNG.run(TestNG.java:1057)
at org.testng.remote.RemoteTestNG.run(RemoteTestNG.java:111)
at org.testng.remote.RemoteTestNG.initAndRun(RemoteTestNG.java:204)
at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:175)
Caused by: org.postgresql.util.PSQLException: Large Objects may not be used in auto-commit mode.
at org.postgresql.largeobject.LargeObjectManager.createLO(LargeObjectManager.java:301)
at solve.scm.repository.jdbc.MasterpriceFileRepositoryJdbc.save_aroundBody4(MasterpriceFileRepositoryJdbc.java:52)
... 44 more
任何人都可以告诉我这个案例有什么问题吗?我好像在交易中
org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspecta73e96cproceed(AbstractTransactionAspect.aj:66
那么为什么 Postgres 认为我不是?
这是导致堆栈跟踪的 postgres 中的方法:
public long createLO(int mode) throws SQLException
{
if (conn.getAutoCommit())
throw new PSQLException(GT.tr("Large Objects may not be used in auto-commit mode."),
PSQLState.NO_ACTIVE_SQL_TRANSACTION);
FastpathArg args[] = new FastpathArg[1];
args[0] = new FastpathArg(mode);
return fp.getOID("lo_creat", args);
}
我通过更改以下代码行使其工作:
ConnectionHandle connectionHandle = (ConnectionHandle) getTemplate().getDataSource().getConnection();
进入这个:
ConnectionHandle connectionHandle = (ConnectionHandle) DataSourceUtils.getConnection(getTemplate().getDataSource());
看起来 DataSourceUtils.getConnection 检索参与事务的 current 连接,而 getTemplate().getDataSource().getConnection() returns一个新的连接。
我知道 Postgres 在处理大对象时需要一个事务。对我来说很奇怪的是,即使我在交易中,我也会收到这个错误——或者至少我认为我是。我是 运行 我在 TestNG 测试用例中的代码,如下所示:
public class MasterpriceFileRepositoryJdbcTest extends AbstractTransactionalTestNGSpringContextTests
这是我的测试方法:
public void insertAndRetrieve() throws Exception {
MasterpriceFile expectedMasterpriceFile = repository.save("A test file.txt", "text/plain", new ByteArrayInputStream("This is a test file".getBytes()));
MasterpriceFile actualMasterpriceFile = repository.getById(expectedMasterpriceFile.getId());
assertThat(actualMasterpriceFile).isEqualToComparingFieldByField(expectedMasterpriceFile);
}
这是我存储库中的保存方法(我想测试的那个):
public MasterpriceFile save(final String filename, final String contentType, final InputStream inputStream) throws IOException {
try {
// NOTE: Below code is postgres specific. Needs to be changed if DB vendor changes.
ConnectionHandle connectionHandle = (ConnectionHandle) getTemplate().getDataSource().getConnection();
PGConnection connection = (PGConnection) connectionHandle.getInternalConnection();
LargeObjectManager largeObjectManager = connection.getLargeObjectAPI();
long contentOid = largeObjectManager.createLO(LargeObjectManager.READWRITE);
LargeObject largeObject = largeObjectManager.open(contentOid);
IOUtils.copy(inputStream, largeObject.getOutputStream());
largeObject.close();
MasterpriceFile masterpriceFile = new MasterpriceFile();
masterpriceFile.setContentType(contentType);
masterpriceFile.setCreationDate(DateTime.now());
masterpriceFile.setContentOid(contentOid);
masterpriceFile.setFilename(filename);
masterpriceFile.setId(updateAndReturnSimpleKeyAsLong(INSERT, "id",
convert(masterpriceFile.getCreationDate()),
masterpriceFile.getContentOid(),
masterpriceFile.getFilename(),
masterpriceFile.getContentType()));
return masterpriceFile;
}
catch (SQLException e) {
throw getTemplate().getExceptionTranslator().translate("store masterprice file", null, e);
}
}
运行 测试给出了以下结果:
org.springframework.jdbc.UncategorizedSQLException: store masterprice file; uncategorized SQLException for SQL []; SQL state [25P01]; error code [0]; Large Objects may not be used in auto-commit mode.; nested exception is org.postgresql.util.PSQLException: Large Objects may not be used in auto-commit mode.
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:84)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
at solve.scm.repository.jdbc.MasterpriceFileRepositoryJdbc.save_aroundBody4(MasterpriceFileRepositoryJdbc.java:73)
at solve.scm.repository.jdbc.MasterpriceFileRepositoryJdbc$AjcClosure5.run(MasterpriceFileRepositoryJdbc.java:1)
at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspecta73e96cproceed(AbstractTransactionAspect.aj:66)
at org.springframework.transaction.aspectj.AbstractTransactionAspect$AbstractTransactionAspect.proceedWithInvocation(AbstractTransactionAspect.aj:72)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspecta73e96c(AbstractTransactionAspect.aj:70)
at solve.scm.repository.jdbc.MasterpriceFileRepositoryJdbc.save(MasterpriceFileRepositoryJdbc.java:44)
at solve.scm.repository.jdbc.MasterpriceFileRepositoryJdbcTest.insertAndRetrieve_aroundBody0(MasterpriceFileRepositoryJdbcTest.java:28)
at solve.scm.repository.jdbc.MasterpriceFileRepositoryJdbcTest$AjcClosure1.run(MasterpriceFileRepositoryJdbcTest.java:1)
at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspecta73e96cproceed(AbstractTransactionAspect.aj:66)
at org.springframework.transaction.aspectj.AbstractTransactionAspect$AbstractTransactionAspect.proceedWithInvocation(AbstractTransactionAspect.aj:72)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspecta73e96c(AbstractTransactionAspect.aj:70)
at solve.scm.repository.jdbc.MasterpriceFileRepositoryJdbcTest.insertAndRetrieve(MasterpriceFileRepositoryJdbcTest.java:26)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:84)
at org.testng.internal.MethodInvocationHelper.runTestMethod(MethodInvocationHelper.java:200)
at org.springframework.test.context.testng.AbstractTestNGSpringContextTests.run(AbstractTestNGSpringContextTests.java:171)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.testng.internal.MethodInvocationHelper.invokeHookable(MethodInvocationHelper.java:212)
at org.testng.internal.Invoker.invokeMethod(Invoker.java:707)
at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:901)
at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1231)
at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:127)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:111)
at org.testng.TestRunner.privateRun(TestRunner.java:767)
at org.testng.TestRunner.run(TestRunner.java:617)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:334)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:329)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:291)
at org.testng.SuiteRunner.run(SuiteRunner.java:240)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1224)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1149)
at org.testng.TestNG.run(TestNG.java:1057)
at org.testng.remote.RemoteTestNG.run(RemoteTestNG.java:111)
at org.testng.remote.RemoteTestNG.initAndRun(RemoteTestNG.java:204)
at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:175)
Caused by: org.postgresql.util.PSQLException: Large Objects may not be used in auto-commit mode.
at org.postgresql.largeobject.LargeObjectManager.createLO(LargeObjectManager.java:301)
at solve.scm.repository.jdbc.MasterpriceFileRepositoryJdbc.save_aroundBody4(MasterpriceFileRepositoryJdbc.java:52)
... 44 more
任何人都可以告诉我这个案例有什么问题吗?我好像在交易中
org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspecta73e96cproceed(AbstractTransactionAspect.aj:66
那么为什么 Postgres 认为我不是?
这是导致堆栈跟踪的 postgres 中的方法:
public long createLO(int mode) throws SQLException
{
if (conn.getAutoCommit())
throw new PSQLException(GT.tr("Large Objects may not be used in auto-commit mode."),
PSQLState.NO_ACTIVE_SQL_TRANSACTION);
FastpathArg args[] = new FastpathArg[1];
args[0] = new FastpathArg(mode);
return fp.getOID("lo_creat", args);
}
我通过更改以下代码行使其工作:
ConnectionHandle connectionHandle = (ConnectionHandle) getTemplate().getDataSource().getConnection();
进入这个:
ConnectionHandle connectionHandle = (ConnectionHandle) DataSourceUtils.getConnection(getTemplate().getDataSource());
看起来 DataSourceUtils.getConnection 检索参与事务的 current 连接,而 getTemplate().getDataSource().getConnection() returns一个新的连接。