Spring数据JPA-多通空
我正在学习Spring数据和JPA,在我的实体中建立简单的多通-一通连接时遇到困难 示例:模拟汽车和人(所有者)之间的关系,一个人可以拥有多辆汽车,但一辆汽车只能由一个人拥有 问题:车主(用ManyToOne注释)总是空的 App.javaSpring数据JPA-多通空,spring,jpa,spring-data,Spring,Jpa,Spring Data,我正在学习Spring数据和JPA,在我的实体中建立简单的多通-一通连接时遇到困难 示例:模拟汽车和人(所有者)之间的关系,一个人可以拥有多辆汽车,但一辆汽车只能由一个人拥有 问题:车主(用ManyToOne注释)总是空的 App.java @EnableJpaRepositories @SpringBootApplication public class App { @Autowired private PersonRepository personRepository; @
@EnableJpaRepositories
@SpringBootApplication
public class App {
@Autowired private PersonRepository personRepository;
@Autowired private CarRepository carRepository;
@EventListener
@Transactional
public void handleContextRefresh(ContextRefreshedEvent event) {
Car car1 = new Car();
Car car2 = new Car();
Person person = new Person();
car1.setName("Ferrari");
car2.setName("Porsche");
person.setName("Person Name");
carRepository.save(car1);
carRepository.save(car2);
person.getCars().add(car1);
person.getCars().add(car2);
personRepository.save(person);
carRepository.findAll().forEach(System.out::println); // TODO owner should not be null
personRepository.findAll().forEach(System.out::println);
}
public static void main(String[] args) throws IOException, URISyntaxException {
SpringApplication.run(App.class, args);
}
}
java
@Entity
public class Car {
@Id
@GeneratedValue
private UUID id;
private String name;
@ManyToOne
private Person owner;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Person getOwner() { return owner; }
public void setOwner(Person owner) { this.owner = owner; }
@Override
public String toString() {
return "Car [id=" + id + ", name=" + name + ", owner=" + owner + "]";
}
}
CarRepository.java
@Repository
public interface CarRepository extends CrudRepository<Car, UUID> { }
@Repository
public interface PersonRepository extends CrudRepository<Person, UUID> { }
两辆车的车主均为空。如何解决此问题?问题是您未通过car.setOwner(…)设置车主。 而且,由于您在
OneToMany
注释上有cascade=CascadeType.ALL
,因此您不需要先保存单个汽车,然后保存车主
只需调用personRepository.save(person)代码>应该足够了
因此,通过上述更改,您可以编写以下代码:
Car car1 = new Car();
Car car2 = new Car();
Person person = new Person();
car1.setName("Ferrari");
car2.setName("Porsche");
person.setName("Person Name");
person.getCars().add(car1);
person.getCars().add(car2);
car1.setOwner(person);
car2.setOwner(person);
personRepository.save(person);
还有一件事,您需要修改Car
对象的toString方法。Person对象的toString正在调用Car的toString,Car的toString在内部再次调用Person
toString。这将导致StackOverflowException
。考虑不要在<代码> Car toStrug()/<代码>方法中打印整个<代码>人对象< /代码>。
可能是Car[id=“+id+”,name=“+name+”,owner=“+owner.getName()+”]
**更新**
如果您正在寻找一种特定的解决方案,希望确保车主在DB中填充,而无需调用Car.setOwner(…)
方法,则可以更新OneToMany
映射,如下所示:
@OneToMany(cascade=CascadeType.ALL)
@JoinColumn(name="owner")
private Set<Car> cars = new HashSet<>();
@OneToMany(cascade=CascadeType.ALL)
@JoinColumn(name=“owner”)
私家车=新的HashSet();
基本上,mappedBy
决定谁是双向关系的所有者。在第一种情况下,您提到了关系的所有权由Car
实体通过mappedBy
属性拥有。因此,除非我们在car对象上设置owner,否则ORM将不会填充owner
列
第二种方法是删除mappedBy,oneToMany
将成为关系的所有者,因此,一旦您添加到此人的车辆列表中,该人员id将用作所有者,并将在DB中根据车辆的“所有者”列填充
但是,在第二种情况下,会触发额外的更新来更新所有者列(与正常插入不同),因此所有者列应该可以为空。这都是在单个事务的上下文中进行的,不同的存储库处理保存,而不进行刷新,所以我不确定你能否从carRepo检索personRepo所做的更改。简单的回答就是打电话给car.setOwner,然后把这个人传进来。否则,您需要使用flush
,使用entityManager
实例而不是两个单独的回购,使用personRepo
检索汽车,或者重新构造代码以用不同的方法处理事情。据我所知,这是少数几种可能的解决方案之一。如果没有更详细的答案,我会接受的。感谢您指出,由于CascadeType.ALL以及潜在的StackOverFlowException,不需要进行任何保存。不确定您是否正在寻找一种特定的解决方案,在这种解决方案中,您希望在不调用Car.setOwner(…)
方法的情况下,确保车主在DB中进行填充?如果是这样,您可以查看更新后的答案。如果我使用第一种方法(Car拥有关系),为什么还需要person.getCars().add(car1)调用?因为cascadeType.ALL位于OneToMany
字段中。因为我们希望persist onPerson
传播到集合中的元素,所以我们需要填充它。否则,您将需要先对Person进行持久化,然后对单个车辆调用persist。
Car car1 = new Car();
Car car2 = new Car();
Person person = new Person();
car1.setName("Ferrari");
car2.setName("Porsche");
person.setName("Person Name");
person.getCars().add(car1);
person.getCars().add(car2);
car1.setOwner(person);
car2.setOwner(person);
personRepository.save(person);
@OneToMany(cascade=CascadeType.ALL)
@JoinColumn(name="owner")
private Set<Car> cars = new HashSet<>();