Question

NOTE: Ultimately my goal is simply to change the resulting URL from "/public/academy/register?param=blah" to a customized SEO-ified URL, as shown in the code. If I'm on the wrong path by trying to change from returning a "success view" JSP in the POST mapping to instead using post-redirect-get (which is good practice anyway), I'm open to suggestions.

Below are two methods: the POST request mapping to retrieve a registration form and process it, and the mapping method for the success page. I'm adding a flash attribute to redirect, which holds the form POSTed to the first method.

The form has a property hierarchy of Form -> Schedule -> Course -> Content -> Vendors, where each is its own class object except that Vendors is a SortedSet<Vendor>. When I load the success page, I get a Hibernate exception stating that the Vendors could not be lazily initialized. Why is it so far down the chain that it stops loading, or more basically, why is it losing this property value in the first place? When I set a breakpoint before the return, the RedirectAttributes object has the Vendors populated in the form I passed to it. What gives?

@RequestMapping(value = "/public/academy/register", method = RequestMethod.POST)
public String processSubmit(Site site, Section section, User user,
        @ModelAttribute @Valid AcademyRegistrationForm form,
        BindingResult result, Model model, RedirectAttributes redirectAttributes) {
    validator.validate(form, result);

    if (site.isUseStates()
            && StringUtils.isBlank(form.getBooker().getState())) {
        result.rejectValue("booker.state",
                "gui.page.academy.attendee.state");
    }

    if (result.hasErrors()) {
        LOG.debug("Form has errors: {}", result.getAllErrors());
        return "common/academy-registration";
    }

    // Form is valid when no errors are present. Complete the registration.
    AcademyRegistration registration = form.toAcademyRegistration();
    academyService.performRegistration(registration, site);

    redirectAttributes.addFlashAttribute(form);

    String redirectUrl = "redirect:/public/academy/register/"
        + registration.getSchedule().getCourse().getContent().getSeoNavTitle() 
        + "-completed";

    return redirectUrl;
}

@RequestMapping(value="/public/academy/register/**-completed", method=RequestMethod.GET)
public String displayRegistrationSuccess(@ModelAttribute("academyRegistrationForm") final AcademyRegistrationForm form)
{
    SortedSet<Vendor> dummy = form.getSchedule().getCourse().getContent().getVendors();
    return "common/academy-registration-success";
}

Here's the exception:

Oct 2, 2013 2:11:31 PM org.apache.catalina.core.ApplicationDispatcher invoke
SEVERE: Servlet.service() for servlet jsp threw exception
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.horn.cms.domain.Content.vendors, could not initialize proxy - no Session
Was it helpful?

Solution

Here's what I assume happens (until you update with the details):

AcademyRegistration registration = form.toAcademyRegistration();
academyService.performRegistration(registration, site);

does some Hibernate queries and retrieves some persisten entities lazily, ie. they haven't been initialized. The loading that did happen probably occurred in some Hibernate Session (do you have a @Transactional somewhere?). The Session is closed and dis-associated from the lazily loaded object.

You then add the form object, which has some nested reference to the lazily loaded entity (it'll be a hibernate proxy), to the RedirectAttributes. This in itself is not a problem because all you're doing is passing a reference.

The request handling completes by sending a 302 response. Your client will then make the new request that is handled by displayRegistrationSuccess() and hits this line

SortedSet<Vendor> dummy = form.getSchedule().getCourse().getContent().getVendors();

Here, the form object is the same as was added in the previous request. One of the objects in this reference chain is your Hibernate proxy that was lazily initialized. Because the object is no longer associated with a Session, Hibernate complains and you get the exception you get.

It's not a good idea to pass around (across request boundaries) objects that depend on persistent state. Instead, you should pass around an ID that you use to retrieve the entity. The alternative is to fully initialize your object inside your academyService method.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top