Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@ManyToMany - data does not persist in Database

Simplifying, in my database I have tables:

Car (pk="id_car")

CarAddon (pk="id_car_fk,id_addon_fk", 
`FK_car_addon_addon` FOREIGN KEY (`id_addon_fk`) REFERENCES `addon` (`id_addon`)
`FK_car_addon_car` FOREIGN KEY (`id_car_fk`) REFERENCES `car` (`id_car`)

Addon (pk="id_addon")

Shortly: I have cars, many cars can has many addons (like ABS etc).
There are tables with cars, addons, and one table which is logical connection.

Overall, entities work fine. I have no problems with persist data, when I want persist single object. I don't have problems, when I want FETCH data, ie. Car->getAddon();

But, when I'm going to persisting a collection, nothing happens. No exceptions were thrown, there were no new data in database.

//DBManager is a singleton to create an EntityManager
EntityManager em = DBManager.getManager().createEntityManager();

em.getTransaction().begin();
Addon addon1 = new Addon();
addon1.setName("czesc1");
em.persist(addon1);
Addon addon2 = new Addon();
addon2.setName("czesc2");
em.persist(addon2);

car.setAddonCollection(new ArrayList<Addon>());
car.getAddonCollection().add(addon1);
car.getAddonCollection().add(addon2);
em.persist(car);
em.getTransaction().commit();

In this case, addons were stored in Addon table, car in Car table. There are no new data in CarAddon table though object car has good data (there is addon collection in debbuger).

When I changed em.persist(car) to em.merge(car) I got an exception:

"SEVERE: Persistence error in /admin/AddAuction : java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: model.entity.Car[ idCar=0 ]."

Simple version of my classess:

  @Entity
  @Table(name = "addon")
  @XmlRootElement
  @NamedQueries({...})
  public class Addon implements Serializable {
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      @Basic(optional = false)
      @NotNull
      @Column(name = "id_addon")
      private Integer idAddon;

      @Size(max = 100)
      @Column(name = "name")
      private String name;

      @JoinTable(name = "car_addon", 
          joinColumns = {
          @JoinColumn(name = "id_addon_fk", referencedColumnName = "id_addon")}, 
          inverseJoinColumns = {
          @JoinColumn(name = "id_car_fk", referencedColumnName = "id_car")})
      @ManyToMany
      private List<Car> carCollection;

      @XmlTransient
      public List<Car> getCarCollection() {
          return carCollection;
      }

      public void setCarCollection(List<Car> carCollection) {
          this.carCollection = carCollection;
      }
  }



@Entity
@Table(name = "car")
@XmlRootElement
@NamedQueries({...)
public class Car implements Serializable {

    @ManyToMany(mappedBy = "carCollection", fetch= FetchType.EAGER, cascade=CascadeType.ALL)
    private List<Addon> addonCollection;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @NotNull
    @Column(name = "id_car")
    private Integer idCar;

    @XmlTransient
    public List<Addon> getAddonCollection() {
        return addonCollection;
    }

    public void setAddonCollection(List<Addon> addonCollection) {
        this.addonCollection = addonCollection;
    }
}

How can I fix it?

ps1. I have:

cascade=CascadeType.ALL przy @ManyToMany private List<Car> carCollection

but this dos not solve my problem.

ps2. I am using Netbeans 7, EclipseLink and MySQL (not Hibernate - I have problem with it)

like image 577
vizzdoom Avatar asked Jun 17 '11 16:06

vizzdoom


1 Answers

I have one theory that always seems to trip people up with many-to-many collections. The problem is that in memory, the associations are made in two places. Both in the car's addons list and in the addon's cars list. In the database, there isn't such a duplication.

The way JPA providers get around this is through the mappedBy attribute. Since you have mappedBy on the car's addons list this means that the relationship is actually controlled by the addon's cars list (confusing I know).

Try adding the following:

addon1.setCarCollection(new ArrayList<Car>());
addon1.getCarCollection().add(car);

addon2.setCarCollection(new ArrayList<Car>());
addon2.getCarCollection().add(car);

before you persist the car.

like image 76
Pace Avatar answered Oct 04 '22 06:10

Pace