Spring MVC 3: return a Spring-Data Page as JSON - issue with PagedResourcesAssembler -
so, i'm new spring , java in general
what try have on same rendered view form post data filter result list displayed under form.
i have simple domain class follows:
@entity @table(name = "sec_person") public class person { @id @generatedvalue(strategy = generationtype.sequence, generator = "seq_sec_person") @sequencegenerator(name = "seq_sec_person", sequencename = "seq_sec_person") @column(name = "id") private long id; @column(name = "code", nullable = false) private string code; @column(name = "firstname", nullable = false) private string firstname; @column(name = "surname", nullable = false) private string surname; @column(name = "creationdate") private datetime creationdate; //getters , setters
a dto because want domain decoupled presentation
public class persondto { private long id; @notempty private string code; @notempty private string firstname; @notempty private string surname; private datetime creationdate; public persondto() { } //getters , setters
a repository extending jpa , querydsl
public interface personrepository extends jparepository<person, long>, querydslpredicateexecutor<person> {}
a data access class search null safe (thanks guava) , equivalent not null safe
the person criteria:
public class personcriteria { private string code; private string surname; private string firstname; private localdate creationdatefrom; private localdate creationdateto; //getters , setters }
the null safe version
public class nullsafepersoncriteria { private final personcriteria personcriteria; public nullsafepersoncriteria(final personcriteria personcriteria) { checkargument(personcriteria != null); this.personcriteria = personcriteria; } public optional<string> getcode() { return optional.fromnullable(this.personcriteria.getcode()); } public optional<string> getsurname() { return optional.fromnullable(this.personcriteria.getsurname()); } public optional<string> getfirstname() { return optional.fromnullable(this.personcriteria.getfirstname()); } public optional<localdate> getcreationdatefrom() { return optional.fromnullable(this.personcriteria.getcreationdatefrom()); } public optional<localdate> getcreationdateto() { return optional.fromnullable(this.personcriteria.getcreationdateto()); }
my predicate search
public class personpredicates { public static predicate personlitstquery(final personcriteria personcriteria) { final qperson person = qperson.person; final nullsafepersoncriteria nspersoncriteria = new nullsafepersoncriteria(personcriteria); booleanexpression criteria = qperson.person.isnotnull(); if (nspersoncriteria.getcode().ispresent()) { criteria = criteria.and(person.code.matches(nspersoncriteria.getcode().get())); } if (nspersoncriteria.getsurname().ispresent()) { criteria.and(person.surname.startswithignorecase(nspersoncriteria.getsurname().get())); } if (nspersoncriteria.getfirstname().ispresent()) { criteria.and(person.firstname.startswithignorecase(nspersoncriteria.getfirstname().get())); } if ((nspersoncriteria.getcreationdatefrom().ispresent()) && (nspersoncriteria.getcreationdateto().ispresent())) { criteria.and(person.creationdate.between(nspersoncriteria.getcreationdatefrom().get().todatetimeatstartofday(), nspersoncriteria.getcreationdateto().get().todatetimeatstartofday())); } return criteria; }
my service implementation follows:
@service public class personserviceimpl implements personservice{ @transactional(readonly = true) @override public page<persondto> search(final personcriteria criteria, final int pageindex) { logger.debug("searching person set of criterias"); return new personpage(this.mapper.map(lists.newarraylist(this.personrepository.findall(personlitstquery(criteria))), persondto.class), constructpagespecification(pageindex), count(criteria)); }
the mapper use extending bit dozermapper:
public class dozermapper{ private final org.dozer.mapper mapper; public dozermapper(final org.dozer.mapper mapper) { this.mapper = mapper; } @override public <t> t map(final object source, final class<t> destinationclass) { return this.mapper.map(source, destinationclass); } @override public <t> list<t> map(final list<?> sources, final class<t> destinationclass) { final list<t> result = lists.newarraylist(); (final object source : sources) { result.add(map(source, destinationclass)); } return result; }
now, of above works, fine unit tested , returns results want. problem controller , views....
i have read oliver's answer question: spring mvc 3: return spring-data page json
though reason cannot make work. i've added following dependencies project use hateoas , spring-data-commons:
<dependency> <groupid>org.springframework.hateoas</groupid> <artifactid>spring-hateoas</artifactid> <version>0.7.0.release</version> </dependency> <dependency> <groupid>org.springframework.data</groupid> <artifactid>spring-data-commons</artifactid> <version>1.6.0.release</version> </dependency>
and controller looks this:
@controller @sessionattributes("person") public class personcontroller @requestmapping(value = request_mapping_list, method = requestmethod.get) public httpentity<pagedresources> persons(final model model, @modelattribute final personcriteria searchcriteria, final pageable pageable, final pagedresourcesassembler assembler) { model.addattribute(model_attribute_searchcriteria, searchcriteria); final page<persondto> persons = this.personservice.search(searchcriteria, searchcriteria.getpageindex()); return new responseentity<>(assembler.toresource(persons), httpstatus.ok); }
and jsp:
<html> <head> <title>testing</title> <script src="jslinks jqgrid , jquery" type="text/javascript"></script> </head> <body> <form:form action="person" commandname="searchcriteria" method="post"> <div> <form:label path="code">code: </form:label> <form:input path="code" type="text"/> <form:label path="surname">surname: </form:label> <form:input path="surname" type="text"/> <form:label path="firstname">firstname: </form:label> <form:input path="firstname" type="text"/> <form:label path="creationdatefrom">creation date from: </form:label> <smj:datepicker id="creationdatefrom" name="creationdatefrom" /> <form:label path="creationdateto">creation date to: </form:label> <smj:datepicker id="creationdateto" name="creationdateto" /> </div> <div> <input type="submit" value="search"/> </div> </form:form> <smjg:grid gridmodel="gridmodel" id="persons" datatype="\"json\"" url="\'person\'" jsonreader="{root:\"content\", repeatitems: false, records: \"numberofelements\", total: \"totalpages\"}"> <smjg:gridcolumn name="code" /> <smjg:gridcolumn name="surname" align="left"/> <smjg:gridcolumn name="firstname" align="left"/> </smjg:grid> </body> </html>
explanation: smj , smjg tags taglibs i'm working on linking jquery spring mvc. ex: smjg:grid create tag , javascript call jqgrid function.
the first difference olivier's answer post spring mvc 3: return spring-data page json if infer persondto within httpentity following compilation error:
type mismatch: cannot convert responseentity<pagedresources> httpentity<pagedresources<persondto>>
the second difference seems should infer persondto pagedresourcesassembler, correct?
the outcome when call url directly localhost:8081/app/person http 500 error:
org.springframework.http.converter.httpmessagenotwritableexception: not marshal [pagedresource { content: [resource { content: com.app.admin.service.persondto@60a349d0[id=2050,code=test2,firstname=chadsdatest,surname=francois,creationdate=<null>], links: [] }, resource { content: com.app.admin.service.persondto@48462da5[id=5050,code=testnew,firstname=francois,surname=asdasdx,creationdate=<null>], links: [] }, resource { content: com.app.admin.crediadmin.service.persondto@5458c9fc[id=51,code=test,firstname=francois,surname=asdawdsx,creationdate=<null>], links: [] }, resource { content: com.app.admin.service.persondto@de47c70[id=2051,code=test3,firstname=chaqweqasdamsh,surname=frasda,creationdate=<null>], links: [] }, resource { content: com.app.admin.service.persondto@7bd2085d[id=3053,code=test7,firstname=francois,surname=cadsdsx,creationdate=<null>], links: [] }, resource { content: com.app.admin.service.persondto@14676697[id=3050,code=tester,firstname=francois,surname=casdadsixchaix,creationdate=<null>], links: [] }, resource { content: com.app.admin.service.persondto@109de504[id=3051,code=tester3,firstname=francoisf,surname=chtest,creationdate=<null>], links: [] }], metadata: metadata { number: 0, total pages: 2, total elements: 7, size: 5 }, links: [<http://localhost:8081/app/person?page=1&size=5&sort=surname,asc>;rel="next"] }]: null; nested exception javax.xml.bind.marshalexception - linked exception: [com.sun.istack.saxexception2: unable marshal type "org.springframework.hateoas.resource" element because not known context.] org.springframework.http.converter.xml.jaxb2rootelementhttpmessageconverter.writetoresult(jaxb2rootelementhttpmessageconverter.java:99) org.springframework.http.converter.xml.abstractxmlhttpmessageconverter.writeinternal(abstractxmlhttpmessageconverter.java:66) org.springframework.http.converter.abstracthttpmessageconverter.write(abstracthttpmessageconverter.java:179) org.springframework.web.servlet.mvc.method.annotation.abstractmessageconvertermethodprocessor.writewithmessageconverters(abstractmessageconvertermethodprocessor.java:148) org.springframework.web.servlet.mvc.method.annotation.httpentitymethodprocessor.handlereturnvalue(httpentitymethodprocessor.java:124) org.springframework.web.method.support.handlermethodreturnvaluehandlercomposite.handlereturnvalue(handlermethodreturnvaluehandlercomposite.java:69) org.springframework.web.servlet.mvc.method.annotation.servletinvocablehandlermethod.invokeandhandle(servletinvocablehandlermethod.java:122)
and root cause:
javax.xml.bind.marshalexception - linked exception: [com.sun.istack.saxexception2: unable marshal type "org.springframework.hateoas.resource" element because not known context.] com.sun.xml.bind.v2.runtime.marshallerimpl.write(marshallerimpl.java:318) com.sun.xml.bind.v2.runtime.marshallerimpl.marshal(marshallerimpl.java:244)
i'm not sure of wrong here.
althoug if call same url .json json output seems weird don't produce json still.
you've solved now, since have working thought i'd add solution @ least 1 of problems else in similar boat.
type mismatch: cannot convert responseentity<pagedresources> httpentity<pagedresources<persondto>>
:
to solve this, add additional type parameter return type:
@requestmapping(value = request_mapping_list, method = requestmethod.get) public httpentity<pagedresources<persondto>> persons(final model model, @modelattribute final personcriteria searchcriteria, final pageable pageable, final pagedresourcesassembler assembler) { ... }
com.sun.istack.saxexception2: unable marshal type "org.springframework.hateoas.resource" element because not known context.]
it looks spring trying produce xml, think default if finds jaxb implementation on classpath. if don't need produce xml method, add produces = {mediatype.application_json_value}
@requestmapping.
Comments
Post a Comment