2010-09-03 26 views
6

Tôi đang cố gắng ánh xạ hai đối tượng với nhau bằng cách sử dụng liên kết ManyToMany, nhưng vì một số lý do khi tôi sử dụng thuộc tính mappedBy, hibernate dường như bị nhầm lẫn về chính xác những gì tôi đang lập bản đồ. Điều kỳ lạ duy nhất về ánh xạ của tôi ở đây là liên kết không được thực hiện trên một trường khóa chính trong một trong các mục nhập (trường này là duy nhất mặc dù).Hibernate không bằng cách thêm tên lớp đủ điều kiện vào tên thuộc tính trên liên kết ManyToMany

Các bảng là:

Sequence (
    id NUMBER, 
    reference VARCHAR, 
) 

Project (
    id NUMBER 
) 

Sequence_Project (
    proj_id number references Project(id), 
    reference varchar references Sequence(reference) 
) 

Đối tượng trông giống như (chú thích trên getter, đặt chúng trên các lĩnh vực để ngưng tụ một chút):

class Sequence { 
    @Id 
    private int id; 

    private String reference; 

    @ManyToMany(mappedBy="sequences") 
    private List<Project> projects; 
} 

Và phía bên sở hữu:

class Project { 
    @Id 
    private int id; 

    @ManyToMany 
    @JoinTable(name="sequence_project", 
       [email protected](name="id"), 
       [email protected](name="reference", 
            referencedColumnName="reference")) 
    private List<Sequence> sequences; 
} 

Điều này không thành công với MappingException:

tài sản ref [_test_local_entities_Project_sequences] không tìm thấy trên thực thể [test.local.entities.Project]

Nó có vẻ thật là thú vị thêm vào trước tên lớp đầy đủ, chia cho dấu gạch dưới. Làm thế nào tôi có thể tránh điều này xảy ra?

CHỈNH SỬA: Tôi đã chơi trò chơi này nhiều hơn một chút. Thay đổi tên của thuộc tính mappedBy ném một ngoại lệ khác nhau, cụ thể là:

org.hibernate.AnnotationException: mappedBy tham khảo một tài sản thực thể mục tiêu không rõ: test.local.entities.Project.sequences

Vì vậy, chú thích được xử lý chính xác, nhưng bằng cách nào đó tham chiếu thuộc tính không được thêm chính xác vào cấu hình bên trong của Hibernate.

+0

Có vẻ như một SQ nhưng dự án có getSequences công cộng và setSequences cộng đồng về nó? – Affe

+0

@Affe Tôi sẽ doublecheck, tôi nghĩ rằng hiện tại nó là công cộng/bảo vệ. – wds

+0

Chỉ trong trường hợp, điều gì sẽ xảy ra nếu bạn đổi tên thuộc tính 'chuỗi ký tự 'thành một thứ khác, ví dụ: 'foo'? –

Trả lời

3

Tôi đã thực hiện cùng một kịch bản được đề xuất bởi câu hỏi của bạn. Và, như mong đợi, tôi nhận được cùng một ngoại lệ. Cũng giống như nhiệm vụ bổ sung, tôi đã thực hiện cùng một trường hợp nhưng với một-nhiều người bằng cách sử dụng khóa không phải chính làm cột được tham gia chẳng hạn như tham chiếu. Tôi có được bây giờ

SecondaryTable JoinColumn không tham khảo một chính phi chính

Vâng, nó có thể là một lỗi có thể ??? Vâng, vâng (và workaround của bạn hoạt động tốt (+1)). Nếu bạn muốn sử dụng khóa không phải là khóa chính làm khóa chính, bạn phải đảm bảo là duy nhất. Có lẽ nó giải thích tại sao Hibernate không cho phép sử dụng khóa không phải là khóa chính làm khóa chính (người dùng Unaware có thể nhận được các hành vi không mong muốn).

Nếu bạn muốn sử dụng các bản đồ tương tự, Bạn có thể chia mối quan hệ của bạn vào @ManyToMany @ OneToMany-ManyToOne Bằng cách sử dụng đóng gói, bạn không cần phải lo lắng về lớp tham gia của bạn

Dự án

@Entity 
public class Project implements Serializable { 

    @Id 
    @GeneratedValue 
    private Integer id; 

    @OneToMany(mappedBy="project") 
    private List<ProjectSequence> projectSequenceList = new ArrayList<ProjectSequence>(); 

    @Transient 
    private List<Sequence> sequenceList = null; 

    // getters and setters 

    public void addSequence(Sequence sequence) { 
     projectSequenceList.add(new ProjectSequence(new ProjectSequence.ProjectSequenceId(id, sequence.getReference()))); 
    } 

    public List<Sequence> getSequenceList() { 
     if(sequenceList != null) 
      return sequenceList; 

     sequenceList = new ArrayList<Sequence>(); 
     for (ProjectSequence projectSequence : projectSequenceList) 
      sequenceList.add(projectSequence.getSequence()); 

     return sequenceList; 
    } 

} 

chuỗi

@Entity 
public class Sequence implements Serializable { 

    @Id 
    private Integer id; 
    private String reference; 

    @OneToMany(mappedBy="sequence") 
    private List<ProjectSequence> projectSequenceList = new ArrayList<ProjectSequence>(); 

    @Transient 
    private List<Project> projectList = null; 

    // getters and setters 

    public void addProject(Project project) { 
     projectSequenceList.add(new ProjectSequence(new ProjectSequence.ProjectSequenceId(project.getId(), reference))); 
    } 

    public List<Project> getProjectList() { 
     if(projectList != null) 
      return projectList; 

     projectList = new ArrayList<Project>(); 
     for (ProjectSequence projectSequence : projectSequenceList) 
      projectList.add(projectSequence.getProject()); 

     return projectList; 
    } 

} 

ProjectSequence

@Entity 
public class ProjectSequence { 

    @EmbeddedId 
    private ProjectSequenceId projectSequenceId; 

    @ManyToOne 
    @JoinColumn(name="ID", insertable=false, updatable=false) 
    private Project project; 

    @ManyToOne 
    @JoinColumn(name="REFERENCE", referencedColumnName="REFERENCE", insertable=false, updatable=false) 
    private Sequence sequence; 

    public ProjectSequence() {} 
    public ProjectSequence(ProjectSequenceId projectSequenceId) { 
     this.projectSequenceId = projectSequenceId; 
    } 

    // getters and setters 

    @Embeddable 
    public static class ProjectSequenceId implements Serializable { 

     @Column(name="ID", updatable=false) 
     private Integer projectId; 

     @Column(name="REFERENCE", updatable=false) 
     private String reference; 

     public ProjectSequenceId() {} 
     public ProjectSequenceId(Integer projectId, String reference) { 
      this.projectId = projectId; 
      this.reference = reference; 
     } 

     @Override 
     public boolean equals(Object o) { 
      if (!(o instanceof ProjectSequenceId)) 
       return false; 

      final ProjectSequenceId other = (ProjectSequenceId) o; 
      return new EqualsBuilder().append(getProjectId(), other.getProjectId()) 
             .append(getReference(), other.getReference()) 
             .isEquals(); 
     } 

     @Override 
     public int hashCode() { 
      return new HashCodeBuilder().append(getProjectId()) 
             .append(getReference()) 
             .hashCode(); 
     } 

    } 

} 
+0

thực sự đó là chính xác những gì tôi đã làm. Tôi chấp nhận điều này vì mã có thể giúp người khác. :-) – wds

+0

@wds Cảm ơn bạn! Tôi hy vọng nó có thể hữu ích –

2

Cuối cùng tôi đã tìm ra, nhiều hơn hoặc ít hơn. Tôi nghĩ rằng đây về cơ bản là một lỗi ngủ đông.

chỉnh sửa: Tôi cố gắng để sửa chữa nó bằng cách thay đổi các bên sở hữu của hiệp hội:

class Sequence { 
    @Id 
    private int id; 

    private String reference; 

    @ManyToMany 
    @JoinTable(name="sequence_project", 
      [email protected](name="id"), 
      [email protected](name="reference", 
         referencedColumnName="reference")) 
    private List<Project> projects; 
} 

class Project { 
    @Id 
    private int id; 

    @ManyToMany(mappedBy="projects") 
    private List<Sequence> sequences; 
} 

này đã làm việc nhưng gây ra vấn đề ở đâu đó (xem bình luận). Vì vậy, tôi đã từ bỏ và lập mô hình hiệp hội như một thực thể với các hiệp hội nhiều người trong Chuỗi và Dự án. Tôi nghĩ rằng đây là ít nhất một lỗi xử lý tài liệu/lỗi (ngoại lệ không phải là rất thích hợp, và chế độ thất bại chỉ là sai) và sẽ cố gắng báo cáo nó cho các nhà phát triển Hibernate.

+0

Kiểm tra thêm cho thấy điều này không hiệu quả, vì bây giờ hibernate đang cố gắng tham gia cột Chuỗi trong bảng kết hợp với cột id của thực thể. Có vẻ như điều này đơn giản là không thể làm được, bằng cách nào đó. – wds

0

IMHO những gì bạn đang cố gắng đạt được là không thể với chú thích JPA/Hibernate. Thật không may, APIDoc của JoinTable là một chút không rõ ràng ở đây, nhưng tất cả các ví dụ tôi tìm thấy sử dụng khóa chính khi lập bản đồ tham gia bảng.

Chúng tôi đã gặp vấn đề tương tự như bạn trong một dự án mà chúng tôi cũng không thể thay đổi lược đồ cơ sở dữ liệu cũ. Các tùy chọn khả thi duy nhất có được đổ Hibernate và sử dụng MyBatis (http://www.mybatis.org), nơi bạn có đầy đủ tính linh hoạt của SQL bản địa để thể hiện điều kiện tham gia phức tạp hơn.

+0

Tôi nghĩ bạn có thể đúng.Trong thực tế, tôi có thể thay đổi một phần lược đồ, vì vậy tôi có thể làm lại bảng tham gia để sử dụng ID, nhưng nó sẽ hoạt động nhiều hơn và vì vậy tôi đã kết thúc bằng cách sử dụng giải pháp thay thế. – wds

0

Tôi chạy vào vấn đề này một chục lần bây giờ và thực hiện giải pháp duy nhất tôi tìm thấy được làm cấu hình của @JoinTable hai lần với các cột hoán đổi ở phía bên kia của mối quan hệ:

class Sequence { 

    @Id 
    private int id; 

    private String reference; 

    @ManyToMany 
    @JoinTable(
     name = "sequence_project", 
     joinColumns = @JoinColumn(name="reference", referencedColumnName="reference"), 
     inverseJoinColumns = @JoinColumn(name="id") 
    ) 
    private List<Project> projects; 

} 

class Project { 

    @Id 
    private int id; 

    @ManyToMany 
    @JoinTable(
     name = "sequence_project", 
     joinColumns = @JoinColumn(name="id"), 
     inverseJoinColumns = @JoinColumn(name="reference", referencedColumnName="reference") 
    ) 
    private List<Sequence> sequences; 

} 

tôi vẫn chưa thử nó với một cột khác với khóa chính.

+0

Bạn đang định nghĩa hai liên kết nhiều chiều một chiều ở đây, đây là ngữ nghĩa rất khác (và tôi thậm chí không chắc chắn mọi thứ sẽ hoạt động chính xác). –

+0

Chúng thực sự là một mối quan hệ bởi vì tôi sử dụng cùng một bảng nối và cùng các cột nối (chỉ cần chuyển đổi). – whiskeysierra

Các vấn đề liên quan