Open session in view is evil

Raffaele Esposito
4 min readMar 27, 2020

--

Let’s consider a simple Spring boot application:

pom.xml Spring dependencies

...
<!-- spring -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
...

With no particular settings in our properties files except those required for database connection.

Using container managed transaction scoped JPA EntityManager to manage persistence.

I want to share with you how a few lines of code were enough to put in doubt concepts I considered myself pretty familiar with.

Let’s consider the following snippets, it’s pretty straightforward, we have two entities a controller and a service.

Question Entity

@Entity
@Table(name = "Question")
public class Question {
@Id
@GeneratedValue
@Column(name = "id")
private Long id;
@Column(name = "description")
private String description;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "survey_id")
private Survey survey;

...
}

Survey Entity

@Entity
@Table(name = "Survey")
public class Survey implements Serializable {
@Id
@GeneratedValue
@Column(name = "id")
private Long id;
@Column(name = "name")
private String name;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "survey")
private List<Question> questions;
...
}

Rest Controller

@RestController
@RequestMapping("/default")
public class DefaultEndpoint {


@Autowired
private MyService myService;


@PostMapping(value = "/foo")
public void foo() {
myService.foo();
}
...
}

Service

@Service
public class MyService {

@PersistenceContext
private EntityManager entityManager;

public void foo() {
Survey survey = entityManager.find(Survey.class, 1L);
System.out.println(survey.getQuestions().size());
}
}

Take a closer look to MyService class.

Can you spot why I was puzzled when I saw the actual number of survey questions printed there nicely on my console ?

Two reasons:

  • The survey is not attached to the persistence context anymore because there’s no active persistence context, since the method does not have @Transactional annotation.
  • The fetch policy on questions is LAZY so indeed a persistence context has to be active in order for the results to be retrieved from the database.

So this was the time when I learned about the OSIV (Open Session In View) pattern.

And it’s basically all about avoiding: org.hibernate.LazyInitializationException which would result from the execution of the code above with OSIV not in place. The exception informs that the entity (survey entity in our case), is in a detached state, since the persistence context used to retrieve it has already been closed and the data we are asking for have not been fetched, due to the LAZY fetch policy adopted.

The way OSIV overcomes this situation is trivial, it keeps the persistence context active, even when we do not explicitly ask for it using @Transactional annotation.

Point is, I always considered this exception and behaviour to be very useful because it helps to enforce many best practices:

  • Separation of concerns among layers: Entities should not reach presentation layer, only DTOs should.
  • Decoupling between Entities and DTOs: With this decoupling in place presentation logic and business/persistence logic are decoupled and can evolve with a good degree of freedom from each other.
  • Limiting the stress on the database: While reading the connection to the database remains held just for the time necessary to retrieve the data to populate the DTOs with.

What is even worse is that the spring boot autoconfiguration mechanism makes OSIV active by default.

You are required to explicitly disable it to opt out of this mechanism by adding the following line in your properties file.

spring.jpa.open-in-view=false

So basically we have a mechanism that helps developers write messy and inefficient code, and is enabled by default, can it get any worse ? It can.

Let’s consider now this alternative version of our Service:

Note that I intentionally omitted @Transactional from method foo to show how OSIV might produce some extravagant and harmful behaviour when backed up by some careless code.

@Service
public class MyService {

@PersistenceContext
private EntityManager entityManager;
@Autowired
private QuestionRepo questionRepo;
public void foo() {
Survey survey = entityManager.find(Survey.class, 1L);
survey.setName("example");
Question question = new Question();
question.setDescription("description");
question.setSurvey(survey);
questionRepo.save(question);
}
}

QuestionRepo

public interface QuestionRepo extends CrudRepository<Question, Long> {
}

Spring data implementation of save method

@Transactional
public <S extends T> S save(S entity) {
if (this.entityInformation.isNew(entity)) {
this.em.persist(entity);
return entity;
} else {
return this.em.merge(entity);
}
}

Let’s try to understand what would happen in a normal scenario, with OSIV disabled.

The question would be saved correctly and our attempt to change the name field on the retrieved survey entity would be nicely ignored because the entity is not attached to any persistence context and we are not using any cascading logic (see again the Survey and Question classes).

What happens when OSIV is enabled is weird to say the least.

The change of name on the survey updates the corresponding field on its table row.

What happens here, in simplified words, is that the persistence context remains active after the find operation, so the survey entity also remains attached to it, the save operation uses the same active persistence context again binding it to its transaction (note the @Transactional on save method).

Save method is carrying out a write operation so it flushes the persistence context at the end of the transaction, triggering the dirty checking mechanism for the survey entity.

Now, especially if you consider a chain of calls a bit longer than this, it looks obvious how this mechanism could lead to unexpected behaviour very hard to troubleshoot.

It’s easy to suppose that many applications out there could be already relying on this kind of effects without their developers even being aware of it.

If you are interested in what you read I also suggest you to give a good read to these articles, where you can also find more details on OSIV works under the hood.

--

--

Raffaele Esposito

Back end Java Developer, will try not to talk only about IT related topics :)