spring - Transactional TestNG 测试导致 Large Objects 可能无法在自动提交模式下使用

标签 spring postgresql blob testng transactional

我知道 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_AbstractTransactionAspect$1$2a73e96cproceed(AbstractTransactionAspect.aj:66)
    at org.springframework.transaction.aspectj.AbstractTransactionAspect$AbstractTransactionAspect$1.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_AbstractTransactionAspect$1$2a73e96c(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_AbstractTransactionAspect$1$2a73e96cproceed(AbstractTransactionAspect.aj:66)
    at org.springframework.transaction.aspectj.AbstractTransactionAspect$AbstractTransactionAspect$1.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_AbstractTransactionAspect$1$2a73e96c(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$1.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_AbstractTransactionAspect$1$2a73e96cproceed(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 检索参与事务的当前连接,而 getTemplate().getDataSource().getConnection() 返回一个新连接。

关于spring - Transactional TestNG 测试导致 Large Objects 可能无法在自动提交模式下使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29467656/

相关文章:

hibernate - Grails-禁止访问org.hibernate.cfg.Environment错误

c++ - 将 PostgreSQL 与 G-WAN 结合使用

使用 CloudDrive Azure 示例时出现 Powershell 错误

Java - JDBC/Swing -executeUpdate 不适用于 BLOB

javascript - 在 Firefox 中,onload 未使用 createObjectURL img.src 触发

python - 在 PostgreSQL 和 Dynamics 365 Web API 之间构建 Python3 授权代理服务器

java - 使用 Hibernate 调用 MySQL 存储过程时分页不起作用

java - 避免与数据对象重复

java - 使用 spring 和 Hibernate 的基本 CRUD 应用程序

java - 0 :25:01, 819 警告 [HtmlImageRendererBase] 缺少 ALT 属性:j_id113