【问题标题】:How to HQL query an item with no Id?如何 HQL 查询没有 Id 的项目?
【发布时间】:2021-03-26 15:42:41
【问题描述】:

我的数据库中有大量数据,因此我决定迁移到 Hibernate。我的一张表(我们称之为“MyItem”)没有任何合适的 ID。它只包含一列“json”。

由于某种原因(与数据量有关),我无法迁移所有 MyItem 数据,因此它们都有一个新的唯一标识符。

我所做的是在该表中添加一个新的“标识符”列(所以两列:“json”和“标识符”),并在我的 Java 代码中声明了我的实体,使其看起来像这样:

@Entity
@Table
public class MyItem {
    @Lob
    private String json;

    private String identifier;

    public MyItem() {
    }

    public MyItem(String json) {
        this.json = json;
    }

    public void setIdentifier(String id) {
        //fake
    }

    @Id
    public String getIdentifier() {
        if (identifier == null) {
            identifier = UUID.randomUUID().toString();
        }
        return identifier;
    }
    
    public String getJson() {
        return json;
    }
    
    public void setJson(String json) {
        this.json = json;
    }
}

我可以正确地创建和访问我的新数据(新数据是使用 Id 生成的),但是只有当我通过 HQL 进行访问时,我才会努力访问最旧的数据(标识符列中包含 null 的数据)。

这是我执行查询的方式(当然,我为问题更改了一些东西,请忽略我的假“json”,当然不是json,只是为了示例):

String myJson = "6044c44a-dd17-40ca-b66b-cb9d29cf8a33-1-0-1";
Query<MyItem> query = statelessSession.createQuery("FROM MyItem WHERE json = :json", MyItem.class);
query.setParameter("json", myJson);
query.setFetchSize(FETCH_SIZE);
query.setReadOnly(true);
query.setLockMode("a", LockMode.NONE);
try (ScrollableResults MyItemList = query.scroll(ScrollMode.FORWARD_ONLY)) {
    while (MyItemList.next()) {
        MyItem myItem = (MyItem) MyItemList.get(0);
        myItem.getJson());
    }
}

我在 myItem.getJson(); 上得到一个 NPE;

这里是 Hibernate 跟踪:

14:55:58.263 [TileBuilderCallable_pool-11-thread-2] TRACE o.h.engine.query.spi.QueryPlanCache@157 - Located HQL query plan in cache (FROM MyItem WHERE json = :json) 
14:55:58.264 [TileBuilderCallable_pool-11-thread-2] TRACE o.h.engine.query.spi.QueryPlanCache@157 - Located HQL query plan in cache (FROM MyItem WHERE json = :json) 
14:55:58.264 [TileBuilderCallable_pool-11-thread-2] TRACE o.h.engine.query.spi.HQLQueryPlan@344 - Iterate: FROM MyItem WHERE json = :json 
14:55:58.264 [TileBuilderCallable_pool-11-thread-3] TRACE o.h.r.j.i.AbstractLogicalConnectionImplementor@66 - Preparing to begin transaction via JDBC Connection.setAutoCommit(false) 
14:55:58.264 [TileBuilderCallable_pool-11-thread-2] TRACE o.h.engine.spi.QueryParameters@325 - Named parameters: {json=6044c44a-dd17-40ca-b66b-cb9d29cf8a33-1-0-1} 
14:55:58.264 [TileBuilderCallable_pool-11-thread-3] TRACE o.h.r.j.i.AbstractLogicalConnectionImplementor@68 - Transaction begun via JDBC Connection.setAutoCommit(false) 
14:55:58.264 [TileBuilderCallable_pool-11-thread-3] TRACE o.h.r.t.b.j.i.JdbcResourceLocalTransactionCoordinatorImpl@172 - ResourceLocalTransactionCoordinatorImpl#afterBeginCallback 
14:55:58.264 [TileBuilderCallable_pool-11-thread-3] DEBUG o.h.e.t.internal.TransactionImpl@56 - On TransactionImpl creation, JpaCompliance#isJpaTransactionComplianceEnabled == false 
14:55:58.264 [TileBuilderCallable_pool-11-thread-2] DEBUG org.hibernate.SQL@94 - 
 select 
 myit0_.identifier as identifi1_0_,
 myit0_.json as json5_0_
 from 
 MyItem myit0_ 
 where 
 myit0_.json=? 
14:55:58.264 [TileBuilderCallable_pool-11-thread-3] DEBUG o.h.e.t.internal.TransactionImpl@78 - begin 
14:55:58.264 [TileBuilderCallable_pool-11-thread-3] DEBUG o.s.j.d.DriverManagerDataSource@144 - Creating new JDBC DriverManager Connection to [jdbc:com.db://localhost:1234/mydb] 
14:55:58.264 [TileBuilderCallable_pool-11-thread-2] TRACE o.h.r.j.i.ResourceRegistryStandardImpl@68 - Registering statement [[Statement - handle 1 (Connection ID - 25769822844)]] 
14:55:58.264 [TileBuilderCallable_pool-11-thread-2] TRACE o.h.e.j.internal.JdbcCoordinatorImpl@339 - Registering last query statement [[Statement - handle 1 (Connection ID - 25769822844)]] 
14:55:58.264 [TileBuilderCallable_pool-11-thread-2] TRACE o.h.type.descriptor.sql.BasicBinder@65 - binding parameter [1] as [VARCHAR] - [6044c44a-dd17-40ca-b66b-cb9d29cf8a33-1-0-1] 
14:55:58.264 [TileBuilderCallable_pool-11-thread-2] TRACE org.hibernate.loader.Loader@2034 - Bound [2] parameters total 
14:55:58.266 [TileBuilderCallable_pool-11-thread-2] TRACE o.h.r.j.i.ResourceRegistryStandardImpl@196 - Registering result set [com.db.ResultSet@55612811] 
14:55:58.266 [TileBuilderCallable_pool-11-thread-2] TRACE o.h.t.descriptor.sql.BasicExtractor@51 - extracted value ([identifi1_0_] : [VARCHAR]) - [null] 
14:55:58.266 [TileBuilderCallable_pool-11-thread-2] DEBUG org.hibernate.loader.Loader@1532 - Result row: null 
14:55:58.267 [TileBuilderCallable_pool-11-thread-2] TRACE org.hibernate.loader.Loader@1149 - Total objects hydrated: 0

我在这里唯一的想法是尝试/捕获 NPE,因此我通过添加标识符来动态迁移数据。还有其他更智能的方式来执行我的查询吗?

谢谢! :)

【问题讨论】:

  • 请问,您是不是在尝试用 NULL 标识符更新旧记录以获得标识符?
  • @NgọcHy 这是我想做的事情之一,是的,但这实际上不是这个查询的目的——而是检索一些数据
  • 据我所知,为了检索任何数据,必须有自己的标识符。所以在我看来,你应该首先尝试给他们一个标识符。这会让你更轻松

标签: java hibernate hql


【解决方案1】:
  1. 这看起来很可疑:
public void setIdentifier(String id) {
  //fake
}

为什么不在这里设置identifier 字段?尝试将其更正为:

public void setIdentifier(String id) {
  this.identifier = id;
}

并重新检查您的问题。

  1. 您试图混淆access strategies 可能导致问题的内容。默认情况下,@Id 注释的放置提供了默认访问策略。当放置在一个字段上时,Hibernate 将假定基于字段的访问。当放置在标识符 getter 上时,Hibernate 将使用基于属性的访问。 因此,当您使用基于属性的访问时,hibernate 将忽略您的 @Lob 注释。

  2. 您对getIdentifier 方法的实现看起来也很可疑。我建议您使用@GeneratedValue 生成实体ID。 Hibernate 支持UUID identifier value generation

所以,我建议您以这种方式更正您的映射:

import org.hibernate.annotations.Type;

@Entity
@Table
public class MyItem {

  @Id
  @GeneratedValue
  @Type(type = "uuid-char")
  private UUID identifier;
  
  @Lob
  private String json;

  // ...

  @Transient
  public String getIdentifierAsString() {
     return identifier != null ? identifier.toString() : null;
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-08
    • 2010-11-30
    • 2017-07-27
    • 1970-01-01
    • 1970-01-01
    • 2012-12-14
    • 2019-07-12
    • 2011-09-16
    相关资源
    最近更新 更多