Answering my own question here :P 

I figured it out. Basically it seems I should be annotating only one side of 
the relationships with a cascade type. Annotating both side seems to cause 
confusion maybe for the entity manager and doesn’t know which to persist first? 
Anyway, I chose to have the declarations within the parent beans (see code 
below). Obviously this means that if I try and persist a child bean before the 
parent, then an error is thrown. However, having it this way means that 
insertion order remains consistent. You save the ‘Person’ bean and then any 
child relationships get saved (inserted) after.

Hope this helps someone in the future when playing with JPA relationships and 
cascading.

Chris.

public class Person {

        @OneToMany(mappedBy = "person", fetch = FetchType.EAGER, cascade = 
CascadeType.ALL)
        private List<Car> cars;
        
        public void addCar(Car car) {
                car.setPerson(this);
                this.cars.add(car);
        }
}

public class Car {
        
        @ManyToOne // no cascade type here
        @JoinColumn(name = "personId")
        private Person person;

        @OneToMany(mappedBy = "car", fetch = FetchType.EAGER, cascade = 
CascadeType.ALL)
        private List<Car> wheels;

        public void setPerson(Person person) { this.person = person; }

        public void addWheel(Wheel wheel) {
                wheel.setCar(this);
                this.wheels(wheel);
        }
        
}

public class Wheel {

        @ManyToOne // no cascade type here
        @JoinColumn(name = "carId")
        private Car car;

        public void setCar(Car car) { this.car = car; }
}

On 10 Feb 2014, at 10:51, Chris Christo <[email protected]> wrote:

> Hi, I was hoping someone could help shed some light on the following issue. 
> 
> (I am using OpenEJB 4.6.1-SNAPSHOT)
> 
> I have a problem when trying to save a bean that has child elements 
> (relationships to other tables). The problem occurs when I want to create a 
> completely new record with completely new child records. The problem is that 
> when the JPA provider gets to work and starts calling INSERT statements, the 
> order in which it does so seems to be random. And thus on certain occasions 
> it tries to call the INSERT statement of the child bean first before the 
> parent which will throw an SQL error (‘foreign key constraint fails’ error).
> 
> It occurs when I call a rest method which will save the bean. I’m assuming a 
> transaction is opened at the start of the method and committed and the end.
> 
> To illustrate, take a look at the code below (which resembles my scenario). 
> Basically I have a rest method (which tries to save a bean with child beans 
> which are all new records). I’m wondering if it has something to do with my 
> cascade annotations? Is there some way to tell the JPA provider to call the 
> INSERT statements in order from parent down to child  (in that order)?
> 
> @Stateless
> @Path(“/example")
> @Consumes({MediaType.APPLICATION_JSON})
> @Produces({MediaType.APPLICATION_JSON})
> public class ExampleAPI {
> 
>       @Path(“/save”)
>       public void create(String name) {
>               Person person = new Person(name);
> 
>               Car car = new Car();
> 
>               car.addWheel(new Wheel());
>               car.addWheel(new Wheel());
> 
>               person.addCar(car);
> 
>               entityManager.persist(person); // throws error
>       }
> 
> }
> 
> public class Person {
> 
>       @OneToMany(mappedBy = "person", fetch = FetchType.EAGER, cascade = 
> CascadeType.PERSIST)
>       private List<Car> cars;
>       
>       public void addCar(Car car) {
>               car.setPerson(this);
>               this.cars.add(car);
>       }
> }
> 
> public class Car {
>       
>       @ManyToOne(cascade = CascadeType.PERSIST)
>       @JoinColumn(name = "personId")
>       private Person person;
> 
>       @OneToMany(mappedBy = "car", fetch = FetchType.EAGER, cascade = 
> CascadeType.PERSIST)
>       private List<Car> wheels;
> 
>       public void setPerson(Person person) { this.person = person; }
> 
>       public void addWheel(Wheel wheel) {
>               wheel.setCar(this);
>               this.wheels(wheel);
>       }
>       
> }
> 
> public class Wheel {
> 
>       @ManyToOne(cascade = CascadeType.PERSIST)
>       @JoinColumn(name = "carId")
>       private Car car;
> 
>       public void setCar(Car car) { this.car = car; }
> }

Reply via email to