如果ID列是VARCHAR,则Hibernate不从二级缓存获取

时间:2017-07-21 19:06:19

标签: hibernate spring-boot second-level-cache

我正在尝试Spring Boot和Hibernate二级缓存。我成功地让它发挥作用。但是,我遇到了一个奇怪的情况,即如果ID列是VARCHAR,则不从二级缓存中获取实体。以下是相关课程:

@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@Table(name = "user1")
@Entity
public class User {
    private long userId;
    private String name;

    @Id
    @Column(name = "id")
    public long getUserId() {
        return userId;
    }

    public void setUserId(long userId) {
        this.userId = userId;
    }

    @Column(name = "name")
    public String getName() {
        return name;
    } 

    public void setName(String name) {
        this.name = name;
    }

    public User(String name) {
        this.name = name;
    }

    public User() {
    }
}

这个实体工作正常,这里是来自hibernate的第一次和第二次获取的调试日志

抓取1

o.h.e.t.internal.TransactionImpl         : begin
org.hibernate.SQL                        : select user0_.id as id1_1_0_,     user0_.name as name2_1_0_ from user1 user0_ where user0_.id=?
o.h.l.p.e.p.i.ResultSetProcessorImpl     : Starting ResultSet row #0
l.p.e.p.i.EntityReferenceInitializerImpl : On call to EntityIdentifierReaderImpl#resolve, EntityKey was already known; should only happen on root returns with an optional identifier specified
o.h.engine.internal.TwoPhaseLoad         : Resolving associations for  [com.voyant.master.entities.User#1]
o.h.engine.internal.TwoPhaseLoad         : Adding entity to second-level cache: [com.voyant.master.entities.User#1]
o.h.engine.internal.TwoPhaseLoad         : Done materializing entity [com.voyant.master.entities.User#1]
o.h.r.j.i.ResourceRegistryStandardImpl   : HHH000387: ResultSet's statement was not registered
.l.e.p.AbstractLoadPlanBasedEntityLoader : Done entity load : com.voyant.master.entities.User#1
o.h.e.t.internal.TransactionImpl         : committing

抓取2

o.h.e.t.internal.TransactionImpl         : begin
o.h.e.t.internal.TransactionImpl         : committing

现在,有问题的实体定义:

@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
@Table(name = "user")
@Entity
public class User2 {

    @Id
    @Column(name = "username")
    private String username;

    @Column(name = "lastname")
    private String lastname;

    @Column(name = "name")
    private String name;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getLastname() {
        return lastname;
    }  

    public void setLastname(String lastname) {
        this.lastname = lastname;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public User2(String name) {
        this.name = name;
    }

    public User2() {
    } 
}

从两次连续获取同一实体

的Hibernate调试日志

抓取1

o.h.e.t.internal.TransactionImpl         : begin
o.h.jpa.criteria.CriteriaQueryImpl       : Rendered criteria query -> select generatedAlias0 from User2 as generatedAlias0 where generatedAlias0.username=:param0
org.hibernate.SQL                        : select user2x0_.username as username1_0_, user2x0_.lastname as lastname2_0_, user2x0_.name as name3_0_ from user user2x0_ where user2x0_.username=?
org.hibernate.loader.Loader              : Result set row: 0
org.hibernate.loader.Loader              : Result row: EntityKey[com.voyant.master.entities.User2#dharam]
o.h.engine.internal.TwoPhaseLoad         : Resolving associations for [com.voyant.master.entities.User2#dharam]
o.h.engine.internal.TwoPhaseLoad         : Adding entity to second-level cache: [com.voyant.master.entities.User2#dharam]
o.h.engine.internal.TwoPhaseLoad         : Done materializing entity [com.voyant.master.entities.User2#dharam]
o.h.e.t.internal.TransactionImpl         : committing
o.h.e.i.AbstractFlushingEventListener    : Processing flush-time cascades
o.h.e.i.AbstractFlushingEventListener    : Dirty checking collections
o.h.e.i.AbstractFlushingEventListener    : Flushed: 0 insertions, 0 updates, 0 deletions to 1 objects
o.h.e.i.AbstractFlushingEventListener    : Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
o.hibernate.internal.util.EntityPrinter  : Listing entities:
o.hibernate.internal.util.EntityPrinter  : com.voyant.master.entities.User2{name=dharam, username=dharam, lastname=dharam}

抓取2

o.h.e.t.internal.TransactionImpl         : begin
o.h.jpa.criteria.CriteriaQueryImpl       : Rendered criteria query -> select generatedAlias0 from User2 as generatedAlias0 where generatedAlias0.username=:param0
org.hibernate.SQL                        : select user2x0_.username as username1_0_, user2x0_.lastname as lastname2_0_, user2x0_.name as name3_0_ from user user2x0_ where user2x0_.username=?
org.hibernate.loader.Loader              : Result set row: 0
org.hibernate.loader.Loader              : Result row: EntityKey[com.voyant.master.entities.User2#dharam]
o.h.engine.internal.TwoPhaseLoad         : Resolving associations for [com.voyant.master.entities.User2#dharam]
o.h.engine.internal.TwoPhaseLoad         : Adding entity to second-level cache: [com.voyant.master.entities.User2#dharam]
o.h.engine.internal.TwoPhaseLoad         : Done materializing entity [com.voyant.master.entities.User2#dharam]
o.h.e.t.internal.TransactionImpl         : committing
o.h.e.i.AbstractFlushingEventListener    : Processing flush-time cascades
o.h.e.i.AbstractFlushingEventListener    : Dirty checking collections
o.h.e.i.AbstractFlushingEventListener    : Flushed: 0 insertions, 0 updates, 0 deletions to 1 objects
o.h.e.i.AbstractFlushingEventListener    : Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
o.hibernate.internal.util.EntityPrinter  : Listing entities:
o.hibernate.internal.util.EntityPrinter  : com.voyant.master.entities.User2{name=dharam, username=dharam, lastname=dharam}

1 个答案:

答案 0 :(得分:0)

我能够找出问题所以在这里做一个日志,这样如果将来有人面对这个问题,它可能会节省时间。

我们知道二级缓存适用于ID列(其中ID可以是字符串,整数,长整数等)。这仅在您使用findOne(id)方法获取对象时有效。

如果您有任何机会在存储库中定义了findByUsername(String username)方法并使用它来获取实体,那么这将被缓存。但是,第二次获取甚至懒得从缓存中查询。

相关代码采用RepositoryFactorySupport.QueryExecutorMethodInterceptor.doInvokce()方法。在哪里可以清楚地看到以下内容:

Method method = invocation.getMethod();
Object[] arguments = invocation.getArguments();

if (isCustomMethodInvocation(invocation)) {

    Method actualMethod = repositoryInformation.getTargetClassMethod(method);
    return executeMethodOn(customImplementation, actualMethod, arguments);
}

if (hasQueryFor(method)) {
    return queries.get(method).execute(arguments);
}

// Lookup actual method as it might be redeclared in the interface
// and we have to use the repository instance nevertheless
Method actualMethod = repositoryInformation.getTargetClassMethod(method);
return executeMethodOn(target, actualMethod, arguments);

根据条件有三种代码路径:

  1. 如果存在自定义方法调用
  2. 如果查询方法
  3. 或者它是一种实际的方法。
  4. 经验教训如果定义了id,请使用框架方法根据ID进行查询,不要编写自定义好看的方法:)