Hibernate:通过单个连接表

时间:2017-11-27 03:27:14

标签: java sql spring hibernate jpa

假设我有一些表格A,每个表格都有B个,其中每个C都有很多X个等等,无论多少级别。

现在还有一些表X位于此层次结构中的某个级别(例如,特定BA相关联,因此也与C相关联,但不是Dhierarchy_mapping)。

我正在尝试通过联接表维护此关系,将其称为a_id,其中包含以下列:b_idc_idX,等等但是,如果现有hierarchy_mappinga_idb_id,则c_id中的一个不会为空,并且X中的插入也会插入X

首先,这是建模这种层次结构的正确方法,其中A可以插入此层次结构的任何级别吗?

其次,显然,我在下面的实现并不适用于Hibernate,而且我不完全确定原因。当B的实体定义仅通过联接表C引用hierarchy_mappingbc之一时,它可以正常工作(即如果我删除了@Entity @Table(name = "X") public class X { @Id @Column(name = "x_id") private Integer xId; @JoinTable( name = "hierarchy_mapping", joinColumns = @JoinColumn(name = "x_id", referencedColumnName = "x_id"), inverseJoinColumns = @JoinColumn(name = "a_id", referencedColumnName = "a_id") ) @OneToOne(fetch = FetchType.LAZY) private A a; @JoinTable( name = "hierarchy_mapping", joinColumns = @JoinColumn(name = "x_id", referencedColumnName = "x_id"), inverseJoinColumns = @JoinColumn(name = "b_id", referencedColumnName = "b_id") ) @OneToOne(fetch = FetchType.LAZY) private B b; @JoinTable( name = "hierarchy_mapping", joinColumns = @JoinColumn(name = "x_id", referencedColumnName = "x_id"), inverseJoinColumns = @JoinColumn(name = "c_id", referencedColumnName = "c_id") ) @OneToOne(fetch = FetchType.LAZY) private C c; } 来自下面POJO定义的成员变量XJpaRepository

SELECT x.x_id, x.name, .., m.b_id FROM TABLE_X x LEFT OUTER JOIN hierarchy_mapping m ON x.x_id = m.x_id WHERE x.name = ?

修改 我看到上面的实现,当我通过b_id实现查询单个hierarchy_mapping时,Hibernate执行左外连接:

a_id

要注意的是,由于某些原因,它仅从c_id中选择B,并且也不会选择b_idb。然后最重要的是Hibernate没有执行后续查询,就像通常从X表中获取与SELECT b.b_id, b.description, .. FROM TABLE_B b where b._id = ?相关联的行的信息一样,以便它可以填充该成员变量{{ 1}}在返回的eb ssh实例中。 (例如后续查询,如phpinfo()

为什么这两个都发生了?

1 个答案:

答案 0 :(得分:1)

我不是在告诉你正在尝试解决的问题的最佳方法是什么,因为你要解决的实际问题根本不明确。

此解决方案基于以下假设:使用此X您只想将一些通用数据附加到任何A,B ... Z

查看您的代码我建议您考虑使用JPA继承来使代码更加清晰和可管理(至少在我看来)。

创建A,B ... Z继承的基础实体。

@Entity
@Getter
@Inheritance(strategy=InheritanceType.JOINED) // needs to be joined to work
public class Alphabet {
   @Id @GeneratedValue
   private Long id;
   // X is here to have relationship bi-directional
   // leave it out if not needed
   @OneToOne(mappedBy="alphabet", cascade=CascadeType.PERSIST) 
   @Setter
   private X x;
}

然后让A,B ... Z中的每个实体继承此内容并创建所需的其他关系,例如A& B

实体A

@Entity
@Getter
public class A extends Alphabet {   
   private Character character = 'A'; // just some field, add the needed fields

   @OneToMany(cascade=CascadeType.PERSIST, mappedBy="a")
   private Collection<B> bs = new ArrayList<>();
}

实体B

@Entity
@Getter
public class B extends Alphabet {
   private Character character = 'B';

   @ManyToOne(cascade=CascadeType.PERSIST)
   @Setter
   private A a;

}

然后简单地说:

实体X

@Entity
@Table(name="hierarchy_mapping")
@Getter
public class X {

   @Id @GeneratedValue
   private Long id;

   @OneToOne(fetch=FetchType.LAZY, cascade=CascadeType.PERSIST)
   @Setter
   private Alphabet alphabet;

   // and possible other data you want to add in your `X`
}

示例:

A a = new A();
B b = new B();
b.setA(a);
a.getBs().add(b);   

X xa = new X(), xb = new X();

xa.setAlphabet(a);
a.setX(xa); 

xb.setAlphabet(b);
b.setX(xb);

em.persist(a);

设置字母表时,无需考虑它是AD还是Q。只需setAlphabet(..)

获得字母表时,您可以为X创建一个实用工具方法,如

@SuppressWarnings("unchecked")
public <T> T getAlphabet(Class<T> classT) {
    Alphabet at = getAlphabet();
    if(null!=at && classT.isAssignableFrom(at.getClass()))
        return (T)at;
    return null;
}

上述方法可以像

一样使用
B b = x.getAlphabet(B.class);

无论如何,你需要知道你在X挖掘的是什么。如果它不存在,您将获得null