Reputation: 108
I'm using spring boot 2 and the spring data neo4j dependency (which I think brings sdn 5.x) in addition to neo4j-ogm 3.1.1 to manage my persistence. I have noted that the default Neo4jTransactionManager does not support nested transactions or requires new propagation.
What I would like to do is having my service method perform essentially multiple steps in seperate transactions.
In order to achieve what I want I have resorted to spring's async / multi threading support to effectively force a new session and transaction around the steps in my service method, shown below. What I would like to know, is there a better way to solve this?
I feel as though I should be able to easily create seperate transactions / units of work but the out of the box spring data neo4j solution is restricting this.
My service method, the first step I wish to isolate is already in a seperate service marked here by the call connectionService.deleteDiagramConnections(diagram.getId());
@Retryable(value = TransientException.class,exceptionExpression="#{message.contains('RWLock')}", maxAttempts = 5)
public Diagram update(final Diagram diagram) throws GUMLException {
AtomicReference<Diagram> result = new AtomicReference<Diagram>();
AtomicReference<Object> isComplete = new AtomicReference<Object>();
isComplete.set(false);
ListenableFuture future = connectionService.deleteDiagramConnections(diagram.getId());
future.addCallback(new ListenableFutureCallback() {
@Override
public void onFailure(Throwable throwable) {
logger.error(throwable);
}
@Override
public void onSuccess(Object o) {
for (Connection connection : diagram.getConnections()) {
connection.setId(null);
if (connection.getId() != null && connection.getMoveablePoints() != null) {
for (MoveablePoint mp : connection.getMoveablePoints()) {
mp.setId(null);
}
}
}
isComplete.set(true);
}
});
while ((boolean)isComplete.get() == false) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new GUMLException(Severity.ERROR, e.getMessage());
}
if ((boolean)isComplete.get() == true)
result.set(umlDiagramRepository.save(diagram));
}
return result.get();
}
The connection service:
@Async
@Override
public ListenableFuture<String> deleteDiagramConnections(long diagramId) throws GUMLException {
connectionRepository.deleteDiagramConnections(diagramId);
return new AsyncResult<String>("delete complete");
}
Here is my test app config
@org.springframework.context.annotation.Configuration
@ComponentScan(basePackages = "au.com.guml", lazyInit = true)
@EnableTransactionManagement
@EnableAsync
public class TestConfig {
@Bean
public org.neo4j.ogm.config.Configuration configuration() {
org.neo4j.ogm.config.Configuration configuration = new org.neo4j.ogm.config.Configuration.Builder()
.uri("bolt://localhost")
.credentials("neo4j", "password")
.build();
return configuration;
}
// @Bean
// public org.neo4j.ogm.config.Configuration configuration() {
// org.neo4j.ogm.config.Configuration configuration = new org.neo4j.ogm.config.Configuration.Builder()
// .uri("http://neo4j:password@localhost:7474")
// .build();
// return configuration;
// }
// @Bean
// public Configuration getConfiguration() {
//
// Configuration config = new Configuration();
// config
// .driverConfiguration()
// .setDriverClassName("org.neo4j.ogm.drivers.http.driver.HttpDriver")
// .setURI("http://neo4j:password@localhost:7474")
// .setCredentials("neo4j","password");
//
// return config;
// }
@Bean
public SessionFactory sessionFactory() {
// with domain entity base package(s)
return new SessionFactory(configuration() ,"au.com.guml.domain");
}
@Bean
public Neo4jTransactionManager transactionManager() {
return new Neo4jTransactionManager(sessionFactory());
}
// @Bean
// public TaskExecutor syncTaskExecutor () {
// SyncTaskExecutor syncTaskExecutor = new SyncTaskExecutor();
// return syncTaskExecutor;
// }
@Bean
public TaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(4);
executor.setThreadNamePrefix("default_task_executor_thread");
executor.initialize();
return executor;
}
}
Upvotes: 0
Views: 305
Reputation: 108
So it turns out all I needed to do was inject the neo4j ogm session into my service and invoke session.getTransaction().commit();
I then set the connection ids to null in my outer service call and they all get added as though they are new.
Upvotes: 1