我正在学习Spring Data和JPA,而我在实体中制作一个简单的ManyToOne - OneToMany连接时遇到了麻烦。
示例:汽车和人(车主)之间的模型关系,一个人可以拥有多辆汽车,但一辆汽车只有一个人拥有。
问题:Car的所有者(使用ManyToOne注释)始终为null。
App.java
@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);
}
}
Car.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> { }
Person.java
@Entity
public class Person {
@Id
@GeneratedValue
private UUID id;
private String name;
@OneToMany(mappedBy="owner", cascade=CascadeType.ALL)
private Set<Car> cars = new HashSet<>();
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Set<Car> getCars() { return cars; }
public void setCars(Set<Car> cars) { this.cars = cars; }
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", cars=" + cars + "]";
}
}
PersonRepository.java
@Repository
public interface PersonRepository extends CrudRepository<Person, UUID> { }
输出:
Car [id=10b38a23-536d-4b8f-8578-16e12f2f65d1, name=Ferrari, owner=null]
Car [id=2c15abf9-cb96-4d70-9271-b25096764276, name=Porsche, owner=null]
Person [id=8484b16f-1645-49af-a4d5-4b6ba7238aba, name=Person Name, cars=[Car [id=2c15abf9-cb96-4d70-9271-b25096764276, name=Porsche, owner=null], Car [id=10b38a23-536d-4b8f-8578-16e12f2f65d1, name=Ferrari, owner=null]]]
两辆车的车主都是空的。我该如何解决这个问题?
答案 0 :(得分:0)
问题是你是not setting the owner of the car via car.setOwner(...)
。
此外,由于cascade=CascadeType.ALL
注释中有OneToMany
,因此您不需要先保留单个车辆,然后再保留所有者。
只需拨打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,它在内部再次调用Person
toString。这将导致StackOverflowException
。考虑不在Person object
方法中打印整个Car toString()
。
可能是Car [id=" + id + ", name=" + name + ", owner=" + owner.getName() + "]
**更新**
如果您正在寻找一个特定的解决方案,您希望确保在不调用car.setOwner(...)
方法的情况下在数据库中填充车主,那么您可以更新OneToMany
映射,如下所示:
@OneToMany(cascade=CascadeType.ALL)
@JoinColumn(name="owner")
private Set<Car> cars = new HashSet<>();
基本上,mappedBy
是决定谁是双向关系所有者的人。在第一种情况下,您提到关系的所有权由Car
实体通过mappedBy
属性拥有。因此,除非我们在car对象上设置所有者,否则ORM将不会填充owner
列。
使用第二种方法删除mappedBy,oneToMany
成为关系的所有者,因此一旦您添加到Person的汽车列表中,该人员ID将被用作所有者并将被填充在DB中对着Car的所有者列。
但是,在第二种情况下,为了更新所有者列而触发额外更新,除了正常插入之外,所以所有者列应该是可为空的。