2013-08-07 23 views
2
问题

所以,我是相当新的春天和一般Spring MVC的3:返回一个Spring的数据页的JSON - 与PagedResourcesAssembler

到Java我尝试做的是对同一渲染视图发布数据以过滤表单下显示的结果列表的表单。

我有一个简单的域类如下:

@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 and setters 

DTO的,因为我想我的域名从我的演讲

public class PersonDTO { 
private Long id; 

@NotEmpty 
private String code; 

@NotEmpty 
private String firstname; 

@NotEmpty 
private String surname; 

private DateTime creationDate; 

public PersonDTO() { 
} 
//getters and setters 

延长JPA和QueryDsl

public interface PersonRepository extends JpaRepository<Person, Long>, QueryDslPredicateExecutor<Person> {} 
的仓库去耦

我的搜索数据访问类是无效安全的(感谢guava)及其等效空安全

的人标准:

public class PersonCriteria { 

private String code; 
private String surname; 
private String firstname; 
private LocalDate creationDateFrom; 
private LocalDate creationDateTo; 

//getters and setters 
} 

空安全版本

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()); 
} 

我断言搜索

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; 
} 

我的服务实现如下:

@Service 
public class PersonServiceImpl implements PersonService{ 
@Transactional(readOnly = true) 
@Override 
public Page<PersonDTO> search(final PersonCriteria criteria, final int pageIndex) { 
    LOGGER.debug("Searching person with set of criterias"); 

    return new PersonPage(this.mapper.map(Lists.newArrayList(this.personRepository.findAll(PersonLitstQuery(criteria))), 
      PersonDTO.class), constructPageSpecification(pageIndex), count(criteria)); 
} 

,我使用的是刚刚延长了一下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(); 
    for (final Object source : sources) { 
     result.add(map(source, destinationClass)); 
    } 
    return result; 
} 

现在,以上所有作品,罚款是单元测试,并返回我想要的结果。我的问题是与控制器和视图....

我仔细看过奥利弗的回答这个问题:Spring MVC 3: return a Spring-Data Page as JSON

但出于某种原因,我不能使它发挥作用。我已经添加了以下依存关系到我的项目使用HATEOAS和Spring数据公地:

<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> 

和我的控制器看起来像这样:

@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); 
} 

和我的jsp:

<html> 
<head> 
    <title>testing</title> 
    <script src="jslinks for jqGrid and 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> 

说明: smj和smjg标签是我正在处理的taglibs,它们将jquery链接到spring mvc。例如:smjg:grid将创建标签和将调用jqgrid函数的javascript。

从这个帖子Spring MVC 3: return a Spring-Data Page as JSON Olivier的答案的第一个区别是,如果我将HttpEntity内推断PersonDTO然后我得到以下编译错误:

Type mismatch: cannot convert from ResponseEntity<PagedResources> to HttpEntity<PagedResources<PersonDTO>> 

的第二个区别是,它似乎我应该推断我PersonDTO放入PagedResourcesAssembler中,是否正确?

结果当我打电话的URL直接本地主机:8081 /应用/人,我得到一个HTTP 500错误:

org.springframework.http.converter.HttpMessageNotWritableException: Could not marshal [PagedResource { content: [Resource { content: [email protected][id=2050,code=TEST2,firstname=ChadsdaTest,surname=Francois,creationDate=<null>], links: [] }, Resource { content: [email protected][id=5050,code=TESTNEW,firstname=Francois,surname=asdasdx,creationDate=<null>], links: [] }, Resource { content: [email protected][id=51,code=TEST,firstname=Francois,surname=asdawdsx,creationDate=<null>], links: [] }, Resource { content: [email protected][id=2051,code=TEST3,firstname=Chaqweqasdamsh,surname=Frasda,creationDate=<null>], links: [] }, Resource { content: [email protected][id=3053,code=TEST7,firstname=Francois,surname=Cadsdsx,creationDate=<null>], links: [] }, Resource { content: [email protected][id=3050,code=TESTER,firstname=Francois,surname=CasdadsixChaix,creationDate=<null>], links: [] }, Resource { content: [email protected][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 is javax.xml.bind.MarshalException 
- with linked exception: 
[com.sun.istack.SAXException2: unable to marshal type "org.springframework.hateoas.Resource" as an element because it is not known to this 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) 

和根源:

javax.xml.bind.MarshalException 
- with linked exception: [com.sun.istack.SAXException2: unable to marshal type "org.springframework.hateoas.Resource" as an element because it is not known to this context.] 
com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:318) 
com.sun.xml.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:244) 

我不知道我在这里做错了什么。

Althoug如果我用.json调用相同的url,那么我得到的json输出看起来很奇怪,因为我不生成json静音。

回答

10

你现在可能已经解决了这个问题,但是由于我有这个工作,我以为我会将解决方案添加到至少其中一个问题的解决方案中。

Type mismatch: cannot convert from ResponseEntity<PagedResources> to HttpEntity<PagedResources<PersonDTO>>

为了解决这个问题,一个额外的类型参数添加到您的返回类型:

@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 to marshal type "org.springframework.hateoas.Resource" as an element because it is not known to this context.]

它看起来像春天正试图产生XML,这我认为它在默认情况下会在类路径中找到JAXB实现。如果您不需要使用此方法生成XML,则可以将produces = {MediaType.APPLICATION_JSON_VALUE}添加到其@RequestMapping中。