Spring Transaction API implementation. #1390

pull/1423/head
Nikita 7 years ago
parent 68506b455b
commit 194b457094

@ -151,6 +151,12 @@
<version>[3.1,5.0)</version> <version>[3.1,5.0)</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>[3.1,5.0)</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>net.jpountz.lz4</groupId> <groupId>net.jpountz.lz4</groupId>

@ -0,0 +1,22 @@
package org.redisson.spring.transaction;
import org.redisson.api.RTransaction;
/**
*
* @author Nikita Koksharov
*
*/
public class RedissonTransactionHolder {
private RTransaction transaction;
public RTransaction getTransaction() {
return transaction;
}
public void setTransaction(RTransaction transaction) {
this.transaction = transaction;
}
}

@ -15,58 +15,112 @@
*/ */
package org.redisson.spring.transaction; package org.redisson.spring.transaction;
import org.redisson.api.RBatch; import java.util.concurrent.TimeUnit;
import org.redisson.api.RTransaction;
import org.redisson.api.RedissonClient; import org.redisson.api.RedissonClient;
import org.redisson.api.TransactionOptions;
import org.springframework.transaction.NoTransactionException;
import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException; import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionSystemException;
import org.springframework.transaction.support.AbstractPlatformTransactionManager; import org.springframework.transaction.support.AbstractPlatformTransactionManager;
import org.springframework.transaction.support.DefaultTransactionStatus; import org.springframework.transaction.support.DefaultTransactionStatus;
import org.springframework.transaction.support.ResourceTransactionManager; import org.springframework.transaction.support.ResourceTransactionManager;
import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
*
* @author Nikita Koksharov
*
*/
public class RedissonTransactionManager extends AbstractPlatformTransactionManager implements ResourceTransactionManager { public class RedissonTransactionManager extends AbstractPlatformTransactionManager implements ResourceTransactionManager {
private static final long serialVersionUID = -6151310954082124041L;
private RedissonClient redisson; private RedissonClient redisson;
public RedissonTransactionManager(RedissonClient redisson) {
this.redisson = redisson;
}
public RTransaction getCurrentTransaction() {
RedissonTransactionHolder to = (RedissonTransactionHolder) TransactionSynchronizationManager.getResource(redisson);
if (to == null) {
throw new NoTransactionException("No transaction is available for the current thread");
}
return to.getTransaction();
}
@Override @Override
protected Object doGetTransaction() throws TransactionException { protected Object doGetTransaction() throws TransactionException {
RedissonTransactionObject tObject = new RedissonTransactionObject(); RedissonTransactionObject transactionObject = new RedissonTransactionObject();
TransactionSynchronizationManager.getResource(redisson); RedissonTransactionHolder holder = (RedissonTransactionHolder) TransactionSynchronizationManager.getResource(redisson);
// TODO Auto-generated method stub if (holder != null) {
return null; transactionObject.setTransactionHolder(holder);
}
return transactionObject;
} }
@Override @Override
protected boolean isExistingTransaction(Object transaction) throws TransactionException { protected boolean isExistingTransaction(Object transaction) throws TransactionException {
// TODO Auto-generated method stub RedissonTransactionObject transactionObject = (RedissonTransactionObject) transaction;
return super.isExistingTransaction(transaction); return transactionObject.getTransactionHolder() != null;
} }
@Override @Override
protected void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException { protected void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException {
RedissonTransactionObject tObject = (RedissonTransactionObject) transaction; RedissonTransactionObject tObject = (RedissonTransactionObject) transaction;
if (tObject.getBatch() == null) { if (tObject.getTransactionHolder() == null) {
RBatch batch = redisson.createBatch(); int timeout = determineTimeout(definition);
batch.atomic(); TransactionOptions options = TransactionOptions.defaults();
tObject.setBatch(batch); if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
options.timeout(timeout, TimeUnit.SECONDS);
}
RTransaction trans = redisson.createTransaction(options);
RedissonTransactionHolder holder = new RedissonTransactionHolder();
holder.setTransaction(trans);
tObject.setTransactionHolder(holder);
TransactionSynchronizationManager.bindResource(redisson, holder);
} }
} }
@Override @Override
protected void doCommit(DefaultTransactionStatus status) throws TransactionException { protected void doCommit(DefaultTransactionStatus status) throws TransactionException {
// TODO Auto-generated method stub RedissonTransactionObject to = (RedissonTransactionObject) status.getTransaction();
try {
to.getTransactionHolder().getTransaction().commit();
} catch (TransactionException e) {
throw new TransactionSystemException("Unable to commit transaction", e);
}
} }
@Override @Override
protected void doRollback(DefaultTransactionStatus status) throws TransactionException { protected void doRollback(DefaultTransactionStatus status) throws TransactionException {
// TODO Auto-generated method stub RedissonTransactionObject to = (RedissonTransactionObject) status.getTransaction();
try {
to.getTransactionHolder().getTransaction().rollback();
} catch (TransactionException e) {
throw new TransactionSystemException("Unable to commit transaction", e);
}
} }
@Override
protected void doSetRollbackOnly(DefaultTransactionStatus status) throws TransactionException {
RedissonTransactionObject to = (RedissonTransactionObject) status.getTransaction();
to.setRollbackOnly(true);
}
@Override
protected void doCleanupAfterCompletion(Object transaction) {
TransactionSynchronizationManager.unbindResourceIfPossible(redisson);
RedissonTransactionObject to = (RedissonTransactionObject) transaction;
to.getTransactionHolder().setTransaction(null);
}
@Override @Override
public Object getResourceFactory() { public Object getResourceFactory() {
return redisson; return redisson;

@ -15,25 +15,33 @@
*/ */
package org.redisson.spring.transaction; package org.redisson.spring.transaction;
import org.redisson.api.RBatch;
import org.springframework.transaction.support.SmartTransactionObject; import org.springframework.transaction.support.SmartTransactionObject;
/**
*
* @author Nikita Koksharov
*
*/
public class RedissonTransactionObject implements SmartTransactionObject { public class RedissonTransactionObject implements SmartTransactionObject {
private RBatch batch; private boolean isRollbackOnly;
private RedissonTransactionHolder transactionHolder;
public RBatch getBatch() {
return batch; public RedissonTransactionHolder getTransactionHolder() {
return transactionHolder;
} }
public void setBatch(RBatch batch) { public void setTransactionHolder(RedissonTransactionHolder transaction) {
this.batch = batch; this.transactionHolder = transaction;
} }
public void setRollbackOnly(boolean isRollbackOnly) {
this.isRollbackOnly = isRollbackOnly;
}
@Override @Override
public boolean isRollbackOnly() { public boolean isRollbackOnly() {
// TODO Auto-generated method stub return isRollbackOnly;
return false;
} }
@Override @Override

@ -0,0 +1,39 @@
package org.redisson.spring.transaction;
import javax.annotation.PreDestroy;
import org.redisson.BaseTest;
import org.redisson.api.RedissonClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement
public class RedissonTransactionContextConfig {
@Bean
public TransactionalBean2 transactionBean2() {
return new TransactionalBean2();
}
@Bean
public TransactionalBean transactionBean() {
return new TransactionalBean();
}
@Bean
public RedissonTransactionManager transactionManager(RedissonClient redisson) {
return new RedissonTransactionManager(redisson);
}
@Bean
public RedissonClient redisson() {
return BaseTest.createInstance();
}
@PreDestroy
public void destroy() {
redisson().shutdown();
}
}

@ -0,0 +1,90 @@
package org.redisson.spring.transaction;
import static org.assertj.core.api.Assertions.*;
import java.io.IOException;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.redisson.RedisRunner;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.TransactionSuspensionNotSupportedException;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = RedissonTransactionContextConfig.class)
public class RedissonTransactionManagerTest {
@Autowired
private RedissonClient redisson;
@Autowired
private TransactionalBean transactionalBean;
@BeforeClass
public static void beforeClass() throws IOException, InterruptedException {
RedisRunner.startDefaultRedisServerInstance();
}
@AfterClass
public static void afterClass() throws IOException, InterruptedException {
RedisRunner.shutDownDefaultRedisServerInstance();
}
@Test
public void test() {
transactionalBean.testTransactionIsNotNull();
transactionalBean.testNoTransaction();
transactionalBean.testCommit();
RMap<String, String> map1 = redisson.getMap("test1");
assertThat(map1.get("1")).isEqualTo("2");
try {
transactionalBean.testRollback();
Assert.fail();
} catch (IllegalStateException e) {
// skip
}
RMap<String, String> map2 = redisson.getMap("test2");
assertThat(map2.get("1")).isNull();
transactionalBean.testCommitAfterRollback();
assertThat(map2.get("1")).isEqualTo("2");
try {
transactionalBean.testNestedNewTransaction();
Assert.fail();
} catch (TransactionSuspensionNotSupportedException e) {
// skip
}
RMap<String, String> mapTr1 = redisson.getMap("tr1");
assertThat(mapTr1.get("1")).isNull();
RMap<String, String> mapTr2 = redisson.getMap("tr2");
assertThat(mapTr2.get("2")).isNull();
transactionalBean.testPropagationRequired();
RMap<String, String> mapTr3 = redisson.getMap("tr3");
assertThat(mapTr3.get("2")).isEqualTo("4");
try {
transactionalBean.testPropagationRequiredWithException();
Assert.fail();
} catch (IllegalStateException e) {
// skip
}
RMap<String, String> mapTr4 = redisson.getMap("tr4");
assertThat(mapTr4.get("1")).isNull();
RMap<String, String> mapTr5 = redisson.getMap("tr5");
assertThat(mapTr5.get("2")).isNull();
}
}

@ -0,0 +1,74 @@
package org.redisson.spring.transaction;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Assert;
import org.redisson.api.RTransaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.NoTransactionException;
import org.springframework.transaction.annotation.Transactional;
public class TransactionalBean {
@Autowired
private RedissonTransactionManager transactionManager;
@Autowired
private TransactionalBean2 transactionalBean2;
@Transactional
public void testTransactionIsNotNull() {
RTransaction transaction = transactionManager.getCurrentTransaction();
assertThat(transaction).isNotNull();
}
public void testNoTransaction() {
try {
RTransaction transaction = transactionManager.getCurrentTransaction();
Assert.fail();
} catch (NoTransactionException e) {
// skip
}
}
@Transactional
public void testCommit() {
RTransaction transaction = transactionManager.getCurrentTransaction();
transaction.getMap("test1").put("1", "2");
}
@Transactional
public void testRollback() {
RTransaction transaction = transactionManager.getCurrentTransaction();
transaction.getMap("test2").put("1", "2");
throw new IllegalStateException();
}
@Transactional
public void testCommitAfterRollback() {
RTransaction transaction = transactionManager.getCurrentTransaction();
transaction.getMap("test2").put("1", "2");
}
@Transactional
public void testNestedNewTransaction() {
RTransaction transaction = transactionManager.getCurrentTransaction();
transaction.getMap("tr1").put("1", "0");
transactionalBean2.testInNewTransaction();
}
@Transactional
public void testPropagationRequired() {
transactionalBean2.testPropagationRequired();
}
@Transactional
public void testPropagationRequiredWithException() {
RTransaction transaction = transactionManager.getCurrentTransaction();
transaction.getMap("tr4").put("1", "0");
transactionalBean2.testPropagationRequiredWithException();
}
}

@ -0,0 +1,32 @@
package org.redisson.spring.transaction;
import org.redisson.api.RTransaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
public class TransactionalBean2 {
@Autowired
private RedissonTransactionManager transactionManager;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void testInNewTransaction() {
RTransaction transaction = transactionManager.getCurrentTransaction();
transaction.getMap("tr2").put("2", "4");
}
@Transactional
public void testPropagationRequired() {
RTransaction transaction = transactionManager.getCurrentTransaction();
transaction.getMap("tr3").put("2", "4");
}
@Transactional
public void testPropagationRequiredWithException() {
RTransaction transaction = transactionManager.getCurrentTransaction();
transaction.getMap("tr5").put("2", "4");
throw new IllegalStateException();
}
}
Loading…
Cancel
Save