使用 JPA 和 Hibernate 提供程序的多对多关系不会创建主键

Posted

技术标签:

【中文标题】使用 JPA 和 Hibernate 提供程序的多对多关系不会创建主键【英文标题】:ManyToMany relationship using JPA with Hibernate provider is not creating primary keys 【发布时间】:2011-03-29 06:54:56 【问题描述】:

我使用 Hibernate 作为提供者创建了两个具有多对多关系的 JPA 实体(客户端、InstrumentTraded)。在让 Hibernate 为 mysql 生成表之后,ManyToMany 关系表似乎不包含两个外键的主键。这允许多对多表中的重复记录,这不是我们想要的结果。

生成的表格:

client(id,name)  
instrument_traded(id,name)  
client_instrument_traded(FK client_id, FK instrument_traded_id)

首选表:

client_instrument_traded(PK,FK client_id, PK,FK instrument_traded_id)

实体:

@Entity
public class Client extends AbstractEntity<Integer> 

    private static final long serialVersionUID = 1L;

    @Basic(optional = false)
    @Column(nullable = false, length = 125)
    private String name;

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(joinColumns = 
        @JoinColumn, inverseJoinColumns = 
        @JoinColumn(name = "instrument_traded_id"), uniqueConstraints =
    @UniqueConstraint(name = "UK_client_instruments_traded_client_id_instrument_traded_id",
    columnNames = "client_id", "instrument_traded_id"))
    @ForeignKey(name = "FK_client_instruments_traded_client_id",
    inverseName = "FK_client_instruments_traded_instrument_traded_id")
    private List<InstrumentTraded> instrumentsTraded;

    public Client() 
    

    public List<InstrumentTraded> getInstrumentsTraded() 
        return instrumentsTraded;
    

    public void setInstrumentsTraded(List<InstrumentTraded> instrumentsTraded) 
        this.instrumentsTraded = instrumentsTraded;
    

    ...




@Entity
@Table(uniqueConstraints = 
    @UniqueConstraint(name = "UK_instrument_traded_name", columnNames = "name"))
public class InstrumentTraded extends AbstractEntity<Integer> 

    private static final long serialVersionUID = 1L;

    @Basic(optional = false)
    @Column(nullable = false, length = 50)
    private String name;

    @ManyToMany(mappedBy = "instrumentsTraded", fetch = FetchType.LAZY)
    private List<Client> clients;

    public InstrumentTraded() 
    

    public List<Client> getClients() 
        return clients;
    

    public void setClients(List<Client> clients) 
        this.clients = clients;
    

    ...


在做了一些研究之后,看起来唯一的解决方案是mapping a join table with additional columns 在我不需要其他列时使用@OneToMany @IdClass 和复合主键类。这是除了我在上面的代码中包含的唯一解决方案之外的唯一解决方案,它使用@UniqueConstraint@ManyToMany 映射上的两个外键列?像这样的常见场景所需的工作量似乎有点荒谬。谢谢!

【问题讨论】:

***.com/questions/1212058/… 另外值得检查一下你是否不小心添加了两次记录,这发生在我身上,所以list.add(x); list.add(x); 结果在列表中重复。 【参考方案1】:

您的映射看起来很奇怪(尤其是 @JoinTable 注释的 joinColumn 部分)。我希望是这样的:

@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(
    joinColumns= 
        @JoinColumn(name="CLIENT_ID", referencedColumnName="ID"), 
    inverseJoinColumns=
        @JoinColumn(name="instrument_traded_id", referencedColumnName="ID"),
    uniqueConstraints=
        @UniqueConstraint(
            name="UK_client_instruments_traded_client_id_instrument_traded_id",
            columnNames = "client_id", "instrument_traded_id"
        )
)
@ForeignKey(name = "FK_client_instruments_traded_client_id",
inverseName = "FK_client_instruments_traded_instrument_traded_id")
private List<InstrumentTraded> instrumentsTraded;

但除非你想覆盖默认值(我猜你会这样做),否则我会跳过 @JoinTable

【讨论】:

我确实想覆盖默认值,因为它使 instrument_traded_id (instruments_traded_id) 复数。我有一个自定义命名策略,如果外键末尾不存在“_id”,则添加“_id”。我可以删除 joinColumns=@JoinColumn。您是否有一个 ManyToMany 的工作示例,它在 ManyToMany 表中创建一个由两个外键组成的主键? @dukethrash: Do you have a working example of a ManyToMany creating a primary key consisting of two foreign keys in the ManyToMany table? 如果正确注释,这是默认行为。你试过没有joinColumn(或者上面那个)吗? 是的,我试过你的(除了referencedColumn实际上是referencedColumnName),它仍然没有生成主键。我什至在 InstrumentTraded 上添加了以下内容,看看这是否会改变任何事情。 @ManyToMany(fetch = FetchType.LAZY, mappedBy = "instrumentsTraded") private List clients;【参考方案2】:

将您的@ManyToMany 拆分为@OneToMany - @ManyToOne 关系请参阅here 如何完成此映射

【讨论】:

【参考方案3】:

这里有一个解决问题的方法:

Client.java:

 @ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.ALL)
 @JoinTable(
        joinColumns = @JoinColumn(name = "client_id"),
        inverseJoinColumns = @JoinColumn(name = "instrument_traded_id"),
        uniqueConstraints = @UniqueConstraint(
            columnNames = "client_id", "instrument_traded_id")
)
 private List<InstrumentTraded> instrumentsTraded;

这用于单向映射。如果您想要双向关系,请将InstrumentTraded.class 中的映射更改为相同。

【讨论】:

【参考方案4】:

我遇到了类似的问题。我所做的只是将集合类型从 List 更改为 Set:

private List<InstrumentTraded> instrumentsTraded;

private Set<InstrumentTraded> instrumentsTraded;

Hibernate 现在以某种方式为连接表生成主键。

【讨论】:

“所有伟大的事情都很简单......” 是吗?还是只是 Java 集在内存上工作? 还值得检查一下你是否不小心添加了两次记录,这发生在我身上,所以list.add(x); list.add(x); 结果在列表中重复。 @ViniciusPires,它确实创建了一个复合主键!经过测试。

以上是关于使用 JPA 和 Hibernate 提供程序的多对多关系不会创建主键的主要内容,如果未能解决你的问题,请参考以下文章

使用JPA和Hibernate提供程序的ManyToMany关系不会创建主键

Spring,JPA,Hibernate,Atomikos - 奇怪的启动错误

如何将 Hibernate 配置为 Payara/Glassfish 4.1 的 JPA 提供程序? [关闭]

如何使用休眠将mysql与Java连接?例外:没有名为 org.hibernate.tutorial_jpa 的 EntityManager 的持久性提供程序 [关闭]

如何使用 Hibernate JPA 和 Spring Framework 启用加载时间/运行时编织

Spring Data JPA 是不是在内部使用 Hibernate 以及如果我不提供方言属性,为啥我的应用程序正在运行?