programing

컴포지트 키에 JPA 및 휴지 상태를 매핑하는 방법

copysource 2022. 7. 16. 13:19
반응형

컴포지트 키에 JPA 및 휴지 상태를 매핑하는 방법

이 코드에서는 컴포지트 키에 대한 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에 관한 것은 아니지만 사양에 의해 정의된 규칙도 적용됩니다.음음음같 뭇매하다

2.1.4 프라이머리 키와 엔티티 아이덴티티 아이덴티티

...

복합 기본 키는 단일 영구 필드 또는 속성 또는 아래에 설명된 필드 또는 속성 집합에 대응해야 합니다.프라이머리 키클래스는 복합 프라이머리 키를 나타내도록 정의해야 합니다.복합 기본 키는 일반적으로 데이터베이스 키가 여러 열로 구성되어 있을 때 레거시 데이터베이스에서 매핑할 때 발생합니다.및 주석은 복합 기본 키를 나타내기 위해 사용됩니다.섹션 9.1.14 및 9.1.15를 참조하십시오.

...

복합 프라이머리 키에는 다음 규칙이 적용됩니다.

  • 기본 키 클래스는 공용이어야 하며 공용 no-arg 생성자가 있어야 합니다.
  • 속성 기반 액세스가 사용되는 경우 기본 키 클래스의 속성이 공개 또는 보호되어야 합니다.
  • 기본 키 클래스는 다음과 같아야 합니다.serializable.
  • 프라이머리 키클래스는 및 메서드를 정의해야 합니다.이러한 메서드의 값 균등성의 의미는 키가 매핑되는 데이터베이스 유형의 데이터베이스 균등성과 일치해야 합니다.
  • 복합 기본 키는 임베디드 가능한 클래스로 표현 및 매핑되거나(섹션 9.1.14 "EmbeddedId 주석" 참조), 엔티티 클래스의 여러 필드 또는 속성에 표현 및 매핑되어야 합니다(섹션 9.1.15 "IdClass 주석" 참조).
  • 복합 프라이머리 키클래스가 엔티티 클래스의 여러 필드 또는 속성에 매핑되어 있는 경우 프라이머리 키클래스와 엔티티 클래스의 프라이머리 키필드 또는 속성의 이름이 일치하고 유형이 같아야 합니다.

를용 an with with with withIdClass

복합 프라이머리 키의 클래스는 다음과 같습니다(스태틱 내부 클래스일 수 있습니다).

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
}

IdClassPK를 사용하다

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

    //...
}

@EmbeddedIdPKPKPKPKPKPK.

차이점:

  • 물리적 모델의 관점에서 보면 차이가 없습니다.
  • @EmbeddedId조합된 pk가 의미 있는 엔티티 자체이거나 코드에서 재사용된 경우 가 복합 키이며 IMO가 의미가 있음을 더 명확하게 알 수 있습니다.
  • @IdClass 는 필드의 일부 조합이 고유하지만 특별한 의미는 없음을 지정하는 데 도움이 됩니다.

또한 쿼리 작성 방식에도 영향을 미칩니다(조회 내용을 다소 상세하게 표시).

  • IdClass

    select t.levelStation from Time t
    
  • EmbeddedId

    select t.timePK.levelStation from Time t
    

레퍼런스

  • JPA 1.0 사 j
    • 섹션 2.1.4 "프라이머리 키와 엔티티 아이덴티티 아이덴티티"
    • 섹션 9.1.14 "EmbeddedId 주석"
    • 섹션 9.1.15 "IdClass 주석"

다음을 사용해야 합니다.

@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의 부모 클래스 2를 통해 할 필요가 @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 메서드를 정의해야 합니다.

  1. equals를 구현하는 경우 instance of를 사용하여 서브클래스와 비교할 수 있습니다.휴지 상태 지연이 1 대 1 또는 다 대 1 관계를 로드하는 경우 일반 클래스가 아닌 클래스의 프록시가 생성됩니다.프록시는 서브 클래스입니다.클래스 이름을 비교하는 것은 실패합니다.
    에 따라.리스코프
  2. 다음 함정에서는 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));

한 테이블이 더 있는데 이 테이블은 이 테이블을 추적하고 있습니다.testcustomer:

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>

하지만 여기서 끝나지 않습니다.휴지 상태에서는 session.load (entityClass,id_type_object프라이머리 키를 사용하여 엔티티를 검색하여 로드합니다.복합 키의 경우 ID 개체는 별도의 ID 클래스여야 합니다(위의 경우,PurchasedTestIdclass)는 다음과 같이 프라이머리속성을 선언합니다.

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()하이버네이트가 그들에게 의존하기 때문입니다.

hbm.xml 사용

    <composite-id>

        <!--<key-many-to-one name="productId" class="databaselayer.users.UserDB" column="user_name"/>-->
        <key-property name="productId" column="PRODUCT_Product_ID" type="int"/>
        <key-property name="categoryId" column="categories_id" type="int" />
    </composite-id>  

주석 사용

복합 키 클래스

public  class PK implements Serializable{
    private int PRODUCT_Product_ID ;    
    private int categories_id ;

    public PK(int productId, int categoryId) {
        this.PRODUCT_Product_ID = productId;
        this.categories_id = categoryId;
    }

    public int getPRODUCT_Product_ID() {
        return PRODUCT_Product_ID;
    }

    public void setPRODUCT_Product_ID(int PRODUCT_Product_ID) {
        this.PRODUCT_Product_ID = PRODUCT_Product_ID;
    }

    public int getCategories_id() {
        return categories_id;
    }

    public void setCategories_id(int categories_id) {
        this.categories_id = categories_id;
    }

    private PK() { }

    @Override
    public boolean equals(Object o) {
        if ( this == o ) {
            return true;
        }

        if ( o == null || getClass() != o.getClass() ) {
            return false;
        }

        PK pk = (PK) o;
        return Objects.equals(PRODUCT_Product_ID, pk.PRODUCT_Product_ID ) &&
                Objects.equals(categories_id, pk.categories_id );
    }

    @Override
    public int hashCode() {
        return Objects.hash(PRODUCT_Product_ID, categories_id );
    }
}

엔티티 클래스

@Entity(name = "product_category")
@IdClass( PK.class )
public  class ProductCategory implements Serializable {
    @Id    
    private int PRODUCT_Product_ID ;   

    @Id 
    private int categories_id ;

    public ProductCategory(int productId, int categoryId) {
        this.PRODUCT_Product_ID = productId ;
        this.categories_id = categoryId;
    }

    public ProductCategory() { }

    public int getPRODUCT_Product_ID() {
        return PRODUCT_Product_ID;
    }

    public void setPRODUCT_Product_ID(int PRODUCT_Product_ID) {
        this.PRODUCT_Product_ID = PRODUCT_Product_ID;
    }

    public int getCategories_id() {
        return categories_id;
    }

    public void setCategories_id(int categories_id) {
        this.categories_id = categories_id;
    }

    public void setId(PK id) {
        this.PRODUCT_Product_ID = id.getPRODUCT_Product_ID();
        this.categories_id = id.getCategories_id();
    }

    public PK getId() {
        return new PK(
            PRODUCT_Product_ID,
            categories_id
        );
    }    
}

다른 옵션은 ConfPath 테이블에서 컴포지트 요소의 맵으로 매핑하는 것입니다.

이 매핑은 (ConfPath)의 인덱스를 사용하는 것이 좋습니다.ID, 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>

언급URL : https://stackoverflow.com/questions/3585034/how-to-map-a-composite-key-with-jpa-and-hibernate

반응형