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

Java 如何使用Hibernate避免从VARCHAR到VARCHAR2的隐式类型转换?,java,oracle,hibernate,Java,Oracle,Hibernate,一些同事提出了一个问题,他们发现查询的执行时间很慢,并且发现由于隐式类型转换而没有使用索引 该表有一个用于存储uuid的属性kgb_uuid。该列定义为VARCHAR2,其上有一个索引,用于按UUID搜索行 实体中的相关字段定义为String。根据Hibernate文档,Hibernate应在Oracle数据库上将此字符串转换为VARCHAR2,因此应使用索引 但事实并非如日志所示: [9/2/1911:56:07:610 CEST]00000177系统输出 2019-09-02T11:56:0

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

该表有一个用于存储uuid的属性
kgb_uuid
。该列定义为
VARCHAR2
,其上有一个索引,用于按UUID搜索行

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

但事实并非如日志所示:

[9/2/1911:56:07:610 CEST]00000177系统输出 2019-09-02T11:56:07610跟踪[ebContainer:3] i、 b.e.b.c.跟踪接收器;日志41-输入法 带有参数的类[MyDAO]中的[checkEindeutigeUUID](MyEntity@b14745f9)

[9/2/1911:56:07:688 CEST]00000177 SQL Z org.hibernate.engine.jdbc.spi.SqlStatementLogger日志语句选择 从MYENTITYTABLE mytab中将(mytab0.KGB_NR)计数为列0 mytab_UUID=?和mytab_uu.EKN_unR=

[9/2/19 11:56:07:688 CEST]00000177 BasicBinder Z org.hibernate.type.descriptor.sql.BasicBinder 将绑定参数[1]绑定为[VARCHAR]- 795BF3B98D879358E0531C03A90ABF0A[9/2/1911: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,Zeilen ID 3中的Das 这是一个非常重要的问题 实施“克格勃”独立宣言。迪塞 数据优化程序的实现 将“MYENTITYTABLE”标签独立于nutzt

它表示谓词
SYS_OP_C2C(“mytab_.”“KGB_UUID”)=:B1
被使用,它包含索引基列
KGB_UUID
的隐式属性类型的对话,并且该隐式类型的对话防止优化器有效地使用表
MYENTITYTABLE
的索引

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

系统:

  • Hibernate 4.2.21和Hibernate方言Oracle10g(根据文档,最多可兼容12个)
  • Oracle 12.2(我想现在不完全是12.2,但可能只有12.1)
Hibernate版本无法升级,因为它是可以与JPA2.0一起使用的最后一个版本,JPA2.0是WebSphereProcessServer8.5支持的JavaEE6的一部分

(短端)实体

@实体
@表(name=“MYENTITYTABLE”)
公共类MyEntity实现可序列化{
私有静态最终长serialVersionUID=1L;
@身份证
//输出序列生成器
@列(name=“KGB\u NR”)
私人长kgbNr;
@列(name=“KGB_UUID”)

私有字符串kgbUuid;//最简单的方法是扩展默认的
StringType
并重写
sqlType
属性,并通过注释将新的休眠类型提供给实体属性

很可能
VARCHAR2
映射是通过Oracle Hibernate方言实现的,因此您不应该覆盖默认的方言映射,因为可能有正确使用
VARCHAR2
的列


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

如果您看到类似
SYS\u OP\u C2C(“mytab”;“KGB\u UUID”)的函数调用=:B1
在该计划中,我非常确定Oracle在重写计划时注入了该代码。在Hibernate中打开SQL日志记录,您可能会看到Hibernate没有生成该代码。我在Oracle中经常看到这种情况。可能与设置Unrelated有关,但是:将UUID存储为
varchar
是非常困难的一开始不是个好主意。将其存储为
RAW(16)会更有效
你确定这是
VARCHAR
问题,而不是
NVARCHAR
问题吗?
VARCHAR
只是
VARCHAR2
的别名,在我的简单测试中,两者之间没有转换。但是,当我尝试将
VARCHAR2
NVARCHAR2
进行比较时,那么
SYS\u OP\u C2C
出现。@JonHeller我个人不确定,因为这个问题只是向我描述的。我已经把你的意见传递给我的同事。谢谢。我会把答案交给我的同事。如果他们告诉我,这有帮助,我会接受答案。但我不知道他们在定义函数索引时是否会真的尝试。
@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
} 
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;
    }
  }