【问题标题】:Foreign key problem in Java Persistance API with Netbeans 6.9.1带有 Netbeans 6.9.1 的 Java Persistence API 中的外键问题
【发布时间】:2010-09-03 09:24:53
【问题描述】:

我在 Netbeans 中设置 Java 持久性时出错。我使用 MySQL 作为后端数据库服务器。

我的代码如下,

数据库

CREATE TABLE `hub` (
 `hub_ID` INT(11) NOT NULL AUTO_INCREMENT,
 `hub_Name` VARCHAR(45) NOT NULL,
 PRIMARY KEY (`hub_ID`),
 UNIQUE INDEX `hub_Name` (`hub_Name`)
)ENGINE=MyISAM

CREATE TABLE `company` (
 `company_ID` INT(11) NOT NULL AUTO_INCREMENT,
 `company_Name` VARCHAR(45) NOT NULL,
 `company_hub_ID` INT(11) NOT NULL,
 PRIMARY KEY (`company_ID`),
 INDEX `fk_company_hub1` (`company_hub_ID`)
 )

Persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
  <persistence-unit name="testJPAPU" transaction-type="RESOURCE_LOCAL">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <class>testjpa.Hub</class>
    <class>testjpa.Company</class>
    <properties>
      <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/logistikumdispatch"/>
      <property name="javax.persistence.jdbc.password" value="rootroot"/>
      <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
      <property name="javax.persistence.jdbc.user" value="root"/>
    </properties>
  </persistence-unit>
</persistence>

Hub.java(项目中的实体类)

    /*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package testjpa;

import java.io.Serializable;
import java.util.Collection;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;

/**
 *
 * @author Anjum
 */
@Entity
@Table(name = "hub")
@NamedQueries({
    @NamedQuery(name = "Hub.findAll", query = "SELECT h FROM Hub h"),
    @NamedQuery(name = "Hub.findByHubID", query = "SELECT h FROM Hub h WHERE h.hubID = :hubID"),
    @NamedQuery(name = "Hub.findByHubName", query = "SELECT h FROM Hub h WHERE h.hubName = :hubName")})
public class Hub implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "hub_ID")
    private Integer hubID;
    @Basic(optional = false)
    @Column(name = "hub_Name")
    private String hubName;

    @OneToMany(mappedBy="hub", cascade=CascadeType.ALL)
    @JoinColumn(name="company_hub_ID")
    private Collection<Company> companies;

    public Hub() {
    }

    public Hub(Integer hubID) {
        this.hubID = hubID;
    }

    public Hub(Integer hubID, String hubName) {
        this.hubID = hubID;
        this.hubName = hubName;
    }

    public Integer getHubID() {
        return hubID;
    }

    public void setHubID(Integer hubID) {
        this.hubID = hubID;
    }

    public String getHubName() {
        return hubName;
    }

    public void setHubName(String hubName) {
        this.hubName = hubName;
    }

    public Collection<Company> getCompanies()
    {
        return companies;
    }

    public void setCompanies(Collection<Company> companies)
    {
        this.companies = companies;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (hubID != null ? hubID.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof Hub)) {
            return false;
        }
        Hub other = (Hub) object;
        if ((this.hubID == null && other.hubID != null) || (this.hubID != null && !this.hubID.equals(other.hubID))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "testjpa.Hub[hubID=" + hubID + "]";
    }

}

Company.java(项目中的实体类)

package testjpa;

import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;

/**
 *
 * @author Anjum
 */
@Entity
@Table(name = "company")
@NamedQueries({
    @NamedQuery(name = "Company.findAll", query = "SELECT c FROM Company c"),
    @NamedQuery(name = "Company.findByCompanyID", query = "SELECT c FROM Company c WHERE c.companyID = :companyID"),
    @NamedQuery(name = "Company.findByCompanyName", query = "SELECT c FROM Company c WHERE c.companyName = :companyName"),
    @NamedQuery(name = "Company.findByCompanyhubID", query = "SELECT c FROM Company c WHERE c.companyhubID = :companyhubID")})
public class Company implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "company_ID")
    private Integer companyID;
    @Basic(optional = false)
    @Column(name = "company_Name")
    private String companyName;
    @Basic(optional = false)
    @Column(name = "company_hub_ID")
    private int companyhubID;
    @ManyToOne()
    @JoinColumn(name="hub_ID", insertable=false, updatable=false)
    private Hub hub;
    public Company() {
    }

    public Company(Integer companyID) {
        this.companyID = companyID;
    }

    public Company(Integer companyID, String companyName, int companyhubID) {
        this.companyID = companyID;
        this.companyName = companyName;
        this.companyhubID = companyhubID;
    }

    public Integer getCompanyID() {
        return companyID;
    }

    public void setCompanyID(Integer companyID) {
        this.companyID = companyID;
    }

    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

    public int getCompanyhubID() {
        return companyhubID;
    }

    public void setCompanyhubID(int companyhubID) {
        this.companyhubID = companyhubID;
    }

    public Hub getHub()
    {
        return hub;
    }

    public void setHub(Hub hub)
    {
        this.hub = hub;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (companyID != null ? companyID.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof Company)) {
            return false;
        }
        Company other = (Company) object;
        if ((this.companyID == null && other.companyID != null) || (this.companyID != null && !this.companyID.equals(other.companyID))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "testjpa.Company[companyID=" + companyID + "]";
    }

}

主程序

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package testjpa;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

/**
 *
 * @author Anjum
 */
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("testJPAPU");
        EntityManager em = emf.createEntityManager();

        em.getTransaction().begin();

        Company c1 = new Company();
        c1.setCompanyName("ays");


        Hub h1 = new Hub();
        h1.setHubName("abc");
        em.persist(h1);
        c1.setHub(h1);

        em.persist(c1);
        em.flush();

    }
}

输出

[EL Info]: 2010-09-03 14:39:02.639--ServerSession(3199106)--EclipseLink, version: Eclipse Persistence Services - 2.0.2.v20100323-r6872
[EL Info]: 2010-09-03 14:39:03.371--ServerSession(3199106)--file:/C:/Users/Anjum/Documents/NetBeansProjects/testJPA/build/classes/_testJPAPU login successful

问题

我的问题是,外键 company.company_hub_ID 在数据库中更新为 comapany.company_name = "ays"。但是 company.company_hub_ID 中总是有 0。

Hub 也已正确更新。表示没有连接或 JPA 问题。问题只出在关系中。

提前感谢您帮助我。

【问题讨论】:

    标签: java orm jpa persistence foreign-keys


    【解决方案1】:

    如前所述,您不应该在 OneToMany / ManyToOne 关联的两侧使用JoinColumn,而只能在拥有方(多方)使用。从OneToMany 一侧删除它并修复列名。

    您也不应该在Company 实体中拥有一个companyhubID 属性来保存外键(这些东西实际上应该隐藏在对象级别)。从JoinColumn 中删除属性和insertable=false, updatable=false

    所以映射应该是:

    @Entity
    ...
    public class Hub implements Serializable {
        private static final long serialVersionUID = 1L;
        @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Basic(optional = false)
        @Column(name = "hub_ID")
        private Integer hubID;
        @Basic(optional = false)
        @Column(name = "hub_Name")
        private String hubName;
    
        @OneToMany(mappedBy="hub", cascade=CascadeType.ALL)
    //    @JoinColumn(name="company_hub_ID") // THIS IS WRONG
        private Collection companies = new HashSet()
        ...
        // method to set both sides of the link of the bidirectional association
        public void addToCompanies(Company company) {
            company.setHub(this);
            this.companies.add(company);
        }
    }

    @Entity
    ...
    public class Company implements Serializable {
        private static final long serialVersionUID = 1L;
        @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Basic(optional = false)
        @Column(name = "company_ID")
        private Integer companyID;
        @Basic(optional = false)
        @Column(name = "company_Name")
        private String companyName;
    //    @Basic(optional = false) 
    //    @Column(name = "company_hub_ID")
    //    private int companyhubID;          // NO, DON'T
    
        @ManyToOne()
        @JoinColumn(name="company_hub_ID"/*, insertable=false, updatable=false*/)
        private Hub hub;
        ...
    }

    一些补充说明:

    • 使用IDENTITY 策略生成ID 很好。
    • 您应该使用 InnoDB 表来实现引用完整性。

    另请注意,您的演示代码不正确,您没有正确设置关联。应该是:

    Company c1 = new Company();
    c1.setCompanyName("ays");
    Hub h1 = new Hub();
    h1.setHubName("abc");
    h1.addToCompanies.add(c1);  // set the bidirectional association
    
    em.persist(h1);             // JPA will cascade the persist
    em.flush();
    

    【讨论】:

      【解决方案2】:

      在您的映射和数据库架构之间没有对齐。数据库模式是由 JPA 提供者生成的吗?

      映射在公司类上定义了一个 hub_id 连接列,但数据库架构缺少该列(它具有“company_hub_ID”)。

      尝试以下方法:

      从@OneToMany 一侧删除@JoinColumn。 @OneToMany+@JoinColumn 仅用于在外键(没有连接表)上映射单向单机关联。您的关联是双向的单机,因此 @OneToMany 与 mappedBy 在反面,@ManyToOne + @JoinColumn (可选)在拥有方。不要将可插入/可更新设置为 false。

      【讨论】:

      • 感谢 romanb 的回复。根据您的建议,我收到以下错误。异常描述:字段 [company.company_hub_ID] 存在多个可写映射。只有一个可以定义为可写,所有其他必须指定为只读。映射:org.eclipse.persistence.mappings.OneToOneMapping[hub] 描述符:RelationalDescriptor(testjpa.Company --> [DatabaseTable(company)])
      【解决方案3】:

      我同意romanb 的建议。

      由于关联是双向的,因此请确保按照post 中的说明设置“链接的两侧”。

      尝试像这样更改您的代码:

      public class Hub {
          ...
          @OneToMany(mappedBy="hub", cascade=CascadeType.ALL)
          private Collection<Company> companies = new ArrayList<Company>();
      
          ...
          public void addCompany(Company company) {
              company.setHub(this);
              this.companies.add(company);
          }       
          ...
      }
      
      public class Company {
          ...         
          @ManyToOne
          @JoinColumn(name="company_hub_ID")
          private Hub hub;    
          ...
      }
      
      public class Main {
      
          public static void Main(String[] args) {
              ...
              Company c1 = new Company();
              c1.setCompanyName("ays");
      
              Hub h1 = new Hub();
              h1.setHubName("abc");
              h1.addCompany(c1);
              em.persist(h1);
          }
      
      }
      

      此外,请使用 GenerationType.IDENTITY 以外的 ID 生成策略,因为在插入发生之前无法访问标识符。

      【讨论】:

        【解决方案4】:

        在 romanB 的支持下,我终于解决了这个问题。

        感谢您对“ROMANB”的帮助。

        看看我是怎么解决这个问题的,你可以使用这个技术在netbeans中实现外键的持久化。

        public class Company implements Serializable {
            private static final long serialVersionUID = 1L;
            @Id
            @GeneratedValue(strategy = GenerationType.IDENTITY)
            @Basic(optional = false)
            @Column(name = "company_ID", nullable=true)
            private Integer companyID;
            @Basic(optional = false)
            @Column(name = "company_Name")
            private String companyName;
            @Basic(optional = false)
            @Column(name = "company_hub_ID", nullable=false, updatable=false, insertable=false)
            private int companyhubID;
            @ManyToOne(cascade=CascadeType.ALL)
            @JoinColumn(name="company_hub_ID")
            private Hub hub_ref;
        ..........
        ..........
        
        public class Hub implements Serializable {
            private static final long serialVersionUID = 1L;
            @Id
            @GeneratedValue(strategy = GenerationType.IDENTITY)
            @Basic(optional = false)
            @Column(name = "hub_ID")
            private Integer hubID;
            @Basic(optional = false)
            @Column(name = "hub_Name")
            private String hubName;
        
            @OneToMany(mappedBy="hub_ref", cascade=CascadeType.ALL)
            private Collection<Company> companies;
        
            ................
            ................
          \\ in Hub class
        public void addCompany(Company company)
            {
                if(this.companies == null)
                {
                    this.companies = new ArrayList<Company>();
                }
                this.companies.add(company);
                company.setHub(this);
            }
        

        最后是主类

         public static void main(String[] args) {
                EntityManagerFactory emf = Persistence.createEntityManagerFactory("testJPAPU");
                EntityManager em = emf.createEntityManager();
        
        
        
                Company c1 = new Company();
                c1.setCompanyName("ays");
        
                Hub h1 = new Hub();
                h1.setHubName("abc");
        
                em.getTransaction().begin();
        
                h1.addCompany(c1);
                em.persist(h1);
        
                em.flush();
        
            }
        

        使用 eclipse link 2.0 获得 JPA 2.0 支持。

        再次感谢,感谢堆栈溢出。

        Anjum Shrimali.

        【讨论】:

        • 不,你不应该有一个 private int companyhubID; 属性来代表 FK,正如我在回答中所写的那样。 JPA 的全部意义在于抽象,数据库内容不应该在对象模型中泄漏。所以不,您不应该使用这种“在 NetBeans 中使用外键实现持久性的技术”。
        猜你喜欢
        • 2011-07-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多