Reputation: 7254
I'm developing a Spring Boot based web application. I heavily rely on @ComponentScan
and @EnableAutoConfiguration
and no explicit XML configuration in place.
I have the following problem. I have a JPA-Annotated Entity class called UserSettings
:
@Entity public class UserSettings {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@OneToMany(cascade = CascadeType.ALL)
private Set<Preference> preferences; // 'Preference' is another @Entity class
public UserSettings() {
this.preferences = new HashSet<Preference>();
}
// some more primitive properties, Getters, Setters...
}
I followed this tutorial and created a repository interface that extends JpaRepository<UserSettings,Long>
.
Furthermore, I have a UserManager
bean:
@Component public class SettingsManager {
@Autowired
UserSettingsRepository settingsRepository;
@PostConstruct
protected void init() {
// 'findGlobalSettings' is a simple custom HQL query
UserSettings globalSettings = this.settingsRepository.findGlobalSettings();
if (globalSettings == null) {
globalSettings = new UserSettings();
this.settingsRepository.saveAndFlush(globalSettings);
}
}
Later in the code, I load the UserSettings
object created here, again with the findGlobalSetttings
query.
The problem is: Every time I try to access the @OneToMany
attribute of the settings object, I get the following exception:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role org.example.UserSettings.preferences, could not initialize proxy - no Session
I understand that each HTTP Session has its own Hibernate Session, as described in the accepted answer of this question, but that does not apply in my case (currently I'm testing this within the same HTTP Session), which is why I have no idea where this exception comes from.
What am I doing wrong and how can I achieve circumvent the error?
Upvotes: 2
Views: 13014
Reputation: 13
I faced similar issue in spring boot application, after googling I'm able to fix this issue by adding the following code to my application.
@Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, DataSource dataSource) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.enable_lazy_load_no_trans", true);
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
Referred here.
Upvotes: 1
Reputation: 422
This question has been answered beautifully by @Steve. However, if you still want to maintain your lazy loading
implementation, you may want to try this
import javax.servlet.Filter;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.hibernate4.support.OpenSessionInViewFilter;
@Configuration
@ComponentScan
public class AppConfig {
@Bean
public FilterRegistrationBean filterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(openSessionInView());
registration.addUrlPatterns("/*");
return registration;
}
@Bean
public Filter openSessionInView() {
return new OpenSessionInViewFilter();
}
}
What this configuration does is, it registers a Filter
on requests to path "/*"
which keeps your Hibernate Session open in your view.
This is an anti-pattern and must be used with care.
NOTE: As of Spring Boot 1.3.5.RELEASE, when you use the default configuration with Spring Data JPA auto-configuration, you shouldn't encounter this problem
Upvotes: 3
Reputation: 9480
If you want to be able to access mapped entities outside the transaction (which you seem to be doing), you need to flag it as an "eager" join. i.e.
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
Upvotes: 4