fix(personal): require outer transaction for reservation
This commit is contained in:
+2
-1
@@ -6,6 +6,7 @@ import org.dromara.common.core.exception.ServiceException;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -46,7 +47,7 @@ public class PersonalSpaceService {
|
||||
*
|
||||
* @return the locked personal space id
|
||||
*/
|
||||
@Transactional
|
||||
@Transactional(propagation = Propagation.MANDATORY)
|
||||
public long reserve(PersonalOwner owner, long bytes) {
|
||||
if (bytes < 0 || properties.getMaxItems() <= 0 || properties.getMaxSpaceMb() <= 0) {
|
||||
throw new ServiceException(QUOTA_EXCEEDED);
|
||||
|
||||
+63
@@ -6,8 +6,17 @@ import org.dromara.aihr.personal.support.PersonalOwner;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.transaction.IllegalTransactionStateException;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
|
||||
import org.springframework.transaction.interceptor.TransactionInterceptor;
|
||||
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
|
||||
import org.springframework.transaction.support.DefaultTransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -188,6 +197,35 @@ class PersonalSpaceServiceTest {
|
||||
verifyNoInteractions(jdbc);
|
||||
}
|
||||
|
||||
@Test
|
||||
void reserveRequiresAnExistingOuterTransactionThroughTheSpringProxy() {
|
||||
JdbcTemplate jdbc = mock(JdbcTemplate.class);
|
||||
PersonalSpaceService target = new PersonalSpaceService(jdbc, properties());
|
||||
TestTransactionManager transactionManager = new TestTransactionManager();
|
||||
ProxyFactory factory = new ProxyFactory(target);
|
||||
factory.setProxyTargetClass(true);
|
||||
TransactionInterceptor interceptor = new TransactionInterceptor();
|
||||
interceptor.setTransactionManager(transactionManager);
|
||||
interceptor.setTransactionAttributeSource(new AnnotationTransactionAttributeSource());
|
||||
interceptor.afterPropertiesSet();
|
||||
factory.addAdvice(interceptor);
|
||||
PersonalSpaceService proxy = (PersonalSpaceService) factory.getProxy();
|
||||
PersonalOwner owner = new PersonalOwner("000000", 101L, null);
|
||||
|
||||
assertThrows(IllegalTransactionStateException.class, () -> proxy.reserve(owner, 1L));
|
||||
verifyNoInteractions(jdbc);
|
||||
|
||||
when(jdbc.queryForMap(contains("from aihr_personal_space"), eq("000000"), eq(101L)))
|
||||
.thenReturn(space(7L, 1024L, 0L, 0));
|
||||
Long spaceId = new TransactionTemplate(transactionManager)
|
||||
.execute(status -> proxy.reserve(owner, 1L));
|
||||
|
||||
assertEquals(7L, spaceId);
|
||||
verify(jdbc).update(contains("on duplicate key update"),
|
||||
eq("000000"), eq(101L), eq(null), eq(500L * 1024 * 1024));
|
||||
verify(jdbc).queryForMap(contains("for update"), eq("000000"), eq(101L));
|
||||
}
|
||||
|
||||
private static Map<String, Object> space(long id, long quota, long used, int count) {
|
||||
return Map.of(
|
||||
"id", id,
|
||||
@@ -200,4 +238,29 @@ class PersonalSpaceServiceTest {
|
||||
private static PersonalKnowledgeProperties properties() {
|
||||
return new PersonalKnowledgeProperties();
|
||||
}
|
||||
|
||||
private static final class TestTransactionManager extends AbstractPlatformTransactionManager {
|
||||
|
||||
@Override
|
||||
protected Object doGetTransaction() {
|
||||
return new Object();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isExistingTransaction(Object transaction) {
|
||||
return TransactionSynchronizationManager.isActualTransactionActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBegin(Object transaction, TransactionDefinition definition) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doCommit(DefaultTransactionStatus status) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRollback(DefaultTransactionStatus status) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user