使用Spring Data JPA处理POST请求中的关系

时间:2018-09-26 14:01:37

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

我有以下实体,每个实体都有一个CrudRepository

@Entity
class Movie {
    @Id Long id;
    @Column String name;
    @ManyToOne Person director;
}

@Entity
class Person {
    @Id Long id;
    @Column String name;
}

我的控制器如下:

@RestController
@RequestMapping("/movies")
class MovieController {

    private MovieRepository movies = ...;
    private PersonRepository people = ...;

    @PostMapping
    public Movie create(@RequestBody MovieRequest request) {
        // Get the director
        Person director = people.findById(request.directorId);

        // Create the new movie
        Movie movie = new Movie();
        movie.name = request.name;
        movie.director = director;

       // Save the new movie
       return movies.save(movie);
    }
}

class MovieRequest {
    String name;
    Long directorId
}

如您所见,create方法首先通过其ID加载导演,然后创建新电影并最后保存它。这导致两次访问数据库:第一次是检索导演,第二次是保存电影。

在这种情况下,这不是什么大问题,但是可能存在一个具有很多关系的实体,这意味着可能要进行大量查询才能实现单个插入。

问题:我想将新电影保存在单一数据库操作中。有没有办法避免最初的人查询?有没有更好的方法来处理这种情况?

3 个答案:

答案 0 :(得分:1)

没有办法告诉Person所需要的代码与您的新Movie相关。因此,您确实需要执行查询并手动进行关联。

只有在端点创建Person的同时创建Movie的情况下,才有其他选择。然后,您可以简单地执行2个保存操作,或使用CascadeType=ALL进行单个保存操作。


如果您能够更改请求参数,则可以选择接收完整的Person对象而不是使用directorId。这样,您就可以建立关联movie.director = director;

请谨慎使用这种方法:如果收到的Person对象未存储在数据库中,则会出现异常。


也许您可以为Directors创建一个缓存。例如,如果您将所有Director保存在Redis中,则可以搜索与收到的Director相对应的directorId,然后执行关联。

当然,您仍然需要进行第二次操作,但这可能比查询数据库便宜。

答案 1 :(得分:0)

这将是丑陋的,但是 您的请求中包含personId,因此可以将您的Movie与长的personId映射起来

class Movie {
    @Id Long id;
    @Column String name;
    @ManyToOne Person director;

    @Column(name="PERSON_ID")
    long personId;
}

在您的控制器中

movie.setPersonId(request.directorId);

答案 2 :(得分:0)

我认为您的MOVIE表中没有'DIRECTOR_NAME'列(假设您遵循第二条规范化规则)。它应该 DIRECTOR_ID

因此,您可以完全跳过将脚本名称加载到您的方案中的情况(前提是DirectId是随请求的查询参数一起发送的)。

由于(应该)在Movie.DIRECTOR_IDDIRECTOR.ID之间有一个外键约束,因此如果您尝试插入任何{{ 1}}不存在。因此,您不必担心。