이 코드에서 복합 키에 대한 Java 클래스를 생성하는 방법 (최대 절전 모드에서 키를 복합화하는 방법) :
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;
답변
복합 키를 매핑하려면, 당신은 사용할 수 있습니다 EmbeddedId
또는 을IdClass
주석을. 나는이 질문이 JPA에 관한 것이 아니라 사양에 의해 정의 된 규칙도 적용한다는 것을 알고 있습니다. 그래서 여기 있습니다 :
기본 키 및 엔터티 ID
…
복합 기본 키는 단일 영구 필드 또는 특성 또는 아래 설명 된 해당 필드 또는 특성 세트에 해당해야합니다. 복합 기본 키를 나타내도록 기본 키 클래스를 정의해야합니다. 복합 기본 키는 일반적으로 데이터베이스 키가 여러 열로 구성된 경우 레거시 데이터베이스에서 매핑 할 때 발생합니다. 및
주석은 복합 기본 키를 나타 내기 위해 사용된다. 섹션 9.1.14 및 9.1.15를 참조하십시오.EmbeddedId
IdClass
…
복합 기본 키에는 다음 규칙이 적용됩니다.
- 기본 키 클래스는 공개이어야하며 인수없는 공개 생성자가 있어야합니다.
- 특성 기반 액세스가 사용되는 경우 기본 키 클래스의 특성은 공용 또는 보호되어야합니다.
- 기본 키 클래스는이어야합니다
serializable
.- 기본 키 클래스는
메소드equals
와hashCode
메소드 를 정의해야합니다 . 이러한 메소드에 대한 값 평등의 의미는 키가 맵핑되는 데이터베이스 유형에 대한 데이터베이스 평등과 일치해야합니다.- 복합 기본 키는 포함 가능한 클래스로 표현 및 매핑되어야하거나 (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
}
그리고 엔티티 :
@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
}
그만큼 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
}
그리고 엔티티 :
@Entity
class Time implements Serializable {
@EmbeddedId
private TimePK timePK;
private String src;
private String dst;
private Integer distance;
private Integer price;
//...
}
그만큼 @EmbeddedId
주석 테이블 PK에 PK 클래스를 매핑합니다.
차이점 :
- 실제 모델 관점에서 차이는 없습니다
@EmbeddedId
어떤 식 으로든 키가 복합 키이고 , 결합 된 pk가 의미있는 엔티티 자체이거나 코드에서 재사용 될 때 IMO가 의미가 있음을보다 명확하게 전달 합니다 .@IdClass
일부 필드 조합이 고유하지만 특별한 의미가 없음을 지정하는 데 유용합니다. .
또한 쿼리 작성 방식에 영향을 미칩니다 (약간의 장황한 표현).
-
와
IdClass
select t.levelStation from Time t
-
와
EmbeddedId
select t.timePK.levelStation from Time t
참고 문헌
- JPA 1.0 사양
- 섹션 2.1.4 “기본 키 및 엔티티 ID”
- 9.1.14 절 “내장 된 주석 어노테이션”
- 9.1.15 절. “IdClass 주석”
답변
당신이 사용해야합니다 @EmbeddedId
:
@Entity
class Time {
@EmbeddedId
TimeId id;
String src;
String dst;
Integer distance;
Integer price;
}
@Embeddable
class TimeId implements Serializable {
Integer levelStation;
Integer confPathID;
}
답변
이 기사 에서 설명했듯이 다음 데이터베이스 테이블이 있다고 가정합니다.
먼저 @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());
}
}
이를 통해 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;
}
}
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;
}
}
답변
기본 키 클래스는 equals 및 hashCode 메소드를 정의해야합니다.
- equals를 구현할 때 instanceof 를 사용하여 서브 클래스와 비교할 수 있도록 해야 합니다 . Hibernate lazy가 일대일 또는 다 대일 관계를로드하면 일반 클래스 대신 클래스에 대한 프록시가 있습니다. 프록시는 서브 클래스입니다. 클래스 이름을 비교할 수 없습니다.
보다 기술적으로 : 당신은 Liskows 대체 원칙을 따르고 대칭성을 무시해야합니다. - 다음 함정은 name.equals (that.getName ()) 대신 name.equals (that.name) 과 같은 것을 사용하는 것 입니다. 프록시 인 경우 첫 번째는 실패합니다.
http://www.laliluna.de/jpa-hibernate-guide/ch06s06.html
답변
처음부터이 작업을 수행 한 것 같습니다. 데이터베이스의 Netbeans Entities와 같은 사용 가능한 리버스 엔지니어링 도구를 사용하여 최소한 기본 사항 (예 : 임베디드 ID)을 자동화하십시오. 테이블이 많으면 큰 두통이 될 수 있습니다. 휠을 재발 명하지 말고 가능한 한 많은 도구를 사용하여 코딩을 최소 및 가장 중요한 부분으로 줄이십시오.
답변
간단한 예를 들어 보자. 하자 두 테이블라는 말을 test
하고 customer
거기에 설명되어 있습니다 :
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));
test
s 및를 추적하는 테이블이 하나 더 있습니다 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));
테이블 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>
그러나 여기서 끝나지 않습니다. Hibernate에서는 session.load ( entityClass
, id_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;
}
}
중요한 점은 우리는 또한 두 가지 기능을 구현한다는 것입니다 hashCode()
및 equals()
Hibernate가 그것들에 의존한다.
답변
다른 옵션은 ConfPath 테이블에서 복합 요소의 맵으로 맵핑하는 것입니다.
이 매핑은 (ConfPathID, levelStation)에 대한 인덱스의 이점이 있습니다.
public class ConfPath {
private Map<Long,Time> timeForLevelStation = new HashMap<Long,Time>();
public Time getTime(long levelStation) {
return timeForLevelStation.get(levelStation);
}
public void putTime(long levelStation, Time newValue) {
timeForLevelStation.put(levelStation, newValue);
}
}
public class Time {
String src;
String dst;
long distance;
long price;
public long getDistance() {
return distance;
}
public void setDistance(long distance) {
this.distance = distance;
}
public String getDst() {
return dst;
}
public void setDst(String dst) {
this.dst = dst;
}
public long getPrice() {
return price;
}
public void setPrice(long price) {
this.price = price;
}
public String getSrc() {
return src;
}
public void setSrc(String src) {
this.src = src;
}
}
매핑 :
<class name="ConfPath" table="ConfPath">
<id column="ID" name="id">
<generator class="native"/>
</id>
<map cascade="all-delete-orphan" name="values" table="example"
lazy="extra">
<key column="ConfPathID"/>
<map-key type="long" column="levelStation"/>
<composite-element class="Time">
<property name="src" column="src" type="string" length="100"/>
<property name="dst" column="dst" type="string" length="100"/>
<property name="distance" column="distance"/>
<property name="price" column="price"/>
</composite-element>
</map>
</class>