I'm the following error when calling purchaseService.updatePurchase(purchase) inside my TagController:
SEVERE: Servlet.service() for servlet [PurchaseAPIServer] in context with path [/PurchaseAPIServer] threw exception [Request processing failed; nested exception is org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.app.model.Purchase.tags, no session or session was closed] with root cause
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.app.model.Purchase.tags, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383)
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:375)
at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:368)
at org.hibernate.collection.PersistentSet.add(PersistentSet.java:212)
at com.app.model.Purchase.addTags(Purchase.java:207)
at com.app.controller.TagController.createAll(TagController.java:79)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:212)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:900)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:827)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:641)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:225)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:169)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:999)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:565)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:309)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
TagController:
#RequestMapping(value = "purchases/{purchaseId}/tags", method = RequestMethod.POST, params = "manyTags")
#ResponseStatus(HttpStatus.CREATED)
public void createAll(#PathVariable("purchaseId") final Long purchaseId, #RequestBody final Tag[] entities)
{
Purchase purchase = purchaseService.getById(purchaseId);
Set<Tag> tags = new HashSet<Tag>(Arrays.asList(entities));
purchase.addTags(tags);
purchaseService.updatePurchase(purchase);
}
Purchase:
#Entity
#XmlRootElement
public class Purchase implements Serializable
{
/**
*
*/
private static final long serialVersionUID = 6603477834338392140L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(mappedBy = "purchase", fetch = FetchType.LAZY, cascade={CascadeType.ALL})
private Set<Tag> tags;
#JsonIgnore
public Set<Tag> getTags()
{
if (tags == null)
{
tags = new LinkedHashSet<Tag>();
}
return tags;
}
public void setTags(Set<Tag> tags)
{
this.tags = tags;
}
public void addTag(Tag tag)
{
tag.setPurchase(this);
this.tags.add(tag);
}
public void addTags(Set<Tag> tags)
{
Iterator<Tag> it = tags.iterator();
while (it.hasNext())
{
Tag tag = it.next();
tag.setPurchase(this);
this.tags.add(tag);
}
}
...
}
Tag:
#Entity
#XmlRootElement
public class Tag implements Serializable
{
/**
*
*/
private static final long serialVersionUID = 5165922776051697002L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({#JoinColumn(name = "PURCHASEID", referencedColumnName = "ID")})
private Purchase purchase;
#JsonIgnore
public Purchase getPurchase()
{
return purchase;
}
public void setPurchase(Purchase purchase)
{
this.purchase = purchase;
}
}
PurchaseService:
#Service
public class PurchaseService implements IPurchaseService
{
#Autowired
private IPurchaseDAO purchaseDAO;
public PurchaseService()
{
}
#Transactional
public List<Purchase> getAll()
{
return purchaseDAO.findAll();
}
#Transactional
public Purchase getById(Long id)
{
return purchaseDAO.findOne(id);
}
#Transactional
public void addPurchase(Purchase purchase)
{
purchaseDAO.save(purchase);
}
#Transactional
public void updatePurchase(Purchase purchase)
{
purchaseDAO.update(purchase);
}
}
TagService:
#Service
public class TagService implements ITagService
{
#Autowired
private ITagDAO tagDAO;
public TagService()
{
}
#Transactional
public List<Tag> getAll()
{
return tagDAO.findAll();
}
#Transactional
public Tag getById(Long id)
{
return tagDAO.findOne(id);
}
#Transactional
public void addTag(Tag tag)
{
tagDAO.save(tag);
}
#Transactional
public void updateTag(Tag tag)
{
tagDAO.update(tag);
}
}
Any ideas on how I can fix this? (I want to avoid using EAGER loading).
Do I need to setup some form of session management for transactions?
Thanks
Updates based on JB suggestions
TagController
#RequestMapping(value = "purchases/{purchaseId}/tags", method = RequestMethod.POST, params = "manyTags")
#ResponseStatus(HttpStatus.CREATED)
public void createAll(#PathVariable("purchaseId") final Long purchaseId, #RequestBody final Tag[] entities)
{
Purchase purchase = purchaseService.getById(purchaseId);
// Validation
RestPreconditions.checkRequestElementNotNull(purchase);
RestPreconditions.checkRequestElementIsNumeric(purchaseId);
Set<Tag> tags = new HashSet<Tag>(Arrays.asList(entities));
purchaseService.addTagsToPurchase(purchaseId, tags);
}
PurchaseService
#Transactional
public void addTagsToPurchase(Long purchaseId, Set<Tag> tags)
{
Purchase p = purchaseDAO.findOne(purchaseId);
p.addTags(tags);
}
You load the Purchase without initializing its tags collection and then, in your controller, once the transaction is committed and the session closed, you're adding a tag to this non-initialized collection. So obviously, this exception is thrown.
You have two possibilities:
Load the tags with the Purchase, either by calling Hibernate.initialize(purchase.getTags()), or by executing a JPQL query that loads the tag along with its tags, in a single query (select distinct p from Purchase p left join fetch p.tags).
Instead of having a service class that offers noting more than the DAO, add a real service method addTagsToPurchase(Long purchaseId, Set<Tag> tags) which reloads the purchase and adds the tags to this purchase. This is in fact the service method needed by your controller.
I of course prefer the second solution by far:
it shows why a service layer is useful,
it removes business code from the presentation layer to put it in the service layer,
it's more efficient
it's safer because it uses attached entities, and thus avoids the kind of exception you're having.
In general, if your controller, to execute a single atomic action, needs several calls to the service layer, it means that the service layer doesn't offer the needed functionality, and that the service is implemented in the presentation layer instead.
Related
I am implementing a spring boot service with an underlying mongodb database for data storage.
In this service I have to use some domain classes that comes from another library which I can not alter. Unfortunately these classes have instance variables marked as final. For example here is the Discount domain class:
#Value
#NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
#AllArgsConstructor(access = AccessLevel.PRIVATE)
#SuperBuilder(toBuilder = true)
#JsonDeserialize(builder = Discount.DiscountBuilderImpl.class)
#EqualsAndHashCode(callSuper = true)
#ToString(callSuper = true)
public class Discount extends AbstractEntityBase {
#NonNull
UUID id;
#NonNull
String no;
#JsonProperty("iNo")
Integer iNo;
#NonNull
LocalizedTexts designation;
LocalizedTexts printText;
boolean isAutomatic;
LocalDate validFromDate;
LocalDate validToDate;
LocalTime validFromTime;
LocalTime validToTime;
String checkScriptParameters;
ReferenceScript checkScript;
#NonNull
ReferenceScript calculationScript;
public static DiscountBuilder<?, ?> builder(UUID id,
String no,
LocalizedTexts designation,
ReferenceScript calculationScript,
boolean isAutomatic) {
return new DiscountBuilderImpl()
.id(id)
.no(no)
.designation(designation)
.calculationScript(calculationScript)
.isAutomatic(isAutomatic);
}
/**
* Overwritten getters for optional properties
*/
...
#JsonPOJOBuilder(withPrefix = "")
#JsonIgnoreProperties(ignoreUnknown = true)
public static final class DiscountBuilderImpl
extends DiscountBuilder<Discount, DiscountBuilderImpl> {
}
}
In my test class I am testing save and find (findAll, findById) operations. Save operations work fine but I have issues with findAll and findById methods. Here is such a test class:
#RunWith(SpringRunner.class)
#DataMongoTest
public class TestDiscountRepository {
#Autowired
DiscountRepository discountRepository;
#Before
public void init(){
}
#After
public void resetMongoDb() {
//discountRepository.deleteAll();
}
#Test
public void save_success() {
UUID id = UUID.randomUUID();
...
Discount discount = Discount.builder(id, no, designation, calculationScript, true)
.iNo(iNo)
.printText(printText)
.isAutomatic(isAutomatic)
.validFromDate(validFromDate)
.validToDate(validToDate)
.validFromTime(validFromTime)
.validToTime(validToTime)
.checkScriptParameters(checkScriptParameters)
.checkScript(checkScript)
.build();
Discount savedDiscount = discountRepository.save(discount);
assertThat(savedDiscount).isNotNull();
assertThat(savedDiscount.getId()).isNotNull();
}
#Test
public void save_bulk_success() {
UUID id1 = UUID.randomUUID();
...
UUID id2 = UUID.randomUUID();
...
UUID id3 = UUID.randomUUID();
...
List<Discount> discounts = Arrays.asList(
Discount.builder(id1, no1, designation1, calculationScript1, true)
...
.build(),
Discount.builder(id2, no2, designation2, calculationScript2, true)
...
.build(),
Discount.builder(id3, no3, designation3, calculationScript3, true)
...
.build()
);
List<Discount> allDiscounts = discountRepository.saveAll(discounts);
AtomicInteger validIdFound = new AtomicInteger();
allDiscounts.forEach(discount -> {
if(discount.getId() != null) {
validIdFound.getAndIncrement();
}
});
assertThat(validIdFound.intValue()).isEqualTo(3);
}
#Test
public void find_all_success() {
List<Discount> discounts = discountRepository.findAll();
Assert.assertNotNull(discounts);
}
#Test
public void find_by_id_success() {
UUID id = UUID.randomUUID();
String no = "900";
...
Discount discount = Discount.builder(id, no, designation, calculationScript, true)
...
.build();
Discount savedDiscount = discountRepository.save(discount);
Optional<Discount> result = discountRepository.findById(savedDiscount.getId());
Assert.assertNotNull(result.get());
}
}
And here is the exception I am getting when I try to run/test findAll() and findById():
java.lang.IllegalStateException: Cannot set property id because no setter, no wither and it's not part of the persistence constructor private net.mspos.possible.svc_pos_controller_data.entity.Discount()!
at org.springframework.data.mapping.model.InstantiationAwarePropertyAccessor.setProperty(InstantiationAwarePropertyAccessor.java:118)
at org.springframework.data.mapping.model.ConvertingPropertyAccessor.setProperty(ConvertingPropertyAccessor.java:64)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readAndPopulateIdentifier(MappingMongoConverter.java:450)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.populateProperties(MappingMongoConverter.java:418)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:394)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readDocument(MappingMongoConverter.java:356)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:292)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:288)
at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:107)
at org.springframework.data.mongodb.core.MongoTemplate$ReadDocumentCallback.doWith(MongoTemplate.java:3207)
at org.springframework.data.mongodb.core.MongoTemplate.executeFindOneInternal(MongoTemplate.java:2822)
at org.springframework.data.mongodb.core.MongoTemplate.doFindOne(MongoTemplate.java:2529)
at org.springframework.data.mongodb.core.MongoTemplate.doFindOne(MongoTemplate.java:2499)
at org.springframework.data.mongodb.core.MongoTemplate.findById(MongoTemplate.java:888)
at org.springframework.data.mongodb.repository.support.SimpleMongoRepository.findById(SimpleMongoRepository.java:132)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.data.repository.core.support.RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0(RepositoryMethodInvoker.java:289)
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:137)
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:121)
at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:529)
at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:285)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:639)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:163)
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:138)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:80)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215)
at jdk.proxy2/jdk.proxy2.$Proxy93.findById(Unknown Source)
at net.mspos.possible.svc_pos_controller_data.repository.TestDiscountRepository.find_by_id_success(TestDiscountRepository.java:209)
Here is also how a document into the database looks like:
Any ideas on how I could overcome this issue? Unfortunately altering the domain classes is not an option.
Finally the easiest and more convenient solution in my case is to avoid using repositories to map the domain classes and provide basic CRUD operations.
Instead of that I am using the MongoCollection interface alongside with ObjectMapper to serialize/deserialize objects into mongodb documents for all CRUD operations. For instance a relevant service for a domain class could look like:
#Service
#EnableConfigurationProperties(MongoProperties.class)
public class DiscountMongoService implements BasicMongoService {
private MongoProperties mongoProperties;
private CustomMongoConfig customMongoConfig;
private BusinessEntityMapper mapper;
private MongoCollection<Document> discountCollection;
#Autowired
public DiscountMongoService(MongoProperties mongoProperties, CustomMongoConfig customMongoConfig, BusinessEntityMapper mapper) {
this.mongoProperties = mongoProperties;
this.customMongoConfig = customMongoConfig;
this.mapper = mapper;
discountCollection =
this.customMongoConfig.mongoClient().getDatabase(this.mongoProperties.getDatabase()).getCollection(Discount.class.getSimpleName());
}
// Insert
public boolean insertOne(Discount discount) throws JsonProcessingException {
Document discountDoc = transformEntityIntoDocumentWithMongodbId(discount);
InsertOneResult result = discountCollection.insertOne(discountDoc);
System.out.println(result);
return result.wasAcknowledged();
}
....
// Find
public List<Discount> findAll() {
List<Discount> discounts = new ArrayList<>();
discountCollection.find().forEach(dd -> {
Discount discount;
try {
discount = (Discount) mapper.getBusinessEntityFromJSON(dd.toJson(),Discount.class);
discounts.add(discount);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
});
return discounts;
}
I am having below mappings as Company having 1:N relation with CompanyFunds
#Entity
public class Company{
#Id
private Integer companyId;
private String name;
#OneToMany(mappedBy = "company")
private List<CompanyFund> companyFunds;
}
#Entity
public class CompanyFunds{
#Id
private Integer fundId;
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "company_id")
private Company company;
}
I am using Spring-data-Jpa for my persistence layer and below are the controller and service methods:
//controller
#GetMapping(value = "/{companyId}")
public Resource<Company> find(#PathVariable Integer companyId) {
Resource<Company> companyResource = companyService.find(companyId);
return companyResource;
}
//service
public Resource<CompanyTypeOther> find(Integer companyId) {
Company company = companyRepository.findById(companyId);
return restResourceAssembler.toResource(company);
}
#Component
public class RestResourceAssembler implements ResourceAssembler<T, Resource<T>> {
private EntityLinks entityLinks;
public RestResourceAssembler(EntityLinks entityLinks) {
this.entityLinks = entityLinks;
}
#Override
public Resource<T> toResource(T entity) {
Resource<T> resource = new Resource<>(entity);
resource.add(entityLinks.linkToSingleResource(entity.getClass(), entity.getId()).withSelfRel());
return resource;
}
}
Now the weird thing is, until the return companyResource;(in controller) doesn't get executed, the companyResource contains null for companyFunds i.e the LAZY loading is working fine till that point. But the moment the return companyResource; executes, something goes inside the Spring and the Select statement for CompanyFund gets fired. I debugged the steps and below is the code(try block) responsible for this:
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
......
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
........other code
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
.....
}
There are no toString() declared in entities, also no getCompanyFund() called. Can't understand what Spring is doing with returnValue above, so that some getters(or something) are getting called.
One more thing I noticed is, this problem only occurs when the Resource<Company> is returned. If I return Company from the controller, nothing unexpected happens. Lazy loading works fine.
Since I want to lazy load the entity, a little fix/hack solved the problem for me(as of now).
#JsonIgnore
#OneToMany(mappedBy = "company")
private List<CompanyFund> companyFunds;
#JsonIgnore prevents LAZY loaded entity from being serialized. So I guess the jackson is the culprit here.
This is not a fix is my view but just a hack to do the thing. Still waiting someone from Spring team to reply.
I wanted to add a bean to model in Spring MVC Controller. But, validator exception is thrown:
Illegalstate exception.
Can anyone guide me to submit a form and display the content which I get after form submission? In this case, I need to use a bean to display all my information in view.
Like:
model.addAttribute("simple", new Student());
But, I am keep getting IllegaStateException from validator.
Download:
https://sites.google.com/site/jimjicky/SpringFormValidation.rar?attredirects=0&d=1
Controller:
#Controller
public class EmployeeController {
private static final Logger logger = LoggerFactory
.getLogger(EmployeeController.class);
private Map<Integer, Employee> emps = null;
#Autowired
#Qualifier("employeeValidator")
private Validator validator;
#InitBinder
private void initBinder(WebDataBinder binder) {
binder.setValidator(validator);
}
public EmployeeController() {
emps = new HashMap<Integer, Employee>();
}
#ModelAttribute("employee")
public Employee createEmployeeModel() {
// ModelAttribute value should be same as used in the empSave.jsp
return new Employee();
}
#ModelAttribute("student")
public Student createStudentModel() {
// ModelAttribute value should be same as used in the empSave.jsp
return new Student();
}
#RequestMapping(value = "/emp/save", method = RequestMethod.GET)
public String saveEmployeePage(Model model) {
logger.info("Returning empSave.jsp page");
return "empSave";
}
#RequestMapping(value = "/emp/save.do", method = RequestMethod.POST)
public String saveEmployeeAction(
#ModelAttribute("employee") #Validated Employee employee,#ModelAttribute("student")Student student,
BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors()) {
logger.info("Returning empSave.jsp page");
return "empSave";
}
logger.info("Returning empSaveSuccess.jsp page");
model.addAttribute("emp", employee);
model.addAttribute("student", createStudentModel());
emps.put(employee.getId(), employee);
return "empSaveSuccess";
}
}
Validator:
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import com.journaldev.spring.form.model.Employee;
public class EmployeeFormValidator implements Validator {
//which objects can be validated by this validator
#Override
public boolean supports(Class<?> paramClass) {
return Employee.class.equals(paramClass);
}
#Override
public void validate(Object obj, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "id", "id.required");
Employee emp = (Employee) obj;
if(emp.getId() <=0){
errors.rejectValue("id", "negativeValue", new Object[]{"'id'"}, "id can't be negative");
}
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "name.required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "role", "role.required");
}
}
Stack Trace
java.lang.IllegalStateException: Invalid target for Validator [com.journaldev.spring.form.validator.EmployeeFormValidator#1625cd8]: com.journaldev.spring.form.model.Student#bd8550
org.springframework.validation.DataBinder.assertValidators(DataBinder.java:495)
org.springframework.validation.DataBinder.setValidator(DataBinder.java:486)
com.journaldev.spring.form.controllers.EmployeeController.initBinder(EmployeeController.java:38)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:606)
org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
org.springframework.web.method.annotation.InitBinderDataBinderFactory.initBinder(InitBinderDataBinderFactory.java:62)
org.springframework.web.bind.support.DefaultDataBinderFactory.createBinder(DefaultDataBinderFactory.java:53)
org.springframework.web.method.annotation.ModelFactory.updateBindingResult(ModelFactory.java:222)
org.springframework.web.method.annotation.ModelFactory.updateModel(ModelFactory.java:206)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.getModelAndView(RequestMappingHandlerAdapter.java:852)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:755)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:690)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:945)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
Based on the information provided try changing your InitBinder to following:
#InitBinder("employee")
private void initBinder(WebDataBinder binder) {
binder.setValidator(validator);
}
The reason this works is because of the value provided, in this case the value being "employee".
public abstract String[] value
The names of command/form attributes and/or request parameters that this init-binder method is supposed to apply to.
In your form you have multiple objects, without advising the #InitBinder what object to validate it attempted to validate your Student object as well, thus failing as it didn't meet the class requirements.
By specifying "employee" it basically ensured it only applied the validation against the Employee object.
I have two #NodeEntities mapped via SDN using simple mapping, PersonNode and FamilyNode. FamilyNode has a #RelatedTo collection, children. I also have a FamilyService (using Spring's #Service annotation) with #Transactional annotation on an updateFamily method. This method loads the FamilyNode given an id, and uses a callback interface to modify the node. In one implementation of the callback, I am adding a PersonNode to the children collection, and this is generating the NotInTransactionException, specifically at the point that Neo4J is attempting to create the relationship between the FamilyNode and the PersonNode.
Source code can be found at github and in particular a failing test. Here are relevant bits of the code:
FamilyNode.java:
#NodeEntity
public class FamilyNode implements Family {
#Indexed(indexName = "families", unique = true)
private String id;
#GraphId
private Long identifier;
#RelatedTo(elementClass = PersonNode.class, type = "CHILD")
private Set<Person> children;
void addChild(Person child) {
if (this.children == null) {
this.children = new HashSet<>();
}
this.children.add(child);
}
}
PersonNode.java:
#NodeEntity
public class PersonNode implements Person {
#RelatedTo(elementClass = FamilyNode.class, type = "CHILD", direction = INCOMING)
private Family childOf;
#Indexed(indexName = "people", unique = true)
private String id;
#GraphId
private Long identifier;
}
FamilyRepository.java:
public interface FamilyRepository extends GraphRepository<Family> {
public FamilyNode findById(String id);
}
FamilyServiceImpl.java:
#Service
public class FamilyServiceImpl implements FamilyService {
#Autowired
private FamilyRepository families;
#Autowired
private Neo4jTemplate template;
#Override
public List<Family> getFamilies(String[] ids) {
List<Family> families = new ArrayList<>();
for (String id : ids) {
families.add(getFamily(id));
}
return families;
}
#Override
public Family getFamily(String id) {
return familyNode(id);
}
#Override
#Transactional
public Family createFamily(Family family) {
return lazyLoadRelationships((FamilyNode) this.families.save(family));
}
#Override
#Transactional
public Family updateFamily(String id, FamilyNodeUpdater updater) {
return this.families.save(updater.update(familyNode(id)));
}
private FamilyNode familyNode(String id) {
return lazyLoadRelationships(this.families.findById(id));
}
private FamilyNode lazyLoadRelationships(FamilyNode family) {
this.template.fetch(family.getFather());
this.template.fetch(family.getMother());
this.template.fetch(family.getChildren());
return family;
}
}
and the failing test, FamilyServiceTest.java:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = { TestConfig.class })
public class FamilyServiceTest {
#Configuration
#ComponentScan(basePackageClasses = { com.bonevich.ancestral.family.FamilyServiceImpl.class }, resourcePattern = "FamilyServiceImpl.class")
#EnableNeo4jRepositories(basePackageClasses = { com.bonevich.ancestral.family.FamilyNode.class })
static class TestConfig extends Neo4jConfiguration {
#Bean
public GraphDatabaseService graphDatabaseService() {
return new GraphDatabaseFactory().newEmbeddedDatabaseBuilder("/data/neo4j/ancestral-familyservicetest/")
.newGraphDatabase();
}
}
#Autowired
private FamilyService families;
#Autowired
private GraphDatabaseService graphDatabaseService;
#Autowired
private Neo4jTemplate neo4jTemplate;
#Test
public void testUpdateFamily() {
this.families.createFamily(FamilyNode.instance("testFamily"));
Transaction tx = this.graphDatabaseService.beginTx();
PersonNode person = PersonNode.instance("John", "Johanson", "M", "a_person");
PersonNode expectedChild = this.neo4jTemplate.save(person);
final long childId = expectedChild.getIdentifier();
tx.success();
tx.finish();
Family actualFamily = this.families.updateFamily("testFamily", new FamilyNodeUpdater() {
#Override
public FamilyNode update(FamilyNode family) {
family.addChild(FamilyServiceTest.this.neo4jTemplate.findOne(childId, PersonNode.class));
return family;
}
});
assertThat(actualFamily.getId(), is("testFamily"));
assertThat(actualFamily.getChildren().get(0), is((Person) expectedChild));
}
}
Running this test yields the following exception stack:
org.neo4j.graphdb.NotInTransactionException
at org.neo4j.kernel.impl.core.NoTransactionState.acquireWriteLock(NoTransactionState.java:43)
at org.neo4j.kernel.impl.transaction.LockType$2.acquire(LockType.java:51)
at org.neo4j.kernel.impl.core.NodeManager.getNodeForProxy(NodeManager.java:473)
at org.neo4j.kernel.InternalAbstractGraphDatabase$6.lookup(InternalAbstractGraphDatabase.java:733)
at org.neo4j.kernel.impl.core.NodeProxy.createRelationshipTo(NodeProxy.java:207)
at org.springframework.data.neo4j.fieldaccess.RelationshipHelper.obtainSingleRelationship(RelationshipHelper.java:62)
at org.springframework.data.neo4j.fieldaccess.RelationshipHelper.createSingleRelationship(RelationshipHelper.java:142)
at org.springframework.data.neo4j.fieldaccess.RelationshipHelper.createAddedRelationships(RelationshipHelper.java:96)
at org.springframework.data.neo4j.fieldaccess.RelatedToFieldAccessor.createAddedRelationships(RelatedToFieldAccessor.java:78)
at org.springframework.data.neo4j.fieldaccess.RelatedToCollectionFieldAccessorFactory$RelatedToCollectionFieldAccessor.setValue(RelatedToCollectionFieldAccessorFactory.java:68)
at org.springframework.data.neo4j.fieldaccess.ManagedFieldAccessorSet.updateValue(ManagedFieldAccessorSet.java:112)
at org.springframework.data.neo4j.fieldaccess.ManagedFieldAccessorSet.update(ManagedFieldAccessorSet.java:100)
at org.springframework.data.neo4j.fieldaccess.ManagedFieldAccessorSet.add(ManagedFieldAccessorSet.java:126)
at com.bonevich.ancestral.family.FamilyNode.addChild(FamilyNode.java:124)
at com.bonevich.ancestral.family.FamilyServiceTest$1.update(FamilyServiceTest.java:64)
at com.bonevich.ancestral.family.FamilyServiceImpl.updateFamily(FamilyServiceImpl.java:42)
at com.bonevich.ancestral.family.FamilyServiceTest.testUpdateFamily(FamilyServiceTest.java:61)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
I have got to believe I am missing something in configuring SDN or Transactions, but have been unable to track it down.
I recently answered this question here:
Spring Data Neo4J - NotInTransactionException while modifying set of nodeEntity
In short, it has to do with the fact that these List are special list which are backed by SDN and any modification is immediately persisted to the DB. If you want to prevent this behaviour, you should use Iterable in your model classes. If you have some more question after reading the link, just post them as a comment.
I'm developing a restful web service for my database. I'm using jpa to retriving data from the db and spring for the architecture. I already tested the architecture with the basic dao queries (findById, save...) and it works perfectly. Then in the dao implementation I added a new method wich basically execute a query that is tested directly on the mysql db (and it worked)
public List<PositionHistory> findByFavUserGBuser(Integer favoriteUserId,
Integer pageNumber, Integer rowsPerPage, String usernameFilter) {
String queryString="" +
"SELECT ph.* " +
"FROM user u, position_history ph, spot s " +
"WHERE " +
"ph.date IN (SELECT MAX(ph2.date) FROM position_history ph2 GROUP BY ph2.user_iduser) and " +
"s.id_spot=ph.spot_idspot and " +
"u.id_user=ph.user_iduser and ";
if (usernameFilter!=null)
queryString+="u.username like '%:usernameFilter%' and ";
queryString+="" +
"ph.user_iduser IN (SELECT ALL fu.user_iduserto FROM favorite_user fu WHERE fu.user_iduserfrom=:favoriteUserId) " +
"GROUP BY ph.user_iduser " +
"LIMIT :startLimit,:rowsPerPage";
Query query = entityManager.createNativeQuery(queryString,PositionHistory.class);
query.setParameter("favoriteUserId", favoriteUserId);
query.setParameter("startLimit", pageNumber*rowsPerPage);
query.setParameter("rowsPerPage", rowsPerPage);
if (usernameFilter!=null)
query.setParameter("usernameFilter", usernameFilter);
return query.getResultList();
}
and then I created a controller to retrive data as follow:
#Controller
#Transactional
public class MyController {
#Autowired
public DaoPositionHistory dph;
#Transactional
#RequestMapping(value = "/getData/{id}/", method = RequestMethod.POST)
#ResponseBody
public List<PositionHistory> home(#PathVariable int id) {
List<PositionHistory> resultlist=(List<PositionHistory>) dph.findByNearestPositionGBuser(id, 0, 10, null, null, null);
return resultlist;
}
}
but when i call the service i get the following error:
ERROR: org.hibernate.LazyInitializationException - failed to lazily initialize a collection of role: com.windy.spring.data.User.favoriteSports, no session or session was closed
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.windy.spring.data.User.favoriteSports, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383)
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:375)
at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:368)
at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:111)
at org.hibernate.collection.PersistentBag.iterator(PersistentBag.java:272)
at org.codehaus.jackson.map.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:45)
at org.codehaus.jackson.map.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:23)
at org.codehaus.jackson.map.ser.std.AsArraySerializerBase.serialize(AsArraySerializerBase.java:86)
at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:446)
at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:150)
at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:112)
at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:446)
at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:150)
at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:112)
at org.codehaus.jackson.map.ser.std.StdContainerSerializers$IndexedListSerializer.serializeContents(StdContainerSerializers.java:122)
at org.codehaus.jackson.map.ser.std.StdContainerSerializers$IndexedListSerializer.serializeContents(StdContainerSerializers.java:71)
at org.codehaus.jackson.map.ser.std.AsArraySerializerBase.serialize(AsArraySerializerBase.java:86)
at org.codehaus.jackson.map.ser.StdSerializerProvider._serializeValue(StdSerializerProvider.java:610)
at org.codehaus.jackson.map.ser.StdSerializerProvider.serializeValue(StdSerializerProvider.java:256)
at org.codehaus.jackson.map.ObjectMapper.writeValue(ObjectMapper.java:1613)
at org.springframework.http.converter.json.MappingJacksonHttpMessageConverter.writeInternal(MappingJacksonHttpMessageConverter.java:142)
at org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:179)
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:137)
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:81)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:94)
at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:73)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:900)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:827)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:859)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:602)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
at java.lang.Thread.run(Unknown Source)
I can not understand why I get this error if i declared the methos as #transactional? Any idea on how I can solve the problem?
Here is also my User class
#XmlRootElement
#Entity
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id_user")
private int idUser;
private String cellphone;
private String email;
#Lob()
private byte[] foto;
private String name;
private String notes;
private String password;
private String surname;
private String username;
//bi-directional many-to-one association to FavoriteSport
#OneToMany(mappedBy="user")
private List<FavoriteSport> favoriteSports;
//bi-directional many-to-one association to FavoriteSpot
#OneToMany(mappedBy="user")
private List<FavoriteSpot> favoriteSpots;
//bi-directional many-to-one association to FavoriteUser
#OneToMany(mappedBy="user2")
private List<FavoriteUser> favoriteUsers;
//uni-directional many-to-one association to Role
#ManyToOne
#JoinColumn(name="role_idrole")
private Role role;
//bi-directional many-to-one association to UserAccount
#OneToMany(mappedBy="user")
private List<UserAccount> userAccounts;
public User() {
}
public int getIdUser() {
return this.idUser;
}
public void setIdUser(int idUser) {
this.idUser = idUser;
}
public String getCellphone() {
return this.cellphone;
}
public void setCellphone(String cellphone) {
this.cellphone = cellphone;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
public byte[] getFoto() {
return this.foto;
}
public void setFoto(byte[] foto) {
this.foto = foto;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getNotes() {
return this.notes;
}
public void setNotes(String notes) {
this.notes = notes;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSurname() {
return this.surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public List<FavoriteSport> getFavoriteSports() {
return this.favoriteSports;
}
public void setFavoriteSports(List<FavoriteSport> favoriteSports) {
this.favoriteSports = favoriteSports;
}
public List<FavoriteSpot> getFavoriteSpots() {
return this.favoriteSpots;
}
public void setFavoriteSpots(List<FavoriteSpot> favoriteSpots) {
this.favoriteSpots = favoriteSpots;
}
public List<FavoriteUser> getFavoriteUsers() {
return this.favoriteUsers;
}
public void setFavoriteUsers(List<FavoriteUser> favoriteUsers) {
this.favoriteUsers = favoriteUsers;
}
public Role getRole() {
return this.role;
}
public void setRole(Role role) {
this.role = role;
}
public List<UserAccount> getUserAccounts() {
return this.userAccounts;
}
public void setUserAccounts(List<UserAccount> userAccounts) {
this.userAccounts = userAccounts;
}
}
I figured this out from reading the other answers to your question. Just in case anyone needs it here is an example:
#Override
public OriginServer get(Integer id){
OriginServer server = (OriginServer) sessionFactory
.getCurrentSession().get(OriginServer.class, id);
Hibernate.initialize(server.getPools());
return server;
}
In this example 'getPools' is a #OneToMany relationship from OriginServer.
The stacktrace clearly shows that the exception occurs after the controller method has completed (and the transaction closed).
So either use an extended persistence context (where sessions live longer than transactions), access the lazy collection before the controller method returns, modify your DAO or mapping to load the collection eagerly, or don't return an object containing that collection.
Your error is because you are not initializing entity associations in your DAO, and then the Jackson converter is trying to convert all associations in your return object to JSON. Since the associations are not initialized, the error is thrown since your Controller doesn't have a handle to the Hibernate Session.
See my answer here for how to correct: Spring #ResponseBody Json Cyclic Reference