Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Empty Join Table resulting from JPA ManyToMany

I have an application in which I am trying to implement ManyToMany relation between 2 entities using Hibernate as my JPA provider.

The example I am trying is an uni-directional one, wherein a Camera can have multiple Lenses and Lense can fit into multiple Cameras.

Following is my entity class...(Just pasting the relevant part of it)

Camera:

@Entity
@Table(name = "CAMERAS")
public class Camera implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "CAM_ID", nullable = false)
    private Long camId;

    @Column(name="CAMERA_BRAND")
    private String cameraBrand;

    @ManyToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE},fetch=FetchType.EAGER)
    @JoinTable(name="CAMERA_LENS",joinColumns=@JoinColumn(name="CAM_ID"),inverseJoinColumns=@JoinColumn(name="LENS_ID"))
        private Set<Lens> lenses = new HashSet<Lens>();

    ...
}

Lens:

@Entity
@Table(name = "LENSES")
public class Lens implements Serializable {        
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "LENS_ID", nullable = false)
    private Long lensId;

    @Column(name="LENS_NAME")
    private String name;

    ...
}

In the test-case, I try to persist the Camera instance like this...

@Test
public void test_saveCameraLenseManyToMany() {
    Camera cam = new Camera();
    cam.setCameraBrand("Nikon");
    Set<Camera> cameras = new HashSet<Camera>();
    cameras.add(cam);

    Set<Lens> lenses = new HashSet<Lens>();
    Lens lens1 = new Lens();
    lens1.setName("WideAngle");
    lenses.add(lens1);

    cam.setLenses(lenses);

    // concreteDAO.saveLens(lens1);
    concreteDAO.saveCamera(cam);
}

The persistence happens successfully, and there is no error/exception thrown by Hibernate. However, contrary to expectation, a row is not created in the JoinTable (CAMERA_LENS in this case). A row is created in each of Lens and Camera tables only, thereby not serving the purpose of ManyToMany. However, the DDL is generated for the join table and it is created as well, but just that it does not have any records.

Any assistance or pointers would be much appreciated.

like image 553
PaiS Avatar asked Aug 09 '10 11:08

PaiS


1 Answers

I found the problem. It is a bit weird. The DAO class that I was using had a class level annotation of:

@Transactional(readOnly=true)

The sql generated was

Hibernate: insert into CAMERAS (CAMERA_BRAND) values (?)
Hibernate: insert into LENSES (LENS_NAME) values (?)
Hibernate: insert into LENSES (LENS_NAME) values (?)

And still it saved the 2 entities, but did not save or insert data into the join table.

The moment I added a method level annotation of :

@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)

everything turned out to be fine, and data was indeed inserted into the join table. The new SQLs generated in this case was...

Hibernate: insert into CAMERAS (CAMERA_BRAND) values (?)
Hibernate: insert into LENSES (LENS_NAME) values (?)
Hibernate: insert into LENSES (LENS_NAME) values (?)

Hibernate: insert into CAMERAS_LENSES (CAM_ID, LENS_ID) values (?, ?)
Hibernate: insert into CAMERAS_LENSES (CAM_ID, LENS_ID) values (?, ?)

Kinda weird still that no transaction propogation strategy saved data into 2 tables, but once the propogation strategy was given, everything turned out to be fine.

Another interesting observation is that I tried putting the propogation strategy as MANDATORY at the method level (to check if there existed a Transaction which led to the first and faulty case), however the method threw an exception indicating that no transaction existed, still the data was saved into the 2 tables.

Hope that helps someone further on...

like image 191
PaiS Avatar answered Nov 18 '22 19:11

PaiS