我对这个问题的争论时间太长了,是时候去讨论开发者社区了。
==简短描述==
使用Spring数据JPA,我无法从Controller
删除我的实体,没有抛出任何异常。
如果我尝试从.delete()
致电IntegrationTest class
,那就可以了。
==我的环境==
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中的奇怪行为:
按预期工作,该项目已删除。
实际上,如果我编写以下集成测试方法,一切看起来都不错! (测试变为绿色)
@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]