Search code examples
javahibernatejpamany-to-one

JPA many-to-one relation - need to save only Id


I have 2 classes: Driver and Car. Cars table updated in separate process. What I need is to have property in Driver that allows me to read full car description and write only Id pointing to existing Car. Here is example:

@Entity(name = "DRIVER")
public class Driver {
... ID and other properties for Driver goes here .....

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name = "CAR_ID")
    private Car car;

    @JsonView({Views.Full.class})
    public Car getCar() {
      return car;
    }
    @JsonView({Views.Short.class})
    public long getCarId() {
      return car.getId();
    }
    public void setCarId(long carId) {
      this.car = new Car (carId);
    }

}

Car object is just typical JPA object with no back reference to the Driver.

So what I was trying to achieve by this is:

  1. I can read full Car description using detailed JSON View
  2. or I can read only Id of the Car in Short JsonView
  3. and most important, when creating new Driver I just want to pass in JSON ID of the car. This way I dont need to do unnesessery reads for the Car during persist but just update Id.

Im getting following error:

object references an unsaved transient instance - save the transient instance before flushing : com.Driver.car -> com.Car

I dont want to update instance of the Car in DB but rather just reference to it from Driver. Any idea how to achieve what I want?

Thank you.

UPDATE: Forgot to mention that the ID of the Car that I pass during creation of the Driver is valid Id of the existing Car in DB.


Solution

  • That error message means that you have have a transient instance in your object graph that is not explicitly persisted. Short recap of the statuses an object can have in JPA:

    • Transient: A new object that has not yet been stored in the database (and is thus unknown to the entitymanager.) Does not have an id set.
    • Managed: An object that the entitymanager keeps track of. Managed objects are what you work with within the scope of a transaction, and all changes done to a managed object will automatically be stored once the transaction is commited.
    • Detached: A previously managed object that is still reachable after the transction commits. (A managed object outside a transaction.) Has an id set.

    What the error message is telling you is that the (managed/detached) Driver-object you are working with holds a reference to a Car-object that is unknown to Hibernate (it is transient). In order to make Hibernate understand that any unsaved instances of Car being referenced from a Driver about be saved should also be saved you can call the persist-method of the EntityManager.

    Alternatively, you can add a cascade on persist (I think, just from the top of my head, haven't tested it), which will execute a persist on the Car prior to persisting the Driver.

    @ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST)
    @JoinColumn(name = "CAR_ID")
    private Car car;
    

    If you use the merge-method of the entitymanager to store the Driver, you should add CascadeType.MERGE instead, or both:

    @ManyToOne(fetch=FetchType.LAZY, cascade={ CascadeType.PERSIST, CascadeType.MERGE })
    @JoinColumn(name = "CAR_ID")
    private Car car;