1

I've got entity FooDetails, which has two fields: Customer and list of Location. Customer has Address (@OneToOne unidirectional mapping), and Location also has Address with @OneToOne mapping.

It happens that Address in Customer and in Location are the same. All these objects come from remote service and I manually put ID from remote object into entity before saving it. Mapping looks like this:

@Entity
@Table(name = "FOO")
public class FooDetails {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private long id;

    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "customer_id")
    private Customer customer;

    @OneToMany(cascade = CascadeType.ALL)
    @JoinColumn(name = "details_id")
    private Set<Location> locationList;
...
}

@Entity
@Table(name = "CUSTOMER")
public class Customer {

    @Id
    @Column(name = "customer_id", unique = true)
    private long customerId;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "address_id")
    private Address address;
...
}

@Entity
@Table(name = "LOCATION")
public class Location {

    @Id
    @Column(name = "location_id", unique = true)
    private long locationId;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "address_id")
    private Address address;
...
}

@Entity
@Table(name = "ADDRESS")
public class Address{

    @Id
    @Column(name = "address_id")
    private long addressId;
    ...

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof StawareAddress)) return false;

        StawareAddress that = (StawareAddress) o;

        return addressId == that.addressId;
    }

    @Override
    public int hashCode() {
        return (int) (addressId ^ (addressId >>> 32));
    }
}

When I receive whole FooDetails object from webservice I try to save it to local database.

If database is clean (no Addresses saved yet), one Address with proper id from WS is saved. If there already is an address with this id Hibernate tries to insert new one into database and it there is an error because of unique constraint on addressId.

I'm using Spring Data Jpa for saving entities (save() method).

What obvious entity mapping problem I missed?

mdziob
  • 1,116
  • 13
  • 26

1 Answers1

1

I would actually discourage the use of CascadeType.ALL here and manually handle it.

public void saveFoodetails(FooDetails fooDetails) {
  Address address = addressRepository.find( fooDetails.getAddress().getId() );
  if ( address != null ) {
    // perhaps you update address with data from fooDetails.getAddress()
    addressRepository.save( address );
    // associate attached address instance with fooDetails now.
    fooDetails.setAddress( address );
  }
  else {
    // save the new incoming address contained in FooDetails
    addressRepository.save( fooDetails.getAddress() );
  }
  // now save/update FooDetails
  fooDetailsRepository.save( fooDetails );        
}
Naros
  • 19,928
  • 3
  • 41
  • 71
  • It's one of the solutions that I know will work, but I would like to avoid - I believe Hibernate should handle it. Detached entity passed to save() has proper hashcode & equals basing on always-existing primary key, which is the same as existing in the database. Why cascade doesn't work? – mdziob Mar 10 '17 at 07:50
  • Hashcode/Equals have nothing to do with this. You are performing a PERSIST operation on FooDetails and that is what gets cascaded to the child relation, Address. Obviously you cannot PERSIST a new Address with the same identifier; however, you could MERGE. But again, that isn't what you're cascading :). – Naros Mar 10 '17 at 16:23
  • I don't know if I understood you correctly. As I mentioned, I'm using save() method from spring data jpa, which checks if passed entity has ID - if so, it calls merge (code snippet in http://stackoverflow.com/questions/16559407/spring-data-jpa-save-new-entity-referencing-existing-one). So merge doesn't cascade? – mdziob Mar 10 '17 at 22:14
  • If you are inserting a new `FooDetails`, you are cascading a `PERSIST`, not a `MERGE`. – Naros Mar 11 '17 at 20:30
  • OK, So I believe the only way to do this i as in your answer - which I followed in my code. Thank you! – mdziob Mar 13 '17 at 14:39