Search code examples
javaspringhibernatemany-to-many

move many-to-many association to @Embedded object


I have following code:

@Entity
public class Car {

    @Id
    @GeneratedValue()
    private Long id;

    @ManyToMany(fetch = FetchType.LAZY, mappedBy = "cars")
    private List<Driver> drivers = new ArrayList<>();

    //other properties, getters and setters
}

@Entity
public class Driver {

    @Id
    @GeneratedValue()
    private Long id;

    @ManyToMany
    private List<Car> cars = new ArrayList<>();

    //other properties, getters and setters
}

public void addCar(List<Driver> drivers) {
    Car car = new Car();

    car.setDrivers(drivers);

    //set other properties

    carRepository.save(car);
}

And it works fine.

For some reason, I want to move drivers list to @Embedded object:

@Entity
public class Car {

    @Id
    @GeneratedValue()
    private Long id;

    @Embedded
    private DriverInfo driverInfo;

    //other properties, getters and setters
}

@Embeddable
public class DriverInfo {

    @ManyToMany(fetch = FetchType.LAZY, mappedBy = "cars")
    private List<Driver> drivers = new ArrayList<>();

    //other properties, getters and setters
}

But adding a car doesn't associate cars and drivers in drivers_cars table. It works only if I do it like this:

public void addCar(List<Driver> drivers) {
    Car car = new Car();

    car.setDrivers(drivers);

    for (Driver driver : drivers) {
        driver.getCars.add(car);
    }

    //set other properties

    carRepository.save(car);
}

So, I want to know why this two ways of association work differently


Solution

  • Both examples have problems.

    You should be in the habit of making sure that when you associate two entities with one another, that both sides of a bidirectional relationship is properly maintained, or else you'll get strange behavior which could result in not persisting the objects as in your second case or traversing the object graph within the boundary of the same session and getting NullPointerExceptions.

    Your former case should be assigning the Car instance to the Driver's car collection much like how you're doing so as a part of your embeddable example to maintain the correct relationship between the two objects.

    That said, the reason you're first use case may work at persist time is because the type can be inferred because the collection element type on Driver's cars collection is the same managed type that holds the inverse side of the relation. This isn't the case for the embeddable case, thus why no data is actually persisted as you observed.

    Again, the proper solution is to make sure that both sides of the bidirectional relationship are set. This not only makes sure the code works under Hibernate, but is portable across persistence providers.