【问题标题】:How to avoid implicit type conversion from VARCHAR to VARCHAR2 using Hibernate?如何避免使用 Hibernate 从 VARCHAR 到 VARCHAR2 的隐式类型转换?
【发布时间】: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 的别名,在我的简单测试中,两者之间没有转换。但是,当我尝试将VARCHAR2NVARCHAR2 进行比较时,会出现SYS_OP_C2C
  • @JonHeller 我个人不确定,因为问题只是向我描述的。我已将您的评论转达给我的同事。

标签: java oracle hibernate


【解决方案1】:

最简单的方法是扩展默认的StringType 并覆盖sqlType 属性,并通过@Type 注释为您的实体属性提供新的休眠类型。

VARCHAR2 映射很可能来自 Oracle Hibernate Dialect,因此您不应该覆盖默认的 Dialect 映射,因为可能存在正确使用 VARCHAR2 的列。

因此,自定义 Hibernate 类型为您提供控制权,并允许您仅将其用于 VARCHAR 列。

【讨论】:

  • 谢谢。我会处理我的同事的答案。如果他们告诉我,这对我有帮助,我会接受答案。但是我不知道他们是否会在定义功能索引时真正尝试一下。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-16
  • 1970-01-01
  • 2015-09-26
  • 1970-01-01
  • 2020-01-18
相关资源
最近更新 更多