【发布时间】:2019-09-14 09:10:11
【问题描述】:
一些同事提出了一个问题,他们发现查询的执行时间很慢,他们发现由于隐式类型转换而没有使用索引。
该表有一个属性 kgb_uuid 用于存储 UUID。该列定义为VARCHAR2,并在其上具有一个索引,用于按 UUID 搜索行。
实体中的相关字段定义为String。根据 Hibernate 文档,Hibernate 应该在 Oracle 数据库上将此字符串转换为 VARCHAR2,因此应该使用索引。
但日志显示的情况并非如此:
[9/2/19 11:56:07:610 CEST] 00000177 SystemOut O
2019-09-02T11:56:07,610 TRACE [ebContainer:3] i.b.e.b.c.TraceInterceptor;log;;41 - 入口方法 类 [MyDAO] 中的 [checkEindeutigeUUID] 带参数 (MyEntity@b14745f9)[9/2/19 11:56:07:688 CEST] 00000177 SQL Z org.hibernate.engine.jdbc.spi.SqlStatementLogger logStatement 选择 count(mytab0_.KGB_NR) as col_0_0_ from MYENTITYTABLE mytab_ where mytab_.KGB_UUID=?和 mytab_.EKN_NR=?
[9/2/19 11:56:07:688 CEST] 00000177 BasicBinder Z org.hibernate.type.descriptor.sql.BasicBinder 将绑定参数 [1] 绑定为 [VARCHAR] - 795BF3B98D879358E0531C03A90ABF0A [9/2/19 11:56:07:688 CEST] 00000177 BasicBinder Z org.hibernate.type.descriptor.sql.BasicBinder 绑定 绑定参数 [2] 作为 [BIGINT] - 1
正如所见,字符串值绑定为VARCHAR 而不是VARCHAR2,导致数据库进行隐式类型转换而不使用索引,如 OEM 所示(这是来自 OEM 的原始德语消息) :
Das Prädikat SYS_OP_C2C("mytab_"."KGB_UUID")=:B1, das in Zeilen-ID 3 des Ausführungsplans benutzt wrd, enthält eine Konvertierung des 隐含数据类型 auf der indexierten Spalte "KGB_UUID"。迪斯 Konvertierung des impliziten Datentyps verhindert, dass der Optimizer Indizes auf Tabelle "MYENTITYTABLE" effizient nutzt。
它说使用了谓词SYS_OP_C2C("mytab_"."KGB_UUID")=:B1,它包含索引基列KGB_UUID的隐式属性类型的对话,并且这种隐式类型的对话防止优化器可以使用表的indizies @ 987654330@高效。
我们已经解决了在表上使用函数索引的问题,但我们仍然想知道为什么 Hibernate 提供的数据类型显然不是 VARCHAR2。
系统:
- Hibernate 4.2.21 与 Hibernate Dialect Oracle10g(根据文档最多兼容 12 个)
- Oracle 12.2(现在不确切,我认为是 12.2,但可能只有 12.1)
Hibernate 版本无法升级,因为它是可与 JPA 2.0 一起使用的最后一个版本,它是 Websphere Process Server 8.5 支持的 JavaEE 6 的一部分。
(简称)实体
@Entity
@Table(name = "MYENTITYTABLE")
public class MyEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Id
// out commented the sequence generator
@Column(name="KGB_NR")
private long kgbNr;
@Column(name="KGB_UUID")
private String kgbUuid; // <<== DEFINED AS STRING!
//bi-directional many-to-one association to Ekistnutzer
@ManyToOne
@JoinColumn(name="EKN_NR")
private EkistnutzerEntity ekistnutzer;
// Other attributes not related in problem
}
DAO 方法
public int checkEindeutigeUUID(MyEntity myEntity) throws Exception {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> query = criteriaBuilder.createQuery(Long.class);
ParameterExpression<String> kgbUuidParam = criteriaBuilder.parameter(String.class, "kgbUuid");
ParameterExpression<EkistnutzerEntity> ekistnutzerParam = criteriaBuilder.parameter(EkistnutzerEntity.class,
"ekistnutzer");
Root<MyEntity> root = query.from(MyEntity.class);
query.select(criteriaBuilder.count(root));
query.where(criteriaBuilder.equal(root.get("kgbUuid"), kgbUuidParam),
criteriaBuilder.equal(root.get("ekistnutzer"), ekistnutzerParam));
try {
TypedQuery<Long> typedQuery = entityManager.createQuery(query);
typedQuery.setParameter("ekistnutzer", myEntity.getEkistnutzer());
typedQuery.setParameter("kgbUuid", myEntity.getKgbUuid());
return typedQuery.getSingleResult().intValue();
} catch (Exception e) {
throw e;
}
}
【问题讨论】:
-
如果您在计划中看到像
SYS_OP_C2C("mytab_"."KGB_UUID")=:B1这样的函数调用,我很确定,Oracle 在重写计划时注入了它。在 Hibernate 中打开 SQL 日志记录,您可能会看到,Hibernate 没有生成该代码。我在 Oracle 中经常看到这种情况。可能与cursor_sharing的设置有关 -
不相关,但是:将 UUID 存储为
varchar并不是一个好主意。将其存储为RAW(16)会更有效 -
您确定这是
VARCHAR问题而不是NVARCHAR问题吗?VARCHAR只是VARCHAR2的别名,在我的简单测试中,两者之间没有转换。但是,当我尝试将VARCHAR2与NVARCHAR2进行比较时,会出现SYS_OP_C2C。 -
@JonHeller 我个人不确定,因为问题只是向我描述的。我已将您的评论转达给我的同事。