English 中文(简体)
Transactional not working while implementing AbstractMongoEventListener
原标题:

I try to save a Parent document containing a list of Child document. While saving a Parent, i check if the nested Children need to be saved based on an annotation. if yes, i save the Child document before the parent document and i want to cancel the operations if the Parent document can t be saved.

To test it, i throw an exception just after saving the Child Document and check if the document is saved.

Here the code i use, this is my implementation of AbstractMongoEventListener.onBeforeConvert():

@Component
public class CascadeSaveMongoListener extends AbstractMongoEventListener<Object> {

    private final ReactiveMongoTemplate reactiveMongoTemplate;

    public CascadeSaveMongoListener(ReactiveMongoTemplate reactiveMongoTemplate) {
        this.reactiveMongoTemplate = reactiveMongoTemplate;
    }

    @Override
    public void onBeforeConvert(BeforeConvertEvent<Object> event) {
        ReflectionUtils.doWithFields(event.getSource().getClass(), new CascadeSaveCallback(event, reactiveMongoTemplate));
    }

}

and this Callback:

public class CascadeSaveCallback implements ReflectionUtils.FieldCallback {

    private MongoMappingEvent event;
    private ReactiveMongoTemplate reactiveMongoTemplate;

    CascadeSaveCallback(MongoMappingEvent event, ReactiveMongoTemplate reactiveMongoTemplate) {
        this.event = event;
        this.reactiveMongoTemplate = reactiveMongoTemplate;
    }

    @Override
    public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
        ReflectionUtils.makeAccessible(field);
        if (field.isAnnotationPresent(DBRef.class) && field.isAnnotationPresent(CascadeSave.class)) {
            final Object fieldValue = field.get(event.getSource());
            if (Collection.class.isAssignableFrom(fieldValue.getClass())) {
                Mono<Collection<Object>> mono = Mono.just((Collection<Object>) fieldValue);
                reactiveMongoTemplate.insertAll(mono).then().subscribe();
                throw new IllegalArgumentException("Not yet implemented");
            } else {
                reactiveMongoTemplate.insert(fieldValue);
            }
        }
    }
}

To give you more information, I use mongo with docker from the official image and here the command i use to run it with replica and enable transaction :

$ docker run --name my-mongo -p 27017:27017 -d mongo --replSet rs0 && sleep 2 && docker exec my-mongo mongosh --eval "rs.initiate();"

and here the SpringBootApplication class, the service, the Entity and my mongoReactiveConfig class:

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableTransactionManagement
@EnableMongoRepositories
public class ApiApplication {
    public static void main(String[] args) {
        SpringApplication.run(ApiApplication.class, args);
    }

}
@Service
public class ApiService {

    private final ParentRepository parentRepository;

    public ApiService(ParentRepository parentRepository) {
        this.parentRepository = parentRepository;
    }
    
    @Transactional
    public Mono<Parent> save(Parent parent) {
        return parentRepository.save(parent);
    }
}
@Document
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Parent extends BaseEntity {
    @JsonFormat(pattern = "dd-MM-yyyy HH:mm:ss")
    private LocalDateTime startDate;

    @DBRef
    @CascadeSave
    List<Child> children;
}
@Configuration
public class MongoReactiveConfig extends AbstractReactiveMongoConfiguration {

    @Bean
    ReactiveMongoTransactionManager transactionManager(ReactiveMongoDatabaseFactory factory) {
        return new ReactiveMongoTransactionManager(factory);
    }

    @Override
    public MongoClient reactiveMongoClient() {
        return MongoClients.create("mongodb://localhost:27017/api");
    }

    @Override
    protected String getDatabaseName() {
        return "api";
    }
}

First, i tried without injecting the ReactiveMongoTemplate. but the transaction didn t work. Then i tried with it and i get the same result.

When i run the code, i can see the exception, but i still see the Child document in mongoDB and i expect it to not be here.

Do anyone have an idea please ? PS: English is not my native langage, i apologize for any mistakes i did.

Edit : From the logs i get, it seems related to the bound between the thread where ApiService.save() is called and the one used for the custom listener. A new transaction is created, then the custom listener throw an exception and the rollback start. but then i can see the starting of the insert instruction.

问题回答

暂无回答




相关问题
Spring Properties File

Hi have this j2ee web application developed using spring framework. I have a problem with rendering mnessages in nihongo characters from the properties file. I tried converting the file to ascii using ...

Logging a global ID in multiple components

I have a system which contains multiple applications connected together using JMS and Spring Integration. Messages get sent along a chain of applications. [App A] -> [App B] -> [App C] We set a ...

Java Library Size

If I m given two Java Libraries in Jar format, 1 having no bells and whistles, and the other having lots of them that will mostly go unused.... my question is: How will the larger, mostly unused ...

How to get the Array Class for a given Class in Java?

I have a Class variable that holds a certain type and I need to get a variable that holds the corresponding array class. The best I could come up with is this: Class arrayOfFooClass = java.lang....

SQLite , Derby vs file system

I m working on a Java desktop application that reads and writes from/to different files. I think a better solution would be to replace the file system by a SQLite database. How hard is it to migrate ...

热门标签