使用mem数据库进行测试,实体管理器不释放锁'unable to obtain lock'



我有一个带有一些集成测试的春季启动应用程序,检查了数据库中数据的前端操作的结果。

该应用在Spring-Data-JPa和Spring-Data-Rest下方使用Datanucleus JPA,并带有内存数据库,例如。德比,通过春季启动测试自动设置。

我曾经使用Hibernate,但我将其转向Datanucleus。测试全部通过Hibernate通过,但是现在我的测试JDBCTEMPLATE查询正在悬挂,好像JPA没有释放其锁。

我已经尝试了H2(默默失败(,德比(悬挂到超时(和HSQLDB(永远悬挂(。

我尝试了各种工作,例如没有交易@Transactional(Transactional.TxType.NEVER)或/没有提交的@Rollback(true/false)

Spring Boot会自动实例化数据源,并将其注入EntityManagerFactory和JDBCtemplate。

这是测试:

@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK,
        classes = { TestDataSourceConfig.class })
@EnableAutoConfiguration
@AutoConfigureMockMvc
@AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.DERBY)
@Transactional
public class SymbolRestTests  {
    @Autowired
    private MockMvc mockMvc;
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private SymbolRepository symbolRepository;
    @PersistenceContext
    private EntityManager entityManager;
    @Before
    public void setUp() throws Exception {
        symbolRepository.deleteAll();
        entityManager.flush();
        entityManager.clear();
    }
    @Test
    public void shouldCreateEntity() throws Exception {
        String testTitle = "TEST.CODE.1";
        String testExtra = "Test for SymbolRestTests.java";
        String json = createJsonExample(testTitle, testExtra, true);
        MockHttpServletRequestBuilder requestBuilder =
                post("/symbols").content(json);
        mockMvc.perform(requestBuilder)
                .andExpect(status().isCreated())
                .andExpect(header().string("Location",
                        containsString("symbols/")));
        entityManager.flush();
        entityManager.close(); // this didn't help
        String sql = "SELECT count(*) FROM symbol WHERE title = ?";
        // exception thrown on this next line
        int count = jdbcTemplate.queryForObject(
                sql, new Object[] { testTitle }, Integer.class);
        Assert.assertThat(count, is(1)); 
    }
}

这是HSQLDB的错误(似乎是最有用的(:

org.springframework.dao.CannotAcquireLockException: PreparedStatementCallback; 
        SQL [SELECT count(*) FROM symbol WHERE title = ?]; 
        A lock could not be obtained within the time requested;  
        nested exception is java.sql.SQLTransactionRollbackException: 
        A lock could not be obtained within the time requested
    at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:259)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:649)
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:684)
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:716)
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:726)
    at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:794)
    at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:813)
    at com.gis.integration.SymbolRestTests.shouldCreateEntity(SymbolRestTests.java:127)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:316)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:114)
    at org.junit.jupiter.engine.descriptor.MethodTestDescriptor.lambda$invokeTestMethod$6(MethodTestDescriptor.java:171)
    at org.junit.jupiter.engine.execution.ThrowableCollector.execute(ThrowableCollector.java:40)
    at org.junit.jupiter.engine.descriptor.MethodTestDescriptor.invokeTestMethod(MethodTestDescriptor.java:168)
    at org.junit.jupiter.engine.descriptor.MethodTestDescriptor.execute(MethodTestDescriptor.java:115)
    at org.junit.jupiter.engine.descriptor.MethodTestDescriptor.execute(MethodTestDescriptor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.lambda$execute$1(HierarchicalTestExecutor.java:81)
    at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:76)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.lambda$execute$1(HierarchicalTestExecutor.java:91)
    at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:76)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.lambda$execute$1(HierarchicalTestExecutor.java:91)
    at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:76)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:51)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:43)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:137)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:87)
    at org.junit.platform.launcher.Launcher.execute(Launcher.java:93)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:61)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.sql.SQLTransactionRollbackException: A lock could not be obtained within the time requested
    at org.apache.derby.impl.jdbc.SQLExceptionFactory.getSQLException(Unknown Source)
    at org.apache.derby.impl.jdbc.Util.generateCsSQLException(Unknown Source)
    at org.apache.derby.impl.jdbc.TransactionResourceImpl.wrapInSQLException(Unknown Source)
    at org.apache.derby.impl.jdbc.TransactionResourceImpl.handleException(Unknown Source)
    at org.apache.derby.impl.jdbc.EmbedConnection.handleException(Unknown Source)
    at org.apache.derby.impl.jdbc.ConnectionChild.handleException(Unknown Source)
    at org.apache.derby.impl.jdbc.EmbedResultSet.closeOnTransactionError(Unknown Source)
    at org.apache.derby.impl.jdbc.EmbedResultSet.movePosition(Unknown Source)
    at org.apache.derby.impl.jdbc.EmbedResultSet.next(Unknown Source)
    at org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:92)
    at org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:60)
    at org.springframework.jdbc.core.JdbcTemplate$1.doInPreparedStatement(JdbcTemplate.java:697)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:633)
    ... 35 more
Caused by: ERROR 40XL1: A lock could not be obtained within the time requested
    at org.apache.derby.iapi.error.StandardException.newException(Unknown Source)
    at org.apache.derby.iapi.error.StandardException.newException(Unknown Source)
    at org.apache.derby.impl.services.locks.ConcurrentLockSet.lockObject(Unknown Source)
    at org.apache.derby.impl.services.locks.ConcurrentLockSet.zeroDurationLockObject(Unknown Source)
    at org.apache.derby.impl.services.locks.AbstractPool.zeroDurationlockObject(Unknown Source)
    at org.apache.derby.impl.services.locks.ConcurrentPool.zeroDurationlockObject(Unknown Source)
    at org.apache.derby.impl.store.raw.xact.RowLocking2nohold.lockRecordForRead(Unknown Source)
    at org.apache.derby.impl.store.access.heap.HeapController.lockRow(Unknown Source)
    at org.apache.derby.impl.store.access.heap.HeapController.lockRow(Unknown Source)
    at org.apache.derby.impl.store.access.btree.index.B2IRowLocking3.lockRowOnPage(Unknown Source)
    at org.apache.derby.impl.store.access.btree.index.B2IRowLocking3._lockScanRow(Unknown Source)
    at org.apache.derby.impl.store.access.btree.index.B2IRowLockingRR.lockScanRow(Unknown Source)
    at org.apache.derby.impl.store.access.btree.BTreeForwardScan.fetchRows(Unknown Source)
    at org.apache.derby.impl.store.access.btree.BTreeScan.fetchNextGroup(Unknown Source)
    at org.apache.derby.impl.sql.execute.BulkTableScanResultSet.reloadArray(Unknown Source)
    at org.apache.derby.impl.sql.execute.BulkTableScanResultSet.getNextRowCore(Unknown Source)
    at org.apache.derby.impl.sql.execute.ProjectRestrictResultSet.getNextRowCore(Unknown Source)
    at org.apache.derby.impl.sql.execute.ScalarAggregateResultSet.getRowFromResultSet(Unknown Source)
    at org.apache.derby.impl.sql.execute.ScalarAggregateResultSet.getNextRowCore(Unknown Source)
    at org.apache.derby.impl.sql.execute.ProjectRestrictResultSet.getNextRowCore(Unknown Source)
    at org.apache.derby.impl.sql.execute.BasicNoPutResultSetImpl.getNextRow(Unknown Source)
    ... 41 more

update 使用datanucleus事务文档我在persistence.xml中添加了一些datanucleus特定属性(没有结果(:

<?xml version="1.0" encoding="UTF-8" ?>
<persistence version="2.1"
        xmlns="http://xmlns.jcp.org/xml/ns/persistence"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
            persistence_2_1.xsd">
    <persistence-unit name="test">
        <provider>org.datanucleus.api.jpa.PersistenceProviderImpl</provider>
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <properties>
            <property name="datanucleus.schema.autoCreateAll" value="true"/>
            <property name="datanucleus.transactionIsolation" value="read-uncommitted"/>
            <property name="datanucleus.NontransactionalRead" value="true"/>
            <property name="datanucleus.NontransactionalWrite" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

更新#2 在调试级别的日志输出显示datanucleus-jpa/jdbctemplate日志语句(首先是jpa插入,然后jdbctemplate选择计数(*((:

2017-06-09 14:56:18.055 DEBUG 9492 --- [           main] DataNucleus.Connection                   : ManagedConnection(non-enlisted) "org.datanucleus.store.rdbms.ConnectionFactoryImpl$ManagedConnectionImpl@5621a671 [conn=org.apache.derby.impl.jdbc.EmbedConnection@2006fdaa, commitOnRelease=true, closeOnRelease=true, closeOnTxnEnd=true]" is being committed.
2017-06-09 14:56:18.055 DEBUG 9492 --- [           main] DataNucleus.Connection                   : ManagedConnection(non-enlisted) "org.datanucleus.store.rdbms.ConnectionFactoryImpl$ManagedConnectionImpl@5621a671 [conn=org.apache.derby.impl.jdbc.EmbedConnection@2006fdaa, commitOnRelease=true, closeOnRelease=true, closeOnTxnEnd=true]" closed
2017-06-09 14:56:18.068 DEBUG 9492 --- [           main] DataNucleus.Persistence                  : Object "com.bp.gis.tardis.entity.SymbolEntity@59c08cf1" being inserted into table "SYMBOL"
2017-06-09 14:56:18.071 DEBUG 9492 --- [           main] DataNucleus.Connection                   : ManagedConnection(non-enlisted) "org.datanucleus.store.rdbms.ConnectionFactoryImpl$ManagedConnectionImpl@63a7af06 [conn=null, commitOnRelease=false, closeOnRelease=false, closeOnTxnEnd=true]" opened with isolation level "read-uncommitted" and auto-commit=false
2017-06-09 14:56:18.074 DEBUG 9492 --- [           main] DataNucleus.Transaction                  : Running enlist operation on resource: org.datanucleus.store.rdbms.ConnectionFactoryImpl$EmulatedXAResource@3d4b45b, error code TMNOFLAGS and transaction: [DataNucleus Transaction, ID=Xid=   , enlisted resources=[]]
2017-06-09 14:56:18.076 DEBUG 9492 --- [           main] DataNucleus.Connection                   : ManagedConnection(enlisted) "org.datanucleus.store.rdbms.ConnectionFactoryImpl$ManagedConnectionImpl@63a7af06 [conn=org.apache.derby.impl.jdbc.EmbedConnection@19d76106, commitOnRelease=false, closeOnRelease=false, closeOnTxnEnd=true]" starting for transaction "Xid=   " with flags "0"
2017-06-09 14:56:18.078 DEBUG 9492 --- [           main] DataNucleus.Connection                   : ManagedConnection added to the pool : "org.datanucleus.store.rdbms.ConnectionFactoryImpl$ManagedConnectionImpl@63a7af06 [conn=org.apache.derby.impl.jdbc.EmbedConnection@19d76106, commitOnRelease=false, closeOnRelease=false, closeOnTxnEnd=true]" for key="org.datanucleus.ExecutionContextImpl@2c47a053" in factory="ConnectionFactory:tx[org.datanucleus.store.rdbms.ConnectionFactoryImpl@204c5ddf]"
2017-06-09 14:56:18.125 DEBUG 9492 --- [           main] DataNucleus.Datastore                    : Using PreparedStatement "org.datanucleus.store.rdbms.ParamLoggingPreparedStatement@37753b69" for connection "org.apache.derby.impl.jdbc.EmbedConnection@19d76106"
2017-06-09 14:56:18.134 DEBUG 9492 --- [           main] DataNucleus.Datastore.Native             : INSERT INTO SYMBOL (ACTIVE,CREATED,EXTRA,GLOBAL_READ,GLOBAL_WRITE,LAST_MODIFIED,TITLE) VALUES (<'Y'>,<2017-06-09>,<null>,<'N'>,<'N'>,<2017-06-09>,<'TEST.CODE.1'>)
2017-06-09 14:56:18.169 DEBUG 9492 --- [           main] DataNucleus.Datastore.Persist            : Execution Time = 36 ms (number of rows = 1) on PreparedStatement "org.datanucleus.store.rdbms.ParamLoggingPreparedStatement@37753b69"
2017-06-09 14:56:18.172 DEBUG 9492 --- [           main] DataNucleus.Datastore.Persist            : Object "com.bp.gis.tardis.entity.SymbolEntity@59c08cf1" was inserted in the datastore and was given strategy value of "1"
2017-06-09 14:56:18.179 DEBUG 9492 --- [           main] DataNucleus.Cache                        : Object "com.bp.gis.tardis.entity.SymbolEntity@59c08cf1" (id="org.datanucleus.identity.IdentityReference@93fb44") being changed to be referenced by id="com.bp.gis.tardis.entity.SymbolEntity:1" in Level 1 cache
2017-06-09 14:56:18.180 DEBUG 9492 --- [           main] DataNucleus.Cache                        : Object "com.bp.gis.tardis.entity.SymbolEntity@59c08cf1" (id="com.bp.gis.tardis.entity.SymbolEntity:1") added to Level 1 cache (loadedFlags="[YYYYYYYYY]")
2017-06-09 14:56:18.180 DEBUG 9492 --- [           main] DataNucleus.Transaction                  : Object "com.bp.gis.tardis.entity.SymbolEntity@59c08cf1" (id="org.datanucleus.identity.IdentityReference@93fb44") enlisted in transactional cache is now enlisted using id="com.bp.gis.tardis.entity.SymbolEntity:1"
2017-06-09 14:56:18.181 DEBUG 9492 --- [           main] DataNucleus.Persistence                  : Insert of object "com.bp.gis.tardis.entity.SymbolEntity@59c08cf1" is calling insertPostProcessing for field "com.bp.gis.tardis.entity.SymbolEntity.timeSeriesList"
2017-06-09 14:56:18.181 DEBUG 9492 --- [           main] DataNucleus.Datastore                    : Closing PreparedStatement "org.datanucleus.store.rdbms.ParamLoggingPreparedStatement@37753b69"
2017-06-09 14:56:18.181 DEBUG 9492 --- [           main] DataNucleus.Persistence                  : Insert of object "com.bp.gis.tardis.entity.SymbolEntity@59c08cf1" is calling postInsert for field "com.bp.gis.tardis.entity.SymbolEntity.timeSeriesList"
2017-06-09 14:56:18.205 DEBUG 9492 --- [           main] DataNucleus.Persistence                  : Object "com.bp.gis.tardis.entity.SymbolEntity@59c08cf1" field "timeSeriesList" is replaced by a SCO wrapper of type "org.datanucleus.store.types.wrappers.backed.List" [cache-values=true, lazy-loading=true, allow-nulls=true]
2017-06-09 14:56:18.207 DEBUG 9492 --- [           main] DataNucleus.Persistence                  : ExecutionContext.internalFlush() process finished
2017-06-09 14:56:18.218 DEBUG 9492 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL query
2017-06-09 14:56:18.220 DEBUG 9492 --- [           main] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [SELECT count(*) FROM symbol WHERE title = ?]
2017-06-09 14:56:18.250 TRACE 9492 --- [           main] o.s.jdbc.core.StatementCreatorUtils      : Setting SQL statement parameter value: column index 1, parameter value [TEST.CODE.1], value class [java.lang.String], SQL type unknown
2017-06-09 14:57:18.298  INFO 9492 --- [           main] o.s.b.f.xml.XmlBeanDefinitionReader      : Loading XML bean definitions from class path resource [org/springframework/jdbc/support/sql-error-codes.xml]
2017-06-09 14:57:18.390  INFO 9492 --- [           main] o.s.jdbc.support.SQLErrorCodesFactory    : SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase, Hana]
MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /symbols
       Parameters = {}
          Headers = {}
Handler:
             Type = org.springframework.data.rest.webmvc.RepositoryEntityController
           Method = public org.springframework.http.ResponseEntity<org.springframework.hateoas.ResourceSupport> org.springframework.data.rest.webmvc.RepositoryEntityController.postCollectionResource(org.springframework.data.rest.webmvc.RootResourceInformation,org.springframework.data.rest.webmvc.PersistentEntityResource,org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler,java.lang.String) throws org.springframework.web.HttpRequestMethodNotSupportedException
Async:
    Async started = false
     Async result = null
Resolved Exception:
             Type = null
ModelAndView:
        View name = null
             View = null
            Model = null
FlashMap:
       Attributes = null
MockHttpServletResponse:
           Status = 201
    Error message = null
          Headers = {X-Application-Context=[application:-1], Location=[http://localhost/symbols/0]}
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = http://localhost/symbols/0
          Cookies = []
2017-06-09 14:57:18.424 DEBUG 9492 --- [           main] DataNucleus.Transaction                  : Transaction rolling back for ExecutionContext org.datanucleus.ExecutionContextImpl@2c47a053
2017-06-09 14:57:18.429 DEBUG 9492 --- [           main] DataNucleus.Lifecycle                    : Object "com.bp.gis.tardis.entity.SymbolEntity@59c08cf1" (id="com.bp.gis.tardis.entity.SymbolEntity:1") has a lifecycle change : "P_NEW"->""
2017-06-09 14:57:18.447 DEBUG 9492 --- [           main] DataNucleus.Transaction                  : Object "com.bp.gis.tardis.entity.SymbolEntity@59c08cf1" (id="com.bp.gis.tardis.entity.SymbolEntity:1") was evicted from transactional cache
2017-06-09 14:57:18.448 DEBUG 9492 --- [           main] DataNucleus.Persistence                  : Disconnecting com.bp.gis.tardis.entity.SymbolEntity@59c08cf1 from StateManager[pc=com.bp.gis.tardis.entity.SymbolEntity@59c08cf1, lifecycle=P_NEW]
2017-06-09 14:57:18.451 DEBUG 9492 --- [           main] DataNucleus.Cache                        : Object with id="com.bp.gis.tardis.entity.SymbolEntity:1" being removed from Level 1 cache [current cache size = 1]
2017-06-09 14:57:18.451 DEBUG 9492 --- [           main] DataNucleus.Transaction                  : Rolling back [DataNucleus Transaction, ID=Xid=   , enlisted resources=[org.datanucleus.store.rdbms.ConnectionFactoryImpl$EmulatedXAResource@3d4b45b]]
2017-06-09 14:57:18.452 DEBUG 9492 --- [           main] DataNucleus.Connection                   : ManagedConnection(enlisted) "org.datanucleus.store.rdbms.ConnectionFactoryImpl$ManagedConnectionImpl@63a7af06 [conn=org.apache.derby.impl.jdbc.EmbedConnection@19d76106, commitOnRelease=false, closeOnRelease=false, closeOnTxnEnd=true]" rolling back for transaction "Xid=   "
2017-06-09 14:57:18.461 DEBUG 9492 --- [           main] DataNucleus.Connection                   : ManagedConnection(non-enlisted) "org.datanucleus.store.rdbms.ConnectionFactoryImpl$ManagedConnectionImpl@63a7af06 [conn=org.apache.derby.impl.jdbc.EmbedConnection@19d76106, commitOnRelease=false, closeOnRelease=false, closeOnTxnEnd=true]" closed
2017-06-09 14:57:18.461 DEBUG 9492 --- [           main] DataNucleus.Connection                   : ManagedConnection removed from the pool : "org.datanucleus.store.rdbms.ConnectionFactoryImpl$ManagedConnectionImpl@63a7af06 [conn=org.apache.derby.impl.jdbc.EmbedConnection@19d76106, commitOnRelease=false, closeOnRelease=false, closeOnTxnEnd=true]" for key="org.datanucleus.ExecutionContextImpl@2c47a053" in factory="ConnectionFactory:tx[org.datanucleus.store.rdbms.ConnectionFactoryImpl@204c5ddf]"
2017-06-09 14:57:18.462 DEBUG 9492 --- [           main] DataNucleus.Transaction                  : Transaction rolled back in 38 ms
2017-06-09 14:57:18.462 DEBUG 9492 --- [           main] DataNucleus.Persistence                  : ExecutionContext "org.datanucleus.ExecutionContextImpl@2c47a053" closed
2017-06-09 14:57:18.463  INFO 9492 --- [           main] o.s.t.c.transaction.TransactionContext   : Rolled back transaction for test context [DefaultTestContext@1f6f0fe2 testClass = SymbolRestTests, testInstance = com.bp.gis.tardis.integration.SymbolRestTests@22604c7e, testMethod = shouldCreateEntity@SymbolRestTests, testException = org.springframework.dao.CannotAcquireLockException: PreparedStatementCallback; SQL [SELECT count(*) FROM symbol WHERE title = ?]; A lock could not be obtained within the time requested; nested exception is java.sql.SQLTransactionRollbackException: A lock could not be obtained within the time requested, mergedContextConfiguration = [WebMergedContextConfiguration@3a48c398 testClass = SymbolRestTests, locations = '{}', classes = '{class com.bp.gis.tardis.config.TestDataSourceConfig, class com.bp.gis.tardis.config.TestDataSourceConfig}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{logging.level.DataNucleus=DEBUG, logging.level.com.bp.gis.tardis=TRACE, logging.level.org.springframework.jdbc.core=TRACE, security.basic.enabled=false, org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[[ImportsContextCustomizer@1b4ba615 key = [@org.springframework.boot.autoconfigure.AutoConfigurationPackage(), @org.junit.FixMethodOrder(value=NAME_ASCENDING), @org.springframework.context.annotation.Import(value=[class org.springframework.boot.autoconfigure.AutoConfigurationPackages$Registrar]), @org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase(replace=ANY, connection=DERBY), @org.springframework.boot.autoconfigure.EnableAutoConfiguration(exclude=[], excludeName=[]), @org.springframework.test.context.BootstrapWith(value=class org.springframework.boot.test.context.SpringBootTestContextBootstrapper), @org.junit.jupiter.api.extension.ExtendWith(value=[class org.springframework.test.context.junit.jupiter.SpringExtension]), @org.springframework.transaction.annotation.Transactional(propagation=REQUIRED, rollbackForClassName=[], readOnly=false, isolation=DEFAULT, transactionManager=, noRollbackFor=[], noRollbackForClassName=[], value=, timeout=-1, rollbackFor=[]), @org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc(webDriverEnabled=true, print=DEFAULT, webClientEnabled=true, secure=true, addFilters=true, printOnlyOnFailure=true), @org.springframework.boot.autoconfigure.ImportAutoConfiguration(value=[], exclude=[], classes=[]), @org.junit.platform.commons.meta.API(value=Experimental), @org.springframework.context.annotation.Import(value=[class org.springframework.boot.autoconfigure.ImportAutoConfigurationImportSelector]), @org.springframework.boot.test.autoconfigure.properties.PropertyMapping(value=spring.test.mockmvc, skip=NO), @org.springframework.boot.test.context.SpringBootTest(webEnvironment=MOCK, value=[], properties=[logging.level.DataNucleus=DEBUG, logging.level.com.bp.gis.tardis=TRACE, logging.level.org.springframework.jdbc.core=TRACE, security.basic.enabled=false], classes=[class com.bp.gis.tardis.config.TestDataSourceConfig]), @org.springframework.boot.test.autoconfigure.properties.PropertyMapping(value=spring.test.database, skip=NO), @org.springframework.context.annotation.Import(value=[class org.springframework.boot.autoconfigure.EnableAutoConfigurationImportSelector])]], org.springframework.boot.test.context.SpringBootTestContextCustomizer@702657cc, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@6025e1b6, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@e30f6a3a, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@1ff4931d], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]]].

更新#3

日志记录显示了Datanucleus的插入物,遵循了几个日志语句,它们看起来都涉及实体对象,然后来到

ExecutionContext.internalFlush() process finished

听起来像Datanucleus EntityManager潮红。然后是Spring JdbcTemplate的记录语句,它想阅读JPA插入内容,这一切都出错了。

Spring实际上确实包裹了EntityManager的测试框架,而包装器只是吞下了对entityManager.close()的呼叫,因此该调用不会导致交易完成。

Spring还会在entityManager.getTransaction()的通话中引发错误。

通常,在我在春季的经验中,刚刚与传统的春季方法一起工作,例如带有完全集成的Hibernate的弹簧数据JPA,与datanucleus一起工作。

感谢您解决这个问题:如何用 @transactional方法手动强制提交?

我从类声明中删除了@Transactional注释,并在测试方法上没有任何内容,并用REQUIRES_NEW称为单独的方法,以执行我要测试的REST-DATABase功能。然后在该调用后的原始测试方法中,它全部投入并没有锁定,因此我可以使用Spring的JDBCtemplate检查结果。

@Test
public void shouldCreateEntity() throws Exception {
    String testTitle = "TEST.CODE.1";
    String testExtra = "Test for SymbolRestTests.java";
    String json = createJsonExample(testTitle, testExtra, true);
    log.debug(String.format("JSON==%s", json));
    doInNewTransaction(json);
    String sql = "SELECT count(*) FROM symbol WHERE title = ?";
    int count = jdbcTemplate.queryForObject(
            sql, new Object[] { testTitle }, Integer.class);
    Assert.assertThat(count, is(1));
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
private void doInNewTransaction(String json) throws Exception {
    MockHttpServletRequestBuilder requestBuilder =
            post("/symbols").content(json);
    mockMvc.perform(requestBuilder)
            .andExpect(status().isCreated())
            .andExpect(header().string("Location",
                    containsString("symbols/")));
}

最新更新