由 JPA 多对多产生的空连接表

Posted

技术标签:

【中文标题】由 JPA 多对多产生的空连接表【英文标题】:Empty Join Table resulting from JPA ManyToMany 【发布时间】:2010-08-09 11:09:29 【问题描述】:

我有一个应用程序,我正在尝试使用 Hibernate 作为我的 JPA 提供程序来实现 2 个实体之间的多对多关系。

我正在尝试的示例是单向示例,其中一个相机可以有多个镜头,而镜头可以适合多个相机。

以下是我的实体类...(只是粘贴它的相关部分)

相机:

@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>();

    ...

镜头:

@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;

    ...

在测试用例中,我尝试像这样持久化 Camera 实例...

@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);

持久化成功发生,Hibernate 没有抛出错误/异常。然而,出乎意料的是,JoinTable(本例中为 CAMERA_LENS)中并没有创建行。仅在每个 Lens 和 Camera 表中创建一行,因此不能满足 ManyToMany 的目的。但是,DDL 是为连接表生成并创建的,只是它没有任何记录。

任何帮助或指点将不胜感激。

【问题讨论】:

【参考方案1】:

我发现了问题。这有点奇怪。我使用的 DAO 类有一个类级别注释:

@Transactional(readOnly=true)

生成的sql是

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

它仍然保存了 2 个实体,但没有保存或将数据插入连接表。

我添加方法级别注释的那一刻:

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

一切都很好,数据确实被插入到连接表中。在这种情况下生成的新 SQL 是...

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 (?, ?)

仍然有点奇怪,没有事务传播策略将数据保存到 2 个表中,但是一旦给出传播策略,一切都很好。

另一个有趣的观察是,我尝试在方法级别将传播策略设置为 MANDATORY(检查是否存在导致第一个错误情况的事务),但是该方法抛出了一个异常,表明不存在事务,数据仍然保存到 2 个表中。

希望对以后的人有所帮助...

【讨论】:

就我而言,这没有帮助。有什么帮助是刷新会话......(我仍然不明白为什么需要这样做,因为在事务中看到其他集合更新。只是没有看到中间表需要更新的这个。去图) 我认为 @Transactional(readOnly=true) 可能仍然有一个默认传播,它是必需的,它将启动一个 tx。我遇到了同样的问题,使用 @Transactional(progpagation=REQUIRED,readOnly=true) 并没有插入连接表。按照@Adres 的建议调用flush,但删除readOnly 也是如此

以上是关于由 JPA 多对多产生的空连接表的主要内容,如果未能解决你的问题,请参考以下文章

JPA多对多合并所有者触发器删除连接表

EntityFramework同一张表多对多关系

Django:表多对多查询聚合分组FQ查询事务

仅从 Spring Data JPA 中的联接表(多对多)中选择特定列

6django操作表多对多实战

多对多关系中的 JPA 条件查询