如何避免使用Hibernate从VARCHAR到VARCHAR2的隐式类型转换?

bis*_*ish 5 java oracle hibernate

一些同事提出了一个问题,他们认识到查询的执行时间很慢,并且发现由于隐式类型转换而未使用索引。

该表具有kgb_uuid用于存储UUID 的属性。该列定义为,VARCHAR2并在其上具有索引以通过UUID搜索行。

实体中的相关字段定义为String。根据Hibernate文档,Hibernate应该将此字符串转换为VARCHAR2Oracle数据库上的字符串,因此应使用索引。

但这不是日志显示的情况:

[9/2/19 11:56:07:610 CEST] 00000177 SystemOut O
2019-09-02T11:56:07,610 TRACE [ebContainer:3] ibebcTraceInterceptor; log ;; 41-类[MyDAO]中的输入方法[checkEindeutigeUUID]带参数(MyEntity @ b14745f9)

[9/2/19 11:56:07:688 CEST] 00000177 SQL Z org.hibernate.engine.jdbc.spi.SqlStatementLogger logStatement从MYENTITYTABLE mytab_中选择count(mytab0_.KGB_NR)作为col_0_0_,其中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

如图所示,String值的绑定VARCHAR不是not VARCHAR2,导致数据库进行隐式类型转换,而未使用索引,如OEM中所示(这是来自OEM的原始德语消息):

DasPrädikatSYS_OP_C2C(“ mytab _”。“ KGB_UUID”)=:B1,在Zeilen-ID 3 desAusführungsplansbenutzt wird中创建,并在Konvertierung des Impliziten DatentypsUuf indexerten SpalID中添加“ KGB_U”。Diese Konvertierung des impliziten Datentyps verhindert,dass der Optimizer为auf Tabelle“ MYENTITYTABLE”有效的坚果添加了知识。

它说使用了谓词SYS_OP_C2C("mytab_"."KGB_UUID")=:B1,并且它包含索引基列的隐式属性类型的KGB_UUID对话,并且这种隐式类型的对话阻止优化器有效地使用表的索引MYENTITYTABLE

我们已经使用表上的功能索引解决了问题,但是我们仍然想知道为什么Hibernate提供的数据类型显然不是VARCHAR2

系统:

  • 带有Hibernate Dialect Oracle10g的Hibernate 4.2.21(根据文档最多可兼容12个)
  • Oracle 12.2(我认为现在不完全是12.2,但可能只有12.1)

Hibernate版本无法升级,因为它是JPA 2.0可以使用的最新版本,而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
} 
Run Code Online (Sandbox Code Playgroud)

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;
    }
  }  
Run Code Online (Sandbox Code Playgroud)

Vla*_*cea 3

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

该映射很可能VARCHAR2是通过 Oracle Hibernate Dialect 实现的,因此您不应该覆盖默认的 Dialect 映射,因为可能存在正确使用VARCHAR2.

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