Hibernate通过在ManyToMany关联上将完全限定类名添加到属性名来失败

时间:2010-09-03 17:06:32

标签: java hibernate

我正在尝试使用ManyToMany关联将两个对象相互映射,但出于某种原因,当我使用mappedBy属性时,hibernate似乎对我正在映射的内容感到困惑。关于我的映射唯一奇怪的是,关联不是在其中一个条目中的主键字段上完成的(虽然该字段是唯一的)。

表格是:

Sequence (
  id NUMBER,
  reference VARCHAR,
)

Project (
  id NUMBER
)

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

对象看起来像(注释在getter上,将它们放在字段上以缩小一点):

class Sequence {
   @Id
   private int id;

   private String reference;

   @ManyToMany(mappedBy="sequences")
   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;
}

这失败并出现MappingException:

  在实体[test.local.entities.Project]上找不到

property-ref [_test_local_entities_Project_sequences]

似乎奇怪地在前面添加完全限定的类名,除以下划线。我怎样才能避免这种情况发生?

编辑: 我多玩了一下这个。更改mappedBy属性的名称会引发另一个异常,即:

  

org.hibernate.AnnotationException:mappedBy引用未知的目标实体属性:test.local.entities.Project.sequences

因此注释正确处理,但不知何故,属性引用未正确添加到Hibernate的内部配置中。

4 个答案:

答案 0 :(得分:3)

我做了你问题提出的相同场景。而且,正如预期的那样,我得到了同样的例外。作为补充任务,我通过使用非主键作为连接列(如引用),使用一对多多对一完成相同的方案 。我现在得到了

  

SecondaryTable JoinColumn不能引用非主键

嗯,这可能是个错误吗???嗯,是的(你的解决方法正常工作(+ 1))。如果您要使用非主键作为主键,则必须确保它是唯一的。也许它解释了为什么Hibernate不允许使用非主键作为主键(Unaware用户可能会遇到意外行为)。

如果您想使用相同的映射,可以将@ManyToMany关系拆分为@ OneToMany-ManyToOne 通过使用封装,您无需担心已加入的类

<强>项目

@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;
    }

}

<强>序列

@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();
        }

    }

}

答案 1 :(得分:2)

我终于弄清楚了,或多或少。我认为这基本上是一个hibernate错误。

编辑:我试图通过更改关联的拥有方来修复它:

class Sequence {
  @Id
  private int id;

  private String reference;

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

class Project {
  @Id
  private int id;

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

这有效但在其他地方引起了问题(见评论)。因此,我放弃并将关联建模为具有序列和项目中多对一关联的实体。我认为这至少是一个文档/错误处理错误(异常并不是非常相关,并且失败模式是错误的)并且会尝试将其报告给Hibernate开发人员。

答案 2 :(得分:0)

恕我直言,JPA / Hibernate注释无法实现您想要实现的目标。不幸的是,JoinTable的APIDoc在这里有点不清楚,但我发现的所有示例在映射连接表时都使用主键。

在我们也无法更改旧数据库架构的项目中,我们遇到了与您相同的问题。唯一可行的选择是转储Hibernate并使用MyBatis( http://www.mybatis.org ),您可以充分灵活地使用本机SQL来表达更复杂的连接条件。

答案 3 :(得分:0)

我现在遇到这个问题已经十几次了,我找到的唯一解决方法是在关系的另一端使用交换列两次配置@JoinTable

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;

}

我还没有尝试使用与主键不同的列。