假设我有一个如下所示的实体,其中每个集合都是一个与PersonEntity主键具有外键关系的独立实体。
PersonEntity - PK: person_id
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "person", orphanRemoval = true)
Set<AddressEntity> addresses
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "person", orphanRemoval = true)
Set<NameEntity> nameParts
AddressEntity和NameEntity都有PersonEntity,这是以实体形式表示的FK关系。
所有表格都有一个名为tenant_id的字段,它们被分区。
如果我创建HibernateCriteria,如下所示:
final Criteria criteria = sessionFactory.getCurrentSession().createCriteria(PersonEntity.class, "p");
criteria.add(Restrictions.eq("p.personId", personId));
criteria.add(Restrictions.eq("p.tenantId", tenantId));
我得到的SQL就像:
select ALL_ATTRIBUTES_SNIPPED
FROM person this_
LEFT OUTER JOIN address addresses2_
ON this_.person_id=addresses2_.person_id
LEFT OUTER JOIN name nameparts4_
ON this_.person_id=nameparts4_.person_id
WHERE this_.person_id=?
AND this_.tenant_id=?
查看解释计划,我看到这会在进行连接时检查所有分区。这是不必要的,因为它只需要查看一个分区。
我希望在所有表上添加额外限制,以便所有表都受到tenant_id的限制。所以SQL可能如下所示:
select ALL_ATTRIBUTES_SNIPPED
FROM person this_
LEFT OUTER JOIN address addresses2_
ON this_.person_id=addresses2_.person_id
LEFT OUTER JOIN name nameparts4_
ON this_.person_id=nameparts4_.person_id
WHERE this_.person_id=?
AND this_.tenant_id=?
AND addresses2_.tenant_id =?
AND nameparts4_.tenant_id =?
但是,我似乎无法弄清楚如何创建标准来执行此操作。当我尝试以下内容时:
final Criteria criteria = sessionFactory.getCurrentSession().createCriteria(PersonEntity.class, "p")
.createAlias("addresses", "address", JoinType.LEFT_OUTER_JOIN)
.createAlias("nameParts", "namePart", JoinType.LEFT_OUTER_JOIN)
criteria.add(Restrictions.eq("p.personId", personId));
criteria.add(Restrictions.eq("p.tenantId", tenantId));
criteria.add(Restrictions.eq("address.tenantId", tenantId));
criteria.add(Restrictions.eq("namePart.tenantId", tenantId));
我得到的SQL看起来像这样:
select ALL_ATTRIBUTES_SNIPPED
FROM person this_
LEFT OUTER JOIN address addresses2_
ON this_.person_id=addresses2_.person_id
LEFT OUTER JOIN name nameparts4_
ON this_.person_id=nameparts4_.person_id
LEFT OUTER JOIN address addresses3_
ON this_.person_id=addresses3_.person_id
LEFT OUTER JOIN name nameparts1_
ON this_.person_id=nameparts1_.person_id
WHERE this_.person_id=?
and this_.tenant_id = ?
and addresses3_.tenant_id = ?
and nameparts1_.tenant_id = ?
如您所见,表格连接了两次。
如何创建使用原始表的限制?我不知道如何能够提供访问现有连接的限制。我尝试了p.addresses.tenantId
之类的内容,但却说addresses
无法识别。
编辑:我已经在很大程度上解决了查询问题,方法是将此行放在Person inntity中的Set和set实体中的PersonEntity上(即AddressEntity)。
@JoinColumns(value={
@JoinColumn(name="PERSON_ID", referencedColumnName="PERSON_ID", insertable=false, updatable=false),
@JoinColumn(name="TENANT_ID", referencedColumnName="TENANT_ID", insertable=false, updatable=false)
})
我还删除了这些列的mappedBy属性。
这会强制加入person_id和tenant_id,并使解释计划的成本显着提高(以及实际性能)。但是,我不确定这是否是一个真正的解决方案,因为它引入了一个新问题。
我现在的问题是,当我尝试创建PersonEntity时,我收到以下错误:
12:09:26.672 WARN [main] org.hibernate.engine.jdbc.spi.SqlExceptionHelper - SQL Error: 1400, SQLState: 23000 12:09:26.672 ERROR [main] org.hibernate.engine.jdbc.spi.SqlExceptionHelper - ORA-01400: cannot insert NULL into ("USER"."ADDRESS"."PERSON_ID")
即使SQL显示在尝试插入地址之前发生了人员插入,也会发生这种情况。似乎没有传递person_id以放入地址插入中。我怎么能强迫Hibernate这样做呢?以前,它只是自动发生(从我的角度来看)。
如果重要的话,我正在使用序列生成器来创建主键。
答案 0 :(得分:0)
为我解决这个问题的方法是将这些行添加到PersonEntity中的集合中,然后添加到子实体类(AddressEntity,NameEntity)中的PersonEntity字段:
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumns(value={
@JoinColumn(name = "PERSON_ID", referencedColumnName = "PERSON_ID", nullable = false),
@JoinColumn(name = "TENANT_ID", referencedColumnName = "TENANT_ID", nullable = false)
})
public PersonEntity getPerson() {
return personEntity;
}
这适用于查询,但我无法进行插入或更新,因此我必须做的另一件事是确保现有的tenantId字段具有insertable = false和updateable = false,如下所示:
@Column(name = "TENANT_ID", insertable = false, updatable = false)
public String getTenantId() {
return tenantId;
}
然后,执行原始问题中的条件将导致所有子表在PERSON_ID和TENANT_ID上具有连接,正如我想要的那样。
这将解释计划中的估计成本从2525改为15,因为它可以直接进入正确的分区而不是循环遍历它们。