Spring Data JPA - ManyToOne null

时间:2018-03-25 23:44:08

标签: spring jpa spring-data

我正在学习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]]]

两辆车的车主都是空的。我该如何解决这个问题?

1 个答案:

答案 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的所有者列。

但是,在第二种情况下,为了更新所有者列而触发额外更新,除了正常插入之外,所以所有者列应该是可为空的。