Skip to content

Commit 9d7f684

Browse files
Added transactions to the complex demo
1 parent a40c860 commit 9d7f684

5 files changed

Lines changed: 190 additions & 41 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package software.xdev.spring.data.eclipse.store.demo.complex;
2+
3+
import org.eclipse.store.integrations.spring.boot.types.configuration.EclipseStoreProperties;
4+
import org.eclipse.store.integrations.spring.boot.types.factories.EmbeddedStorageFoundationFactory;
5+
import org.springframework.beans.factory.ObjectProvider;
6+
import org.springframework.beans.factory.annotation.Autowired;
7+
import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
8+
import org.springframework.context.annotation.Bean;
9+
import org.springframework.context.annotation.Configuration;
10+
import org.springframework.transaction.PlatformTransactionManager;
11+
12+
import software.xdev.spring.data.eclipse.store.repository.config.EclipseStoreClientConfiguration;
13+
import software.xdev.spring.data.eclipse.store.repository.config.EnableEclipseStoreRepositories;
14+
15+
16+
@Configuration
17+
@EnableEclipseStoreRepositories
18+
public class ComplexConfiguration extends EclipseStoreClientConfiguration
19+
{
20+
@Autowired
21+
public ComplexConfiguration(
22+
final EclipseStoreProperties defaultEclipseStoreProperties,
23+
final EmbeddedStorageFoundationFactory defaultEclipseStoreProvider
24+
)
25+
{
26+
super(defaultEclipseStoreProperties, defaultEclipseStoreProvider);
27+
}
28+
29+
/**
30+
* Overriding {@link #transactionManager(ObjectProvider)} only to add the {@link Bean}-Annotation.
31+
*/
32+
@Bean
33+
@Override
34+
public PlatformTransactionManager transactionManager(final ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers)
35+
{
36+
return super.transactionManager(transactionManagerCustomizers);
37+
}
38+
}

spring-data-eclipse-store-demo/src/main/java/software/xdev/spring/data/eclipse/store/demo/complex/ComplexDemoApplication.java

Lines changed: 26 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import org.slf4j.Logger;
2222
import org.slf4j.LoggerFactory;
23+
import org.springframework.beans.factory.annotation.Autowired;
2324
import org.springframework.boot.CommandLineRunner;
2425
import org.springframework.boot.SpringApplication;
2526
import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -31,24 +32,23 @@
3132
import software.xdev.spring.data.eclipse.store.demo.complex.owner.Pet;
3233
import software.xdev.spring.data.eclipse.store.demo.complex.owner.PetType;
3334
import software.xdev.spring.data.eclipse.store.demo.complex.owner.Visit;
34-
import software.xdev.spring.data.eclipse.store.demo.complex.vet.Specialty;
35-
import software.xdev.spring.data.eclipse.store.demo.complex.vet.Vet;
36-
import software.xdev.spring.data.eclipse.store.demo.complex.vet.VetRepository;
37-
import software.xdev.spring.data.eclipse.store.repository.config.EnableEclipseStoreRepositories;
3835

3936

4037
@SpringBootApplication
41-
@EnableEclipseStoreRepositories
4238
public class ComplexDemoApplication implements CommandLineRunner
4339
{
4440
private static final Logger LOG = LoggerFactory.getLogger(ComplexDemoApplication.class);
4541
private final OwnerRepository ownerRepository;
46-
private final VetRepository vetRepository;
42+
private final VetService vetService;
4743

48-
public ComplexDemoApplication(final OwnerRepository ownerRepository, final VetRepository vetRepository)
44+
@Autowired
45+
public ComplexDemoApplication(
46+
final OwnerRepository ownerRepository,
47+
final VetService vetService
48+
)
4949
{
5050
this.ownerRepository = ownerRepository;
51-
this.vetRepository = vetRepository;
51+
this.vetService = vetService;
5252
}
5353

5454
public static void main(final String[] args)
@@ -60,19 +60,13 @@ public static void main(final String[] args)
6060
@Override
6161
public void run(final String... args)
6262
{
63-
LOG.info("----Vets-BeforeDeleteAll----");
64-
this.vetRepository.findAll().forEach(i -> LOG.info(i.toString()));
65-
this.vetRepository.deleteAll();
66-
67-
LOG.info("----Vets-AfterDeleteAll----");
68-
this.vetRepository.findAll().forEach(i -> LOG.info(i.toString()));
69-
70-
final Vet vet = createVet();
71-
this.vetRepository.save(vet);
72-
73-
LOG.info("----Vets-AfterSave----");
74-
this.vetRepository.findAll().forEach(i -> LOG.info(i.toString()));
63+
this.transactionalVetCalls();
7564

65+
this.ownerCalls();
66+
}
67+
68+
private void ownerCalls()
69+
{
7670
LOG.info("----Owner-BeforeDeleteAll----");
7771
this.ownerRepository.findAll(Pageable.unpaged()).forEach(i -> LOG.info(i.toString()));
7872
this.ownerRepository.deleteAll();
@@ -116,6 +110,18 @@ public void run(final String... args)
116110
);
117111
}
118112

113+
/**
114+
* Each of these calls are one transaction.
115+
*/
116+
private void transactionalVetCalls()
117+
{
118+
this.vetService.logVetEntries();
119+
this.vetService.deleteAll();
120+
this.vetService.logVetEntries();
121+
this.vetService.saveNewEntries();
122+
this.vetService.logVetEntries();
123+
}
124+
119125
private static Visit createVisit()
120126
{
121127
final Visit visit = new Visit();
@@ -124,17 +130,6 @@ private static Visit createVisit()
124130
return visit;
125131
}
126132

127-
private static Vet createVet()
128-
{
129-
final Vet vet = new Vet();
130-
vet.setFirstName("Mick");
131-
vet.setLastName("Fleetwood");
132-
final Specialty specialty = new Specialty();
133-
specialty.setName("Vaccination");
134-
vet.addSpecialty(specialty);
135-
return vet;
136-
}
137-
138133
@SuppressWarnings("checkstyle:MagicNumber")
139134
private static Owner createOwner()
140135
{
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package software.xdev.spring.data.eclipse.store.demo.complex;
2+
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.stereotype.Service;
7+
import org.springframework.transaction.annotation.Transactional;
8+
9+
import software.xdev.spring.data.eclipse.store.demo.complex.vet.Specialty;
10+
import software.xdev.spring.data.eclipse.store.demo.complex.vet.Vet;
11+
import software.xdev.spring.data.eclipse.store.demo.complex.vet.VetRepository;
12+
13+
14+
@Service
15+
@Transactional
16+
public class VetService
17+
{
18+
private static final Logger LOG = LoggerFactory.getLogger(VetService.class);
19+
private final VetRepository vetRepository;
20+
21+
@Autowired
22+
public VetService(final VetRepository vetRepository)
23+
{
24+
this.vetRepository = vetRepository;
25+
}
26+
27+
public void deleteAll()
28+
{
29+
this.vetRepository.deleteAll();
30+
LOG.info("----Deleted all vets----");
31+
}
32+
33+
public void saveNewEntries()
34+
{
35+
final Vet vet = this.createVet();
36+
this.vetRepository.save(vet);
37+
LOG.info("----Stored new vet----");
38+
}
39+
40+
public void logVetEntries()
41+
{
42+
LOG.info("----All current stored vets----");
43+
this.vetRepository.findAll().forEach(i -> LOG.info(i.toString()));
44+
}
45+
46+
private Vet createVet()
47+
{
48+
final Vet vet = new Vet();
49+
vet.setFirstName("Mick");
50+
vet.setLastName("Fleetwood");
51+
final Specialty specialty = new Specialty();
52+
specialty.setName("Vaccination");
53+
vet.addSpecialty(specialty);
54+
return vet;
55+
}
56+
}

spring-data-eclipse-store-demo/src/main/java/software/xdev/spring/data/eclipse/store/demo/complex/vet/VetRepository.java

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,9 @@
1515
*/
1616
package software.xdev.spring.data.eclipse.store.demo.complex.vet;
1717

18-
import java.util.Collection;
18+
import org.springframework.data.repository.CrudRepository;
1919

20-
import org.springframework.data.repository.Repository;
2120

22-
23-
public interface VetRepository extends Repository<Vet, Integer>
21+
public interface VetRepository extends CrudRepository<Vet, Integer>
2422
{
25-
Collection<Vet> findAll();
26-
27-
void deleteAll();
28-
29-
@SuppressWarnings("UnusedReturnValue")
30-
Vet save(final Vet entity);
3123
}

spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/transactions/TransactionsConcurrencyTest.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,72 @@ void testSaveConcurrently_PreviouslyExistingAccounts(
123123
assertEquals(testAccounts.size(), accounts.size());
124124
accounts.forEach(account -> Assertions.assertEquals(BigDecimal.valueOf(9), account.getBalance()));
125125
}
126+
127+
/**
128+
* Here it is enough if all the executions are running through. The final balance of the account varies with
129+
* different CPUs.
130+
*/
131+
@Test
132+
void testSaveConcurrently_ChangesOnSameAccount(
133+
@Autowired final PlatformTransactionManager transactionManager)
134+
throws InterruptedException
135+
{
136+
final Account account = new Account(1, BigDecimal.ZERO);
137+
this.accountRepository.save(account);
138+
139+
final ExecutorService service = Executors.newFixedThreadPool(10);
140+
final CountDownLatch latch = new CountDownLatch(100);
141+
IntStream.range(0, 100).forEach(
142+
i ->
143+
service.execute(() ->
144+
{
145+
new TransactionTemplate(transactionManager).execute(
146+
status ->
147+
{
148+
final Account loadedAccount = this.accountRepository.findById(1).get();
149+
loadedAccount.setBalance(loadedAccount.getBalance().add(BigDecimal.ONE));
150+
this.accountRepository.save(loadedAccount);
151+
return null;
152+
});
153+
latch.countDown();
154+
}
155+
)
156+
);
157+
158+
assertTrue(latch.await(5, TimeUnit.SECONDS));
159+
}
160+
161+
@Test
162+
void testSaveConcurrently_ChangesOnSameAccount_MassRollback(
163+
@Autowired final PlatformTransactionManager transactionManager)
164+
throws InterruptedException
165+
{
166+
final Account account = new Account(1, BigDecimal.ZERO);
167+
this.accountRepository.save(account);
168+
169+
final ExecutorService service = Executors.newFixedThreadPool(10);
170+
final CountDownLatch latch = new CountDownLatch(100);
171+
IntStream.range(0, 100).forEach(
172+
i ->
173+
service.execute(() ->
174+
{
175+
Assertions.assertThrows(
176+
RuntimeException.class, () ->
177+
new TransactionTemplate(transactionManager).execute(
178+
status ->
179+
{
180+
final Account loadedAccount = this.accountRepository.findById(1).get();
181+
loadedAccount.setBalance(loadedAccount.getBalance().add(BigDecimal.ONE));
182+
this.accountRepository.save(loadedAccount);
183+
throw new RuntimeException("Random exception");
184+
})
185+
);
186+
latch.countDown();
187+
}
188+
)
189+
);
190+
191+
assertTrue(latch.await(5, TimeUnit.SECONDS));
192+
Assertions.assertEquals(BigDecimal.ZERO, this.accountRepository.findById(1).get().getBalance());
193+
}
126194
}

0 commit comments

Comments
 (0)