I'm struggling with editing and updating nested objects because of this Hibernate Lazy Loading error that I keep hitting again and again.
In my application:
- I bind an Image Set to a product.
- I've used the formatter to convert the imageset for use in a select box, so the user can select the image set.
- It works well if the form succeeds, the object binds and the relationship saves correctly.
- But if there's a validation failure, whenever the UI layer tries to access the nested object value I get the dreaded Lazy Initialization/No Session error!
So I tried to write a method to initialize the related objects as a workaround:
@Transactional
public void initObject(Variant variant) {
Hibernate.initialize(variant.getOption1());
Hibernate.initialize(variant.getOption2());
Hibernate.initialize(variant.getOption3());
Hibernate.initialize(variant.getImageSet());
}
The first 3 lines in that method work (these are unidirectional one to one), but the fourth doesn't (this is a many to one), it still says no proxy.
I've also tried making everything Eager fetch type.
Neither of these worked.
The issue is probably that I'm creating the parent object, it doesn't get saved to the database when there's a validation fault, but the nested object is for whatever reason a lazy proxy object regardless of the fetch type (see formatter below).
How can I fix this?
This is the formatter component:
@Component
public class ImageSetFormatter implements Formatter<ProductImageSet> {
private static Logger logger = LogManager.getLogger(VariantController.class.getName());
@Autowired
private ProductImageService imageService;
@Override
public ProductImageSet parse(String s, Locale locale) throws ParseException {
logger.entry();
Long imageSetId = Long.valueOf(s);
logger.exit();
return imageService.getImageSet(imageSetId);
}
@Override
public String print(ProductImageSet productImageSet, Locale locale) {
logger.entry();
logger.exit();
return Long.toString(productImageSet.getId());
}
}
Stacktrace when the line Hibernate.initialize(variant.getImageSet()) is called:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.hibernate.LazyInitializationException: could not initialize proxy - no Session org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:973) org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863) javax.servlet.http.HttpServlet.service(HttpServlet.java:644) org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837) javax.servlet.http.HttpServlet.service(HttpServlet.java:725) org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113) org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103) org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
Relevant Object Relationships:
@Entity
@Table(name="variants")
@EntityListeners({AuditingEntityListener.class})
public class Variant extends AbstractAuditable<Customer, Long> {
@OneToOne
private VariantOptionValue option1;
@OneToOne
private VariantOptionValue option2;
@OneToOne
private VariantOptionValue option3;
...
@ManyToOne
@JoinColumn(name="image_set_id")
@LazyCollection(LazyCollectionOption.FALSE )
private ProductImageSet imageSet;
}
@Entity
@Table(name="product_image_set")
@EntityListeners({AuditingEntityListener.class})
public class ProductImageSet extends AbstractAuditable<Customer, Long> {
public ProductImageSet(String label)
{
this.label = label;
}
public ProductImageSet(){
}
@Basic
@Column(length = 50, nullable = false)
private String label;
@OneToMany(mappedBy = "imageSet", fetch = FetchType.EAGER)
private List<ProductImage> images;
@OneToMany(mappedBy = "imageSet", fetch = FetchType.EAGER)
private List<Variant> variants;
private int sequence;
@ManyToOne
@JoinColumn(name = "product_id")
private Product product;
...
}