A Guide to Spring’s Open Session In View

Overview: Spring Open Session in View is one of the transactional patterns, for binding Hibernate session/JPA Entity Manager to user request life-cycle. Spring MVC comes with one of the implementations for an open session in view pattern, named OpenSessionInViewInterceptor or OpenSessionInViewFilter to work with lazy associations, and improving hire Java developer productivity.

Introduction:
For a better understanding of Open Session in View, let’s suppose we have an incoming request, and spring MVC will do the below things on behalf of you:

  1. Spring MVC open a Hibernate session/ JPA Entity Manager at the begging of the request, these sessions initially it may not necessary to obtain the connection from connection-pool, and it will bind the session into TransactionSynchronizationManager class.
  2. During the request life cycle, the application needs the session, it re-uses the session that we already created.
  3. At the end of the request, session will be closed.

This is image title

If we understand the overview of this process, it can boosts the developer productivity as session creation/binding the session/ closing the session will be done by framework.

But sometimes Open Session in View will also cause performance issues mainly in production.

Spring Boot: By default Open Session in View, is enabled.

Since Spring Boot 2.0, if this is enabled, it will log the below warning in the console.

Spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning

We can also disable this property by setting spring.jpa.open-in-view to false.

Pros and Cons of Open Session in View (OSIV):
Pros of OSIV:

  1. Fewer queries need to write in service layer.
  2. It provides the transaction outside of the transactional-context.
  3. Avoid lazyInitializationExceptions.

Cons of OSIV:

  1. OSIV can be easily misused, and it will introduce the N+1 performance issues.
  2. Entity property navigation may invoke number of unnecessary queries.
  3. Every session is executing auto-commit mode, after each SQL statement session will commit the transaction, using read-only transaction type, we can avoid the commit the transaction after each SQL statement execution.

Lazy initialization:
For better understanding the OSIV, let’s consider the example, user is one of the entities, class, having a set of permissions.

@Table(name = "users")
public class User {
 
    @Id
    @GeneratedValue
    private Long id;
 
    private String username;
 
    @ElementCollection
    private Set<String> permissions;
 
    // getters and setters
}```

And sample Service class for finding the user by using username:

```@Service
public class SimpleUserService implements UserService {
 
    private final UserRepository userRepository;
 
    public SimpleUserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
 
    @Override
    @Transactional(readOnly = true)
    public Optional<User> findOne(String username) {
        return userRepository.findByUsername(username);
    }
}```

And Sample User Controller will look like below:

```@RestController
@RequestMapping("/users")
public class UserController {
 
    private final UserService userService;
 
    public UserController(UserService userService) {
        this.userService = userService;
    }
 
    @GetMapping("/{username}")
    public ResponseEntity<?> findOne(@PathVariable String username) {
        return userService
                .findOne(username)
                .map(DetailedUserDto::fromEntity)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());
    }
}```

Here, based on the username path-variable, it will fetch the user from database.

Below is the JUnit test case for testing this:

```@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
class UserControllerIntegrationTest {
 
    @Autowired
    private UserRepository userRepository;
 
    @Autowired
    private MockMvc mockMvc;
 
    @BeforeEach
    void setUp() {
        User user = new User();
        user.setUsername("root");
        user.setPermissions(new HashSet<>(Arrays.asList("PERM_READ", "PERM_WRITE")));
 
        userRepository.save(user);
    }
 
    @Test
    Void givenTheUserExists_WhenOsivIsEnabled_ThenLazyInitWorksEverywhere() throws Exception {
        mockMvc.perform(get("/users/root"))
          .andExpect(status().isOk())
          .andExpect(jsonPath("$.username").value("root"))
          .andExpect(jsonPath("$.permissions", containsInAnyOrder("PERM_READ", "PERM_WRITE")));
    }
}```


If we execute this, it will be successful, if the OSIV is not configured then it will fail, as for permission property, it will throw LazyInitializationException exception.

**Alternatives to OSIV:**
1) A developer can handle all dependencies to load in transaction context using like Hibernate.initialize(user.getPermissions()).
2) We can JPA 2 Entity graphs for fetching all dependent entities.
3) We can use the JOIN FETCH keyword to fetch all nested properties.

Conclusion: we learned about Open Session in View (OSIV), pros and cons of it, and also how Spring boot internally uses OSIV, and what is the alternative to OSIV.

#Spring #MVC #Java #Developer

A Guide to Spring’s Open Session In View
54.05 GEEK