使用数据jpa进行Spring启动 - 删除实体仅适用于测试

时间:2017-05-10 13:52:33

标签: spring spring-boot spring-data spring-data-jpa rest-assured

我对这个问题的争论时间太长了,是时候去讨论开发者社区了。

==简短描述==

使用Spring数据JPA,我无法从Controller删除我的实体,没有抛出任何异常。 如果我尝试从.delete()致电IntegrationTest class,那就可以了。

==我的环境==

  • Java 8
  • Spring Boot 1.5.1
  • Spring Data JPA 1.11.0
  • RestAssured 2.9.0

CartItemSecRest.java - 我的控制器方法(不工作)

@RestController
@RequestMapping("api/secure/carts/{id}/items")
public class CartItemSecRest
{
    @DeleteMapping("{itemId}")
    @PreAuthorize("isAuthenticated()")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void delete(@PathVariable("id") CartSession cartSession, @PathVariable("itemId") CartItemSession cartItemSession, @AuthenticationPrincipal JwtUser jwtUser){

        // checking permission and so on..

        if (cartItemSession == null || cartSession == null)
            throw new BaseException.EntityNotFound();

        if(cartSession.getUser().getId() != jwtUser.getId())
            throw new BaseException.Forbidden();

        cartItemService.delete(cartItemSession);
    }
}

=== INTEGRATION TEST SIDE ===

在集成测试期间,我使用内存数据库。 在每次测试之前,我正在使用包含3个CartItem的购物车创建(保存到DB)新用户。

我的整合测试:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class CartItemSecRestIT extends BaseEcommerceRestIT
{
    //Other test methods

    /**
     * Logged user, try to remove item from cart.
     */
    @Test
    public void deleteCartItem()
    {
        CartItemSession cartItemSession = cartSession.getItems().get(0);
        requestWithAuthToken(user)
                .delete(String.format("api/secure/carts/%s/items/%s", cartSession.getId(), cartItemSession.getId()))
                .then()
                .statusCode(HttpStatus.NO_CONTENT.value());

        List<CartItemSession> actualCartItems = cartItemManager.findBySessionId(cartSession.getId());
        assertEquals(2, actualCartItems.size());
    }
}

断言失败并且总是出现相同的错误,似乎没有项目被删除。

java.lang.AssertionError: 
Expected :2
Actual   :3

at org.junit.Assert.fail(Assert.java:88)
...
at yyyy.xxx.api.rest.secure.CartItemSecRestIT.deleteCartItem(CartItemSecRestIT.java:179)

每当我打电话时,在IntegrationClass中的奇怪行为:

  • cartItemService.delete(XX);
  • cartItemManager.delete(XX);
  • cartItemRepository.delete(XX);

按预期工作,该项目已删除。

实际上,如果我编写以下集成测试方法,一切看起来都不错! (测试变为绿色)

@Test
public void deleteTestOK()
{
    //lets test with Manager
    List<CartItemSession> cartItemSessions = cartItemManager.findBySessionId(cartSession.getId());
    cartItemManager.delete(cartItemSessions.get(0).getId());
    assertEquals(2, cartItemManager.findBySessionId(cartSession.getId()).size());

    //test with service
    cartItemSessions = cartItemManager.findBySessionId(cartSession.getId());
    cartItemService.delete(cartItemSessions.get(0));
    assertEquals(1, cartItemManager.findBySessionId(cartSession.getId()).size());

    //test with repository
    cartItemSessions = cartItemManager.findBySessionId(cartSession.getId());
    cartItemRepository.delete(cartItemSessions.get(0));
    assertEquals(0, cartItemManager.findBySessionId(cartSession.getId()).size());
}

如果我在“服务器端”(Controller)上尝试相同的操作,则这些删除方法都不起作用。

@DeleteMapping("{itemId}")
@PreAuthorize("isAuthenticated()")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(@PathVariable("id") CartSession cartSession, @PathVariable("itemId") CartItemSession cartItemSession, @AuthenticationPrincipal JwtUser jwtUser){

    //Check permission and so on

    if (cartItemSession == null || cartSession == null)
        throw new BaseException.EntityNotFound();

    if(cartSession.getUser().getId() != jwtUser.getId())
        throw new BaseException.Forbidden();

    //try using repository
    cartItemRepository.delete(cartItemSession);
    logger.info("1° delete - total items: {}", cartItemManager.count());

    //try using service
    cartItemService.delete(cartItemSession);
    logger.info("2° delete - total items: {}", cartItemManager.count());

    //try using manager
    cartItemManager.delete(cartItemSession);
    logger.info("3° delete - total items: {}", cartItemManager.count());
}

但输出显示没有项目被删除!

1° delete - total items: 3
2° delete - total items: 3
3° delete - total items: 3

我在控制器类上找不到什么错误。

=======更新13/05/2017 =======

我找到了一种让它有效的方法,但没有合理的解释:

好的,我接近这一点,看看这个:

这是我的“EcCartSession”:

public class EcCartSession{
    @LazyCollection(LazyCollectionOption.FALSE)//THIS IS THE IMPORTANT ONE
    @OneToMany(targetEntity = AbstractItemSession.class, mappedBy = "session", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<EcCartItemSession> items = new ArrayList<>();
}

WORKING:

public void delete(@PathVariable("itemId") EcCartItemSession cartItemSession, @AuthenticationPrincipal JwtUser jwtUser){
    cartItemService.delete(cartItemSession);
}

不工作:

public void delete(@PathVariable("id") EcCartSession cartSession, @PathVariable("itemId") EcCartItemSession cartItemSession, @AuthenticationPrincipal JwtUser jwtUser){
    cartItemService.delete(cartItemSession);
}

相反,如果我放@LazyCollection(LazyCollectionOption.TRUE)

它适用于这两种情况。

=======更新31/05/2017 =======

application.properties上启用了事务包的日志记录:

logging.level.org.springframework.transaction=TRACE

再次运行集成测试,这是在调用cartItemService.delete(cartItemSession)(控制器)之后生成的输出:

2017-05-31 00:21:18.479 TRACE 22924 --- [o-auto-1-exec-1] .s.t.s.TransactionSynchronizationManager : Bound value [org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$DefaultCrudMethodMetadata@640f8a5f] for key [public abstract void org.springframework.data.repository.CrudRepository.delete(java.lang.Object)] to thread [http-nio-auto-1-exec-1]
2017-05-31 00:21:18.481 TRACE 22924 --- [o-auto-1-exec-1] .s.t.s.TransactionSynchronizationManager : Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@2a0c8406] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@603bc676] bound to thread [http-nio-auto-1-exec-1]
2017-05-31 00:21:18.486 TRACE 22924 --- [o-auto-1-exec-1] .s.t.s.TransactionSynchronizationManager : Bound value [org.springframework.jdbc.datasource.ConnectionHolder@4c417bab] for key [org.apache.tomcat.jdbc.pool.DataSource@19c1d60a{ConnectionPool[defaultAutoCommit=null; defaultReadOnly=null; defaultTransactionIsolation=-1; defaultCatalog=null; driverClassName=org.h2.Driver; maxActive=100; maxIdle=100; minIdle=10; initialSize=10; maxWait=30000; testOnBorrow=true; testOnReturn=false; timeBetweenEvictionRunsMillis=5000; numTestsPerEvictionRun=0; minEvictableIdleTimeMillis=60000; testWhileIdle=false; testOnConnect=false; password=********; url=jdbc:h2:mem:webproject;;MODE=PostgreSQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE; username=sa; validationQuery=SELECT 1; validationQueryTimeout=-1; validatorClassName=null; validationInterval=3000; accessToUnderlyingConnectionAllowed=true; removeAbandoned=false; removeAbandonedTimeout=60; logAbandoned=false; connectionProperties=null; initSQL=null; jdbcInterceptors=null; jmxEnabled=true; fairQueue=true; useEquals=true; abandonWhenPercentageFull=0; maxAge=0; useLock=false; dataSource=null; dataSourceJNDI=null; suspectTimeout=0; alternateUsernameAllowed=false; commitOnReturn=false; rollbackOnReturn=false; useDisposableConnectionFacade=true; logValidationErrors=false; propagateInterruptState=false; ignoreExceptionOnPreLoad=false; }] to thread [http-nio-auto-1-exec-1]
2017-05-31 00:21:18.487 TRACE 22924 --- [o-auto-1-exec-1] .s.t.s.TransactionSynchronizationManager : Initializing transaction synchronization
2017-05-31 00:21:18.487 TRACE 22924 --- [o-auto-1-exec-1] o.s.t.i.TransactionInterceptor           : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.delete]
2017-05-31 00:21:18.488 TRACE 22924 --- [o-auto-1-exec-1] .s.t.s.TransactionSynchronizationManager : Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@2a0c8406] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@603bc676] bound to thread [http-nio-auto-1-exec-1]
2017-05-31 00:21:18.488 TRACE 22924 --- [o-auto-1-exec-1] .s.t.s.TransactionSynchronizationManager : Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@2a0c8406] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@603bc676] bound to thread [http-nio-auto-1-exec-1]
2017-05-31 00:21:18.493 TRACE 22924 --- [o-auto-1-exec-1] o.s.t.i.TransactionInterceptor           : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.delete]
2017-05-31 00:21:18.497 TRACE 22924 --- [o-auto-1-exec-1] .s.t.s.TransactionSynchronizationManager : Clearing transaction synchronization
2017-05-31 00:21:18.503 TRACE 22924 --- [o-auto-1-exec-1] .s.t.s.TransactionSynchronizationManager : Removed value [org.springframework.jdbc.datasource.ConnectionHolder@4c417bab] for key [org.apache.tomcat.jdbc.pool.DataSource@19c1d60a{ConnectionPool[defaultAutoCommit=null; defaultReadOnly=null; defaultTransactionIsolation=-1; defaultCatalog=null; driverClassName=org.h2.Driver; maxActive=100; maxIdle=100; minIdle=10; initialSize=10; maxWait=30000; testOnBorrow=true; testOnReturn=false; timeBetweenEvictionRunsMillis=5000; numTestsPerEvictionRun=0; minEvictableIdleTimeMillis=60000; testWhileIdle=false; testOnConnect=false; password=********; url=jdbc:h2:mem:webproject;;MODE=PostgreSQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE; username=sa; validationQuery=SELECT 1; validationQueryTimeout=-1; validatorClassName=null; validationInterval=3000; accessToUnderlyingConnectionAllowed=true; removeAbandoned=false; removeAbandonedTimeout=60; logAbandoned=false; connectionProperties=null; initSQL=null; jdbcInterceptors=null; jmxEnabled=true; fairQueue=true; useEquals=true; abandonWhenPercentageFull=0; maxAge=0; useLock=false; dataSource=null; dataSourceJNDI=null; suspectTimeout=0; alternateUsernameAllowed=false; commitOnReturn=false; rollbackOnReturn=false; useDisposableConnectionFacade=true; logValidationErrors=false; propagateInterruptState=false; ignoreExceptionOnPreLoad=false; }] from thread [http-nio-auto-1-exec-1]
2017-05-31 00:21:18.503 TRACE 22924 --- [o-auto-1-exec-1] .s.t.s.TransactionSynchronizationManager : Removed value [org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$DefaultCrudMethodMetadata@640f8a5f] for key [public abstract void org.springframework.data.repository.CrudRepository.delete(java.lang.Object)] from thread [http-nio-auto-1-exec-1]
2017-05-31 00:21:23.380 TRACE 22924 --- [o-auto-1-exec-1] .s.t.s.TransactionSynchronizationManager : Removed value [org.springframework.orm.jpa.EntityManagerHolder@2a0c8406] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@603bc676] from thread [http-nio-auto-1-exec-1]
2017-05-31 00:21:23.881 TRACE 22924 --- [           main] .s.t.s.TransactionSynchronizationManager : Bound value [org.springframework.jdbc.datasource.ConnectionHolder@52a1d6f] for key [org.apache.tomcat.jdbc.pool.DataSource@19c1d60a{ConnectionPool[defaultAutoCommit=null; defaultReadOnly=null; defaultTransactionIsolation=-1; defaultCatalog=null; driverClassName=org.h2.Driver; maxActive=100; maxIdle=100; minIdle=10; initialSize=10; maxWait=30000; testOnBorrow=true; testOnReturn=false; timeBetweenEvictionRunsMillis=5000; numTestsPerEvictionRun=0; minEvictableIdleTimeMillis=60000; testWhileIdle=false; testOnConnect=false; password=********; url=jdbc:h2:mem:webproject;;MODE=PostgreSQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE; username=sa; validationQuery=SELECT 1; validationQueryTimeout=-1; validatorClassName=null; validationInterval=3000; accessToUnderlyingConnectionAllowed=true; removeAbandoned=false; removeAbandonedTimeout=60; logAbandoned=false; connectionProperties=null; initSQL=null; jdbcInterceptors=null; jmxEnabled=true; fairQueue=true; useEquals=true; abandonWhenPercentageFull=0; maxAge=0; useLock=false; dataSource=null; dataSourceJNDI=null; suspectTimeout=0; alternateUsernameAllowed=false; commitOnReturn=false; rollbackOnReturn=false; useDisposableConnectionFacade=true; logValidationErrors=false; propagateInterruptState=false; ignoreExceptionOnPreLoad=false; }] to thread [main]
2017-05-31 00:21:23.881 TRACE 22924 --- [           main] .s.t.s.TransactionSynchronizationManager : Bound value [org.springframework.orm.jpa.EntityManagerHolder@38bb297] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@603bc676] to thread [main]
2017-05-31 00:21:23.881 TRACE 22924 --- [           main] .s.t.s.TransactionSynchronizationManager : Initializing transaction synchronization
2017-05-31 00:21:23.881 TRACE 22924 --- [           main] o.s.t.i.TransactionInterceptor           : Getting transaction for [org.domain.ecommerce.core.data.managers.spring.CartItemManagerImpl.findBySessionId]
2017-05-31 00:21:23.882 TRACE 22924 --- [           main] .s.t.s.TransactionSynchronizationManager : Bound value [org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$DefaultCrudMethodMetadata@6c96e1d5] for key [public abstract java.util.List org.domain.core.data.repositories.jpa.SessionItemRepository.findBySessionId(java.io.Serializable)] to thread [main]
2017-05-31 00:21:23.882 TRACE 22924 --- [           main] o.s.t.i.TransactionInterceptor           : Don't need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findBySessionId]: This method isn't transactional.
2017-05-31 00:21:23.882 TRACE 22924 --- [           main] .s.t.s.TransactionSynchronizationManager : Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@38bb297] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@603bc676] bound to thread [main]
2017-05-31 00:21:23.886 TRACE 22924 --- [           main] .s.t.s.TransactionSynchronizationManager : Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@38bb297] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@603bc676] bound to thread [main]
2017-05-31 00:21:23.893 TRACE 22924 --- [           main] .s.t.s.TransactionSynchronizationManager : Removed value [org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$DefaultCrudMethodMetadata@6c96e1d5] for key [public abstract java.util.List org.domain.core.data.repositories.jpa.SessionItemRepository.findBySessionId(java.io.Serializable)] from thread [main]
2017-05-31 00:21:23.893 TRACE 22924 --- [           main] o.s.t.i.TransactionInterceptor           : Completing transaction for [org.domain.ecommerce.core.data.managers.spring.CartItemManagerImpl.findBySessionId]
2017-05-31 00:21:23.894 TRACE 22924 --- [           main] .s.t.s.TransactionSynchronizationManager : Clearing transaction synchronization
2017-05-31 00:21:23.894 TRACE 22924 --- [           main] .s.t.s.TransactionSynchronizationManager : Removed value [org.springframework.orm.jpa.EntityManagerHolder@38bb297] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@603bc676] from thread [main]
2017-05-31 00:21:23.894 TRACE 22924 --- [           main] .s.t.s.TransactionSynchronizationManager : Removed value [org.springframework.jdbc.datasource.ConnectionHolder@52a1d6f] for key [org.apache.tomcat.jdbc.pool.DataSource@19c1d60a{ConnectionPool[defaultAutoCommit=null; defaultReadOnly=null; defaultTransactionIsolation=-1; defaultCatalog=null; driverClassName=org.h2.Driver; maxActive=100; maxIdle=100; minIdle=10; initialSize=10; maxWait=30000; testOnBorrow=true; testOnReturn=false; timeBetweenEvictionRunsMillis=5000; numTestsPerEvictionRun=0; minEvictableIdleTimeMillis=60000; testWhileIdle=false; testOnConnect=false; password=********; url=jdbc:h2:mem:webproject;;MODE=PostgreSQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE; username=sa; validationQuery=SELECT 1; validationQueryTimeout=-1; validatorClassName=null; validationInterval=3000; accessToUnderlyingConnectionAllowed=true; removeAbandoned=false; removeAbandonedTimeout=60; logAbandoned=false; connectionProperties=null; initSQL=null; jdbcInterceptors=null; jmxEnabled=true; fairQueue=true; useEquals=true; abandonWhenPercentageFull=0; maxAge=0; useLock=false; dataSource=null; dataSourceJNDI=null; suspectTimeout=0; alternateUsernameAllowed=false; commitOnReturn=false; rollbackOnReturn=false; useDisposableConnectionFacade=true; logValidationErrors=false; propagateInterruptState=false; ignoreExceptionOnPreLoad=false; }] from thread [main]

0 个答案:

没有答案