如何将列表发布到Spring Data Rest?

时间:2015-07-13 02:33:59

标签: java spring rest spring-mvc spring-data-rest

我关注了this example,它允许发布一个唯一的Person对象。我想要一个REST服务,我可以在其中一次发布Person的集合,例如一个名为Team的列表/任何集合,只需一次调用即可拥有多个Person个对象。

我的意思是,我的问题并不完全是关于OneToMany关系,你在REST请求中发送每个人。本主题为well answered

我想发送一组Person个对象,利用@RepositoryRestResource或Spring Data Rest的其他功能。这可以通过Spring Data Rest实现,还是应该通过创建控制器来解决,接收列表并解析Team列表以插入每个Person

我找到this feature request,似乎回答说现在Spring Rest Data缺少我想要的东西,但我不确定。

在我的业务需求中,应用程序A会将一个订单列表发布到应用程序B,我必须将其保存在数据库中以供将来处理,因此,在阅读了Spring Data Rest并制作了一些示例后,我发现它的干净架构令人惊叹并且非常适合我的要求,除了我没有弄清楚如何发布一个列表。

5 个答案:

答案 0 :(得分:12)

嗯,AFAIK你不能用Spring数据休息那样做,只需阅读文档,你会看到,没有提到将列表发布到集合资源。

我不清楚这个原因,但一方面 - REST本身并没有真正指定你应该如何进行批处理操作。 所以不清楚应该如何处理该功能,比如你应该将列表发布到收集资源吗?或者您应该导出像/someentity/batch这样能够在一个批次中修补,删除和添加实体的资源吗?如果你要添加列表,你应该如何返回ID?对于单个POST到集合,spring-data-rest在Location头中返回id。对于批量添加,无法完成此操作。

这并不能证明spring-data-rest缺少批处理操作。他们应该实施这个恕我直言,但至少它可以帮助理解为什么他们可能会错过它。

我可以说,你可以随时将你自己的Controller添加到可以正确处理/ someentity / batch的项目中,你甚至可以从中创建一个库,这样你就可以在另一个项目中使用它。或者甚至fork spring-data-rest并添加此功能。虽然我试图了解它是如何工作的并且到目前为止失败了。 但是你可能知道这一切,对吧?

There is a feature request for this

答案 1 :(得分:4)

基于 user1685095 answer,您可以制作自定义控制器PersonRestController并公开collection of Person,因为Spring-date-rest < / p>

@RepositoryRestController
@RequestMapping(value = "/persons")
public class PersonRestController {
private final PersonRepository repo;
@Autowired
public AppointmentRestController(PersonRepository repo) {
    this.repo = repo;
}

@RequestMapping(method = RequestMethod.POST, value = "/batch", consumes = "application/json", produces = "application/json")
public @ResponseBody ResponseEntity<?> savePersonList(@RequestBody Resource<PersonWrapper<Person>> personWrapper,
        PersistentEntityResourceAssembler assembler) {
    Resources<Person> resources = new Resources<Person>(repo.save(personWrapper.getContent()));
    //TODO add extra links `assembler`
    return ResponseEntity.ok(resources);
}

}

  修复

PersonWrapper

     

无法在[来源:java.io.PushbackInputStream@3298b722;来源:START_ARRAY令牌\ n中反序列化org.springframework.hateoas.Resources的实例。 line:1,column:1]

<强>更新

public class PersonWrapper{
 private List<Person> content;

public List<Person> getContent(){
return content;
}

public void setContent(List<Person> content){
this.content = content;
}
}

public class Person{
private String name;
private String email;
// Other fields

// GETTER & SETTER 
}

答案 2 :(得分:1)

我尝试使用@RequestBody List<Resource<MyPojo>>。 当请求正文不包含任何链接时,它运行良好,但是 如果元素带有链接,则服务器无法反序列化请求正文。

然后我尝试使用@RequestBody Resources<MyPojo>,但我找不到列表的默认名称。

最后,我尝试了一个包含List<Resource<MyPojo>>的包装器,它可以工作。

这是我的解决方案:

首先为List<Resource<MyPojo>>创建一个包装类:

public class Bulk<T> {
    private List<Resource<T>> bulk;
    // getter and setter
}

然后使用@RequestBody Resource<Bulk<MyPojo>>作为参数。

最后,示例json带有用于在一个请求中创建批量数据的链接:

{
    "bulk": [
        {
            "title": "Spring in Action",
            "author": "http://localhost:8080/authors/1"
        },
        {
            "title": "Spring Quick Start",
            "author": "http://localhost:8080/authors/2"
        }
    ]
}

答案 3 :(得分:0)

@RequestMapping(method=RequestMethod.POST, value="/batchInsert", consumes = "application/json", produces = "application/json")
@ResponseBody
public ResponseEntity<?> batchInsert(@RequestBody Resources<Person> people, PersistentEntityResourceAssembler assembler) throws Exception {
    Iterable<Person> s = repo.save( people.getContent() ); // save entities

    List<PersistentEntityResource> list = new ArrayList<PersistentEntityResource>();
    Iterator<Sample> itr = s.iterator();
    while(itr.hasNext()) {
        list.add( assembler.toFullResource( itr.next() ) );
    }

    return ResponseEntity.ok( new Resources<PersistentEntityResource>(list) );
}

答案 4 :(得分:0)

基于totran的答案,这是我的代码:

有依赖:

springBootVersion = '2.4.2'
springDependencyManagement = '1.0.10.RELEASE'

implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-rest'
testImplementation 'org.springframework.boot:spring-boot-starter-test'

代码:

import icu.kyakya.rest.jpa.model.Address;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
@RepositoryRestResource(collectionResourceRel = "address", path = "address")
public interface AddressRepository extends PagingAndSortingRepository<Address, Long> {
//...
}
import lombok.Data;
import java.util.List;

@Data
public class Bulk<T> {
    private List<T> bulk;
}
import lombok.RequiredArgsConstructor;
import org.springframework.data.rest.webmvc.BasePathAwareController;
import org.springframework.data.rest.webmvc.RepositoryRestController;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.server.ExposesResourceFor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import java.util.List;

@BasePathAwareController  // if base url exists, it needs to be added
@RepositoryRestController
@RequiredArgsConstructor
@ExposesResourceFor(Address.class)
public class AddressController {

    private final AddressRepository repo;

    @PostMapping("/address/saveAll")
    public ResponseEntity<Iterable<Address>> saveAll(@RequestBody EntityModel<Bulk<Address>> bulk) {
        List<Address> addresses = Objects.requireNonNull(bulk.getContent()).getBulk();
        Iterable<Address> resp = repo.saveAll(addresses);
        return new ResponseEntity<>(resp,HttpStatus.CREATED);
    }
}

更像Spring data rest的方式:

import lombok.RequiredArgsConstructor;
import org.springframework.data.rest.webmvc.BasePathAwareController;
import org.springframework.data.rest.webmvc.RepositoryRestController;
import org.springframework.data.rest.webmvc.support.RepositoryEntityLinks;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.server.ExposesResourceFor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@BasePathAwareController  // if base url exists, it needs to be added
@RepositoryRestController
@RequiredArgsConstructor
@ExposesResourceFor(Address.class)
public class AddressController {

    private final AddressRepository repo;
    private final RepositoryEntityLinks entityLinks; //get link


    /**
     * curl -i -X POST -H "Content-Type:application/json" -d  '{ "bulk": [ {"country" : "Japan" , "city" : "Tokyo" }, {"country" : "Japan" , "city" : "Osaka" }]} '   http://localhost:8080/api/v1/address/saveAll
     *
     * @return 201 https://docs.spring.io/spring-data/rest/docs/current/reference/html/#repository-resources.default-status-codes
     */
    @PostMapping("/address/saveAll")
    public ResponseEntity<CollectionModel<EntityModel<Address>>>         List<Address> data = Objects.requireNonNull(bulk.getContent()).getBulk();
        Iterable<Address> addresses = repo.saveAll(data);

        ArrayList<EntityModel<Address>> models = new ArrayList<>();
        addresses.forEach(i->{
            Link link = entityLinks.linkToItemResource(Address.class, i.getId()).withRel("self");
            models.add(EntityModel.of(i).add(link));
        });
        return new ResponseEntity<>(CollectionModel.of(models),HttpStatus.CREATED);
    }
}