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

Handling relations in POST requests with Spring Data JPA

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

1
2
3
4
5
6
7
8
9
10
11
12
@Entity
class Movie {
    @Id Long id;
    @Column String name;
    @ManyToOne Person director;
}

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

我的控制器如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@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加载导演,然后创建新电影并最后保存它。这将导致两次旅行到数据库:第一次旅行是检索导演,第二次是保存电影。

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

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


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

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

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

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

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

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


我不认为您的Movie表包含'DIRECTOR_NAME'列(假设您遵循规范化的第二条规则)。它只能是DIRECTOR_ID

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

由于您具有(应具有)Movie.DIRECTOR_IDDIRECTOR.ID之间的外键约束,因此如果您尝试插入任何不存在的DIRECTOR_ID,它将照看任何约束冲突。因此,您不必担心。


这将是丑陋的,但是
您的请求中有personId,因此可以将您的Movie与您的long personId

映射在一起

1
2
3
4
5
6
7
8
class Movie {
    @Id Long id;
    @Column String name;
    @ManyToOne Person director;

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

在您的控制器中

1
movie.setPersonId(request.directorId);