如何使用Hibernate映射复合键?

kaa*_*aaf 189 java orm hibernate hql composite-key

在这段代码中,如何为复合键生成Java类(如何在hibernate中复合键):

create table Time (
     levelStation int(15) not null,
     src varchar(100) not null,
     dst varchar(100) not null,
     distance int(15) not null,
     price int(15) not null,
     confPathID int(15) not null,
     constraint ConfPath_fk foreign key(confPathID) references ConfPath(confPathID),
     primary key (levelStation, confPathID)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Run Code Online (Sandbox Code Playgroud)

Pas*_*ent 401

要映射组合键,你可以使用EmbeddedId IdClass注解.我知道这个问题并不严格关于JPA,但规范定义的规则也适用.所以他们在这里:

2.1.4主键和实体标识

...

复合主键必须对应于单个持久字段或属性,或者对应于下面描述的一组此类字段或属性.必须定义主键类以表示复合主键.当数据库键由多个列组成时,从旧数据库映射时,通常会出现复合主键.EmbeddedIdIdClass注解用于表示复合主键.见9.1.14和9.1.15.

...

以下规则适用于复合主键:

  • 主键类必须是公共的,并且必须具有公共的无参数构造函数.
  • 如果使用基于属性的访问,则主键类的属性必须是公共的或受保护的.
  • 主键类必须是serializable.
  • 主键类必须定义equalshashCode 方法.这些方法的值相等的语义必须与键映射到的数据库类型的数据库相等一致.
  • 复合主键必须表示并映射为可嵌入类(请参见第9.1.14节"EmbeddedId注释"),或者必须表示并映射到实体类的多个字段或属性(请参见第9.1.15节"IdClass")注解").
  • 如果复合主键类映射到实体类的多个字段或属性,则主键类中的主键字段或属性的名称与实体类的名称必须对应,并且它们的类型必须相同.

IdClass

复合主键的类可能看起来像(可能是静态内部类):

public class TimePK implements Serializable {
    protected Integer levelStation;
    protected Integer confPathID;

    public TimePK() {}

    public TimePK(Integer levelStation, Integer confPathID) {
        this.levelStation = levelStation;
        this.confPathID = confPathID;
    }
    // equals, hashCode
}
Run Code Online (Sandbox Code Playgroud)

而实体:

@Entity
@IdClass(TimePK.class)
class Time implements Serializable {
    @Id
    private Integer levelStation;
    @Id
    private Integer confPathID;

    private String src;
    private String dst;
    private Integer distance;
    private Integer price;

    // getters, setters
}
Run Code Online (Sandbox Code Playgroud)

IdClass注释映射多个字段的表PK.

EmbeddedId

复合主键的类可能看起来像(可能是静态内部类):

@Embeddable
public class TimePK implements Serializable {
    protected Integer levelStation;
    protected Integer confPathID;

    public TimePK() {}

    public TimePK(Integer levelStation, Integer confPathID) {
        this.levelStation = levelStation;
        this.confPathID = confPathID;
    }
    // equals, hashCode
}
Run Code Online (Sandbox Code Playgroud)

而实体:

@Entity
class Time implements Serializable {
    @EmbeddedId
    private TimePK timePK;

    private String src;
    private String dst;
    private Integer distance;
    private Integer price;

    //...
}
Run Code Online (Sandbox Code Playgroud)

@EmbeddedId注解映射一个PK类表PK.

区别:

  • 从物理模型的角度来看,没有差异
  • @EmbeddedId以某种方式更清楚地传达密钥是一个复合密钥,当组合的pk本身是一个有意义的实体或在代码中重用时,IMO 是有意义的.
  • @IdClass 用于指定某些字段组合是唯一的,但这些字段组合没有特殊含义.

它们也会影响您编写查询的方式(使它们或多或少冗长):

参考

  • JPA 1.0规范
    • 第2.1.4节"主键和实体标识"
    • 第9.1.14节"EmbeddedId注释"
    • 第9.1.15节"IdClass注释"

  • 还有一个特定于Hibernate的解决方案:将多个属性映射为@Id属性_without_,将外部类声明为标识符类型(并使用IdClass注释).见[5.1.2.1.Hibernate手册中的复合标识符(http://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html_single/#mapping-declaration-id). (15认同)

Thi*_*Roy 47

你需要使用@EmbeddedId:

@Entity
class Time {
    @EmbeddedId
    TimeId id;

    String src;
    String dst;
    Integer distance;
    Integer price;
}

@Embeddable
class TimeId implements Serializable {
    Integer levelStation;
    Integer confPathID;
}
Run Code Online (Sandbox Code Playgroud)


Vla*_*cea 13

正如我在本文中所解释的,假设您有以下数据库表:

在此输入图像描述

首先,您需要创建@Embeddable保存复合标识符:

@Embeddable
public class EmployeeId implements Serializable {

    @Column(name = "company_id")
    private Long companyId;

    @Column(name = "employee_number")
    private Long employeeNumber;

    public EmployeeId() {
    }

    public EmployeeId(Long companyId, Long employeeId) {
        this.companyId = companyId;
        this.employeeNumber = employeeId;
    }

    public Long getCompanyId() {
        return companyId;
    }

    public Long getEmployeeNumber() {
        return employeeNumber;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof EmployeeId)) return false;
        EmployeeId that = (EmployeeId) o;
        return Objects.equals(getCompanyId(), that.getCompanyId()) &&
                Objects.equals(getEmployeeNumber(), that.getEmployeeNumber());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getCompanyId(), getEmployeeNumber());
    }
}
Run Code Online (Sandbox Code Playgroud)

有了这个,我们可以Employee通过注释它来映射使用复合标识符的实体@EmbeddedId:

@Entity(name = "Employee")
@Table(name = "employee")
public class Employee {

    @EmbeddedId
    private EmployeeId id;

    private String name;

    public EmployeeId getId() {
        return id;
    }

    public void setId(EmployeeId id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
Run Code Online (Sandbox Code Playgroud)

Phone具有@ManyToOne关联的实体Employee需要通过两个@JoinColumn映射从父类引用复合标识符:

@Entity(name = "Phone")
@Table(name = "phone")
public class Phone {

    @Id
    @Column(name = "`number`")
    private String number;

    @ManyToOne
    @JoinColumns({
        @JoinColumn(
            name = "company_id",
            referencedColumnName = "company_id"),
        @JoinColumn(
            name = "employee_number",
            referencedColumnName = "employee_number")
    })
    private Employee employee;

    public Employee getEmployee() {
        return employee;
    }

    public void setEmployee(Employee employee) {
        this.employee = employee;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }
}
Run Code Online (Sandbox Code Playgroud)

有关更多详细信息,请查看此文章.


jav*_*csw 6

看起来你是从头开始这样做的.尝试使用可用的逆向工程工具,如Netbeans Entities from Database,至少可以自动完成基础知识(如嵌入式ID).如果你有很多桌子,这可能会成为一个巨大的问题.我建议避免重新发明轮子并使用尽可能多的工具来将编码减少到最小和最重要的部分,你打算做什么.


Myk*_*ych 6

主键类必须定义equals和hashCode方法

  1. 实现equals时,您应该使用instanceof来允许与子类进行比较.如果Hibernate延迟加载一对一或多对一关系,您将拥有该类的代理而不是普通类.代理是子类.比较类名会失败.
    更技术上:您应该遵循Liskows替换原则并忽略对称性.
  2. 下一个陷阱是使用name.equals(that.name)而不是name.equals(that.getName()).如果是代理,第一个将失败.

http://www.laliluna.de/jpa-hibernate-guide/ch06s06.html


din*_*pal 5

让我们举一个简单的例子。假设有两个名为的表testcustomer其中描述如下:

create table test(
  test_id int(11) not null auto_increment,
  primary key(test_id));

create table customer(
  customer_id int(11) not null auto_increment,
  name varchar(50) not null,
  primary key(customer_id));
Run Code Online (Sandbox Code Playgroud)

那里还有一张表,它记录tests和customer

create table tests_purchased(
  customer_id int(11) not null,
  test_id int(11) not null,
  created_date datetime not null,
  primary key(customer_id, test_id));
Run Code Online (Sandbox Code Playgroud)

我们可以看到在表tests_purchased中主键是组合键,因此我们将<composite-id ...>...</composite-id>hbm.xml映射文件中使用标记。因此,PurchasedTest.hbm.xml将如下所示:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
  "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
  <class name="entities.PurchasedTest" table="tests_purchased">

    <composite-id name="purchasedTestId">
      <key-property name="testId" column="TEST_ID" />
      <key-property name="customerId" column="CUSTOMER_ID" />
    </composite-id>

    <property name="purchaseDate" type="timestamp">
      <column name="created_date" />
    </property>

  </class>
</hibernate-mapping>
Run Code Online (Sandbox Code Playgroud)

但这并没有到此结束。在Hibernate中,我们使用session.load(entityClassid_type_object)通过主键查找和加载实体。如果是复合键,则ID对象应该是一个单独的ID类(在上面的情况下是一个PurchasedTestId类),它仅声明主键属性,如下所示

import java.io.Serializable;

public class PurchasedTestId implements Serializable {
  private Long testId;
  private Long customerId;

  // an easy initializing constructor
  public PurchasedTestId(Long testId, Long customerId) {
    this.testId = testId;
    this.customerId = customerId;
  }

  public Long getTestId() {
    return testId;
  }

  public void setTestId(Long testId) {
    this.testId = testId;
  }

  public Long getCustomerId() {
    return customerId;
  }

  public void setCustomerId(Long customerId) {
    this.customerId = customerId;
  }

  @Override
  public boolean equals(Object arg0) {
    if(arg0 == null) return false;
    if(!(arg0 instanceof PurchasedTestId)) return false;
    PurchasedTestId arg1 = (PurchasedTestId) arg0;
    return (this.testId.longValue() == arg1.getTestId().longValue()) &&
           (this.customerId.longValue() == arg1.getCustomerId().longValue());
  }

  @Override
  public int hashCode() {
    int hsCode;
    hsCode = testId.hashCode();
    hsCode = 19 * hsCode+ customerId.hashCode();
    return hsCode;
  }
}
Run Code Online (Sandbox Code Playgroud)

重要的一点是,我们还实现了这两个功能hashCode()equals()并且Hibernate也依赖于这两个功能。