11

My expectation is that a lazy loaded collection should be fetched when the collection is accessed within a transactional scope. For example, if I want to fetch a collection I can call foo.getBars.size(). The absence of an active transaction should result in an exception with an error message like

failed to lazily initialize a collection of bars: .... could not initialize proxy - no Session

However, I noticed that the behavior is different in my latest application. I'm using Spring Boot 1.5.1 with the "data-jpa" starter. I have used Spring Boot in the past, but the data-jpa starter is new for me.

Consider the following case. I have a lazy loaded ManyToMany collection.

@SuppressWarnings("serial")
@Entity
@Table(name = "foo")
public class Foo implements java.io.Serializable {
    ....
    private Set<Bar> bars = new HashSet<Bar>(0);
    ....

    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinTable(name = "foo_bar_map",
        joinColumns = {@JoinColumn(name = "foo_id", nullable = false, updatable = false)},
        inverseJoinColumns = {@JoinColumn(name = "bar_id", nullable = false, updatable = false)})
    public Set<Bar> getBars() {
        return this.bars;
    }

    public void setBar(Set<Bar> bars) {
        this.bars = bars;
    }

My service method is NOT marked as Transactional but I am accessing a lazy loaded collection

@Service
public class FooServiceImpl implements FooService {

    @Autowired
    private FooRepository fooRepo;


    @Override
    public FooDTO findById(int fooId) {
        Foo foo = fooRepo.findOne(fooId);
        // The FooDTO constructor will access foo.getBars()  
        return new FooDTO(foo);
    }

And for context on the FooDTO constructor

public FooDTO(Foo foo) {
    ...
    for (Bar bar : foo.getBars()) {
        this.bars.add(bar);
    }
}

Contrary to my expectation and past experience, this code executes successfully and fetches the collection. Further, if I throw a breakpoint in my service method, I can step through the code and see the SQL statements in my logs that fetch the bars after my call to the fooRepo. After my call to fooRepo, I expect the transaction to be closed.

What's happening here?

4
  • Who calls the service? My guess is that the caller is transactional. Commented Mar 3, 2017 at 16:56
  • @JBNizet The method is called by a controller which is not marked as transactional either.... Commented Mar 3, 2017 at 16:58
  • 1
    Spring boot seems to use an OpenEntityManagerInViewwInterceptor by default: github.com/spring-projects/spring-boot/blob/master/…. See also docs.spring.io/spring-boot/docs/1.4.2.RELEASE/reference/… (and search for OpenEntityManagerInView) Commented Mar 3, 2017 at 17:01
  • @JBNizet Thanks for digging that up. That effectively answers my question. If posted as an answer, I would accept it. Commented Mar 3, 2017 at 17:08

3 Answers 3

21

Spring Boot uses an OpenEntityManagerInView interceptor by default. You can turn it off by setting the property spring.jpa.open-in-view to false.

See the documentation for the reference about this (and other) JPA properties.

Sign up to request clarification or add additional context in comments.

1 Comment

It doesn't happen in all cases, why so?
3

You could turn on logging to check if a Transaction is being opened.

org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction

or

org.hibernate.engine.transaction.internal.jta.JtaTransaction

Also, you could set a breakpoint and use this static method to check if a transaction is open.

org.springframework.transaction.support.TransactionSynchronizationManager.isActualTransactionActive()

Comments

0

You have to remove the @JoinTable

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.