0

I met today very strange problem. I try to rewrite my synchronous service to reactive asynchronous service.

The problem is I get LazyInitializationException but I don't get this exception when I don't use RxJava.

Here I don't get exception:

    @Override
    @Transactional
    public User addOrder(String username, Order order) {
        if (username != null && order!= null) {
            final User user = userRepository.findByUsername(username);
            if (user != null) {
                user.getOrders().add(order);
                order.setUser(user);
                try {
                    return userRepository.save(user);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

In this method I get LazyInitializationException:

    @Override
    @Transactional
    public Observable<User> addOrderAsynchronously(String username, Order order) {
        if (username != null && order != null) {
            return Observable
                    .fromCallable(() -> userRepository.findByUsername(username))
                    .filter(Objects::nonNull)
                    .map(user -> {
                        synchronized (this) {
                             user.getOrders().add(order); // I get here LazyInitializationException
                             order.setUser(user);
                        }
                         return user;
                    })
                    .map(userRepository::save);
    }
    return Observable.empty();
}

The collection orders associated to user looks:

    @OneToMany(
            mappedBy = "user",
            cascade = { CascadeType.PERSIST, CascadeType.MERGE }
    )
    private Collection<Order> orders = new ArrayList<>();

I map this list as Collection because Hibernate doesn't need to initialize a Collection types.

What is the problem?

user
  • 4,410
  • 16
  • 57
  • 83
  • In the first one you run code in a transactional context (i.e. normally). In the second one the transaction is over when `orders` needs to be initialized. – Kayaman Oct 05 '17 at 16:52
  • Why? Could you describe it more? – user Oct 05 '17 at 17:03
  • 1
    Basically because the `addOrderAsynchronously` method is `@Transactional`, but the asynchronously executed code isn't. You'd need the async code to call a method that's marked as `@Transactional`, now you have it the other way around. – Kayaman Oct 05 '17 at 17:06
  • Ok, could you write how to resolve this problem. I don't know if I understood what you said. – user Oct 05 '17 at 17:48
  • This code will be asynchronous when I subscribe it. addAsynchronous(arguments).subscribeOn(Schedulers).subscribe() - it's asynchronous implementation. – user Oct 05 '17 at 17:50
  • Yup, but that means that you have 2 pieces of code. One that's running asynchronously on a different thread at a different time, and one that has the necessary transaction context. You should really try to understand what that means in regard to how the code is being run. – Kayaman Oct 05 '17 at 17:55
  • Does it mean that transactional operations always have to be executed synchronously? – user Oct 05 '17 at 19:43
  • 2
    It means that the transactional context that was started in a synchronized method call doesn't transfer to the asynchronous part that you return from the method. I'm not sure what would be the correct fashion to handle it here, but the `@Transactional` attribute is completely useless here, since it only encompasses code that doesn't even do anything with the database (basically a single `if` statement). – Kayaman Oct 05 '17 at 19:47
  • See https://stackoverflow.com/a/54003162/9090751 – Mafor Dec 18 '19 at 20:33

0 Answers0