Java 使用Spring数据JPA处理POST请求中的关系

Java 使用Spring数据JPA处理POST请求中的关系,java,spring,spring-mvc,spring-data-jpa,spring-data,Java,Spring,Spring Mvc,Spring Data Jpa,Spring Data,我有以下实体,每个实体都有一个crudepository: @Entity class Movie { @Id Long id; @Column String name; @ManyToOne Person director; } @Entity class Person { @Id Long id; @Column String name; } 我的控制器如下所示: @RestController @RequestMapping("/movies")

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

@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加载导演,然后创建新电影并最终保存。这会导致两次数据库访问:第一次是检索导演,第二次是保存电影

在这种情况下,这不是一个大问题,但可能有一个实体具有许多关系,这意味着可能需要执行许多查询以实现单个插入


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

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

只有当端点在创建
电影的同时创建
人物
时,才可能有另一种选择。然后,您可以简单地执行两个保存操作,或者使用
CascadeType=ALL
执行单个保存操作


如果您能够更改请求参数,那么接收完整的
Person
对象而不是接受
directorId
可能是一个不错的选择。这样,您就可以创建关联
movie.director=director

注意这种方法:如果收到的
Person
对象未存储在数据库中,则会出现异常


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

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

这会很难看,但是 您的请求中有personId,因此您可以使用长personId映射您的电影

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

    @Column(name="PERSON_ID")
    long personId;
}
在控制器中

movie.setPersonId(request.directorId);

我认为您的
MOVIE
表中没有“
DIRECTOR\u NAME
”列(假设您遵循规范化的第二条规则)。它应该是唯一的
DIRECTOR\u ID

因此,您可以完全跳过在场景中加载控制器名称(前提是使用请求的查询参数发送directId)


由于您在
Movie.DIRECTOR\u ID
DIRECTOR.ID
之间有(应该有)一个外键约束,如果您试图插入任何不存在的
DIRECTOR\u ID
,它将处理任何约束冲突。所以你不必担心。

谢谢你的回答。假设相关实体已经存在。如果我有一个具有10个关系的实体,会发生什么?我需要做10个查询加上保存吗?很遗憾:是的。这主要是因为与电影相关的
人员取决于用户请求参数。我更新了我的答案,提供了一个在您的特定环境中可能会感兴趣的替代方案,以提高性能。感谢您建议使用缓存,但我仍然在等待HTTP请求,该请求应该是一个简单的插入。在我的情况下,将完整的
个人
放入请求中也是不可接受的,因为相关实体可能很大,也可能有关系。我真正想要的是在
spring data jpa
中处理这个问题的方法。好的@ESala,在回答中,我尝试了包含我想到的所有想法。对不起,我帮不上忙。谢谢你@andreybleme,我真的很感激!我暂时不讨论,看看是否有其他人提出了另一个解决方案。你能详细说明一下吗?你用
personId
映射电影是什么意思?谢谢你的回答,但是导演字段不会被更新。此外,当执行查询以获取电影的详细信息时,每个关系都需要额外的查询。由于director字段将与表中的personId相同,因此它也将更新。至于细节,您将使用@manytone-Person-director您熟悉findById和getOne的区别吗?既然你只需要提到导演,而不是整个对象,那么看看这个问题,谢谢@C.Weber,这几乎就是我想要的。正如您所提到的,在本例中,
getOne
方法优于
findById
,因为它获取的是一个引用,而不是整个实体,但它仍将前往数据库检查id是否存在,如果不存在,则抛出
EntityNotFoundException
。我真的很想把它作为一个单一的数据库操作来完成,这看起来可能吗?您正在使用Hibernate作为JPA提供程序?只要您不访问标识符以外的任何属性,AFAIK Hibernate就不会初始化代理。可以通过以下设置hibernate.jpa.compliance.proxy来更改此行为(默认值应为false)。如果设置为true,则在访问标识符时也会初始化代理。有关更多信息,请参阅。我猜你正在访问代理,标志设置为true或我的信息是旧的:)@ESala电影表中的列是什么???@C.Weber是的,我使用的是Hibernate,但不是直接的,我在顶部使用的是spring数据jpa。我刚刚用
PersonRepository
扩展
JpaRepository
并调用
getOne(id)
对它进行了测试,但它仍然获取实体。这是记录的查询:
从person person0中选择person0.id作为id1\u 1\u 0\u,person0.name作为name2\u 1\u 0\u。其中person0.id=?
。是的,这就是我拥有的,控制器被引用为外键
控制器id
。我要找的是如何解决我问题末尾描述的问题。@ESala你可以简单点