web-dev-qa-db-ja.com

JPA 2.0多対多、追加の列

JPA 2.0(JBoss 7.1.1)でManyToManyリレーションシップを実行しようとしていますが、リレーションシップには次のような追加の列(太字、下)があります。

Employer           EmployerDeliveryAgent             DeliveryAgent
(id,...)   (employer_id, deliveryAgent_id, **ref**)  (id,...)

重複する属性を持ちたくないので、 http://giannigar.wordpress.com/2009/09/04/mapping-a-many-to-manyで提示されている2番目のソリューションを適用したいと思います。 -join-table-with-extra-column-using-jpa / 。しかし、私はそれを動作させることができません、私は次のようないくつかのエラーを受け取ります:

  1. 埋め込みIDクラスには、リレーションシップマッピングを含めるべきではありません(実際、仕様ではそうなっています)。
  2. 属性「employerDeliveryAgent」では、「マップ先」の値「pk.deliveryAgent」をターゲットエンティティの属性に解決できません。
  3. 属性「employerDeliveryAgent」では、「マップ先」の値「pk.employer」をターゲットエンティティの属性に解決できません。
  4. オーバーライド属性「pk.deliveryAgent」の永続的なタイプは解決できません。
  5. オーバーライド属性「pk.employer」の永続的なタイプは解決できません。

そのリンク上の多くの人々はそれがうまく働いたと言ったので、私はおそらく環境、JPAまたはHibernateバージョンで何かが違うと思います。私の質問は次のとおりです:JPA 2.0(Jboss 7.1.1/JPA実装としてHibernateを使用)でこのようなシナリオを達成するにはどうすればよいですか?そして、その質問を補完するために:複合キーの使用を避け、代わりに単純に生成されたIDと一意の制約を使用する必要がありますか?

前もって感謝します。

Obs .:私はここでソースコードをコピーしませんでした。なぜなら、それは本質的に上記のリンクにあるもののコピーであり、異なるクラスと属性名を持っているからです。

18
Renan

まず、複数のPKがあるため、EmployerDeliveryAgentPKクラスを生成する必要があります。

@Embeddable
public class EmployerDeliveryAgentPK implements Serializable {

    @Column(name = "EMPLOYER_ID")
    private Long employer_id;

     @Column(name = "DELIVERY_AGENT_ID")
    private Long deliveryAgent_id;
}

次に、EmployerDeliveryAgentクラスを作成する必要があります。このクラスは、EmployerおよびDeliveryAgentの多対多のテーブルを表します。

@Entity
@Table(name = " EmployerDeliveryAgent")
public class EmployerDeliveryAgent implements Serializable {

   @EmbeddedId
    private EmployerDeliveryAgentPK id;

   @ManyToOne
    @MapsId("employer_id") //This is the name of attr in EmployerDeliveryAgentPK class
    @JoinColumn(name = "EMPLOYER_ID")
    private Employer employer;

    @ManyToOne
    @MapsId("deliveryAgent_id")
    @JoinColumn(name = "DELIVERY_AGENT_ID")
    private DeliveryAgent deliveryAgent;    
}

その後、Employerクラスに以下を追加する必要があります。

@OneToMany(mappedBy = "deliveryAgent")
    private Set<EmployerDeliveryAgent> employerDeliveryAgent = new HashSet<EmployerDeliveryAgent>();

そして、DeliveryAgentクラスに以下を追加する必要があります:

@OneToMany(mappedBy = "employer")
        private Set<EmployerDeliveryAgent> employer = new HashSet<EmployerDeliveryAgent>();

これですべてです!幸運を!!

36
Erik Lucio

Eric Lucio および Renan の両方の回答が役立ちましたが、アソシエーションテーブルでのIDの使用は冗長です。関連するエンティティの両方のIDがクラスにあります。これは必須ではありません。関連クラスの関連エンティティを、関連エンティティフィールドの@Idに簡単にマッピングできます。

@Entity
public class Employer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @OneToMany(mappedBy = "employer")
    private List<EmployerDeliveryAgent> deliveryAgentAssoc;

    // other properties and getters and setters
}

@Entity
public class DeliveryAgent {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @OneToMany(mappedBy = "deliveryAgent")
    private List<EmployerDeliveryAgent> employerAssoc;

    // other properties and getters and setters
}

関連クラス

@Entity
@Table(name = "employer_delivery_agent")
@IdClass(EmployerDeliveryAgentId.class)
public class EmployerDeliveryAgent {

    @Id
    @ManyToOne
    @JoinColumn(name = "employer_id", referencedColumnName = "id")
    private Employer employer;

    @Id
    @ManyToOne
    @JoinColumn(name = "delivery_agent_id", referencedColumnName = "id")
    private DeliveryAgent deliveryAgent;

    @Column(name = "is_project_lead")
    private boolean isProjectLead;
}

まだ関連付けPKクラスが必要です。フィールド名は関連クラスのフィールド名に正確に対応する必要がありますが、タイプは関連するタイプのIDのタイプである必要があります。

public class EmployerDeliveryAgentId implements Serializable {

    private int employer;
    private int deliveryAgent;

    // getters/setters and most importantly equals() and hashCode()
}
38
Paul Samsotha

OK、利用可能なソリューションに基づいて動作しました

http://en.wikibooks.org/wiki/Java_Persistence/ManyToMany#Mapping_a_Join_Table_with_Additional_Columns

このソリューションは、データベースに重複属性を生成しませんが、JPAエンティティに重複属性を生成します(余分な作業をコンストラクターまたはメソッドに中継できるため、これは非常に受け入れられます-透過的になります)。データベースで生成される主キーと外部キーは100%正しいです。

リンクに記載されているように、@ PrimaryKeyJoinColumnを使用できず、代わりに@JoinColumn(name = "projectId"、updatable = false、insertable = false、referencedColumnName = "id")を使用しました。言及する価値のある別のこと:EntityManager.persist(association)を使用する必要がありましたが、リンクの例にはありません。

私の最終的な解決策は次のとおりです。

@Entity
public class Employee {
  @Id
  private long id;
  ...
  @OneToMany(mappedBy="employee")
  private List<ProjectAssociation> projects;
  ...
}
@Entity
public class Project {

  @PersistenceContext
  EntityManager em;

  @Id
  private long id;
  ...
  @OneToMany(mappedBy="project")
  private List<ProjectAssociation> employees;
  ...
  // Add an employee to the project.
  // Create an association object for the relationship and set its data.
  public void addEmployee(Employee employee, boolean teamLead) {
    ProjectAssociation association = new ProjectAssociation();
    association.setEmployee(employee);
    association.setProject(this);
    association.setEmployeeId(employee.getId());
    association.setProjectId(this.getId());
    association.setIsTeamLead(teamLead);
    em.persist(association);

    this.employees.add(association);
    // Also add the association object to the employee.
    employee.getProjects().add(association);
  }
}
@Entity
@Table(name="PROJ_EMP")
@IdClass(ProjectAssociationId.class)
public class ProjectAssociation {
  @Id
  private long employeeId;
  @Id
  private long projectId;
  @Column(name="IS_PROJECT_LEAD")
  private boolean isProjectLead;
  @ManyToOne
  @JoinColumn(name = "employeeId", updatable = false, insertable = false,
          referencedColumnName = "id")

  private Employee employee;
  @ManyToOne
  @JoinColumn(name = "projectId", updatable = false, insertable = false,
          referencedColumnName = "id")

  private Project project;
  ...
}
public class ProjectAssociationId implements Serializable {

  private long employeeId;

  private long projectId;
  ...

  public int hashCode() {
    return (int)(employeeId + projectId);
  }

  public boolean equals(Object object) {
    if (object instanceof ProjectAssociationId) {
      ProjectAssociationId otherId = (ProjectAssociationId) object;
      return (otherId.employeeId == this.employeeId) 
              && (otherId.projectId == this.projectId);
    }
    return false;
  }

}
10
Renan