简体   繁体   中英

How to HQL query an item with no Id?

I'm in a situation where I had a LOT of data in my DB, and I decided to move to Hibernate. One of my tables (let's call it "MyItem") didn't have any suitable Id. It contained only one column, 'json'.

For some reason (related to the quantity of data), I could not migrate all the MyItem data so they all have a new unique identifier.

What I did was to add a new 'identifier' column to this table (so two columns: 'json' and 'identifier'), and in my Java code I declared my entity so that it looks like this:

@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;
    }
}

I can create and access my new data correctly (new ones are generated with an Id), but I am struggling accessing the oldest one (those having null in their identifier column) only when I do it through HQL.

Here is the way I perform my query (of course, I have changed some stuffs for the question, please disregard my fake ""json"", of course it's not json, it's just for the example):

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());
    }
}

And I get a NPE on the myItem.getJson();

Here are Hibernate traces:

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

The only idea I have here is to try/catch the NPE so I migrate the data on the fly by adding an identifier. Is there any other smarter way to perform my query?

Thanks: :)

  1. This looks suspicious:
public void setIdentifier(String id) {
  //fake
}

Why you do not set the identifier field here? Try to correct it to this:

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

and recheck your problem.

  1. You are trying to mix up access strategies what can lead to problems. By default, the placement of the @Id annotation gives the default access strategy. When placed on a field, Hibernate will assume field-based access. When placed on the identifier getter, Hibernate will use property-based access. So, hibernate will ignore your @Lob annotation, as you use property-based access.

  2. Your implementation of the getIdentifier method also looks suspicious. I would suggest you to use @GeneratedValue for the entity id generation. Hibernate supports UUID identifier value generation .

So, I would suggest you to correct your mapping in this way:

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;
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM