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你可以简单点