【问题标题】:Hibernate not fetching from Second Level Cache if ID column is VARCHAR如果 ID 列是 VARCHAR,则休眠不会从二级缓存中获取
【发布时间】:2017-12-27 22:36:59
【问题描述】:

我正在尝试 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() {
    } 
}

来自同一实体的两次连续提取的休眠调试日志

获取 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}

【问题讨论】:

  • 您能否将您的用户类更改为实现 Serializable 并查看它是否有效。 *********公共类用户实现可序列化{
  • 对不起!用户类都很好。问题出在 User2 类上,但是是的!我会尝试你的建议。
  • 道歉我的意思是有问题的人。
  • 试过了!但没有运气。顺便说一句,感谢您阅读冗长的问题并尝试一下。
  • 没问题。阅读以下文章dzone.com/articles/hibernate-types

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


【解决方案1】:

我能够找出问题所在,因此在此处制作日志,以便将来有人遇到此问题时,可能会节省时间。

正如我们所知,二级缓存适用于 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. 或者它是一种实际的方法。

经验教训如果定义了 id,请使用框架方法根据 ID 进行查询,不要编写自定义好看的方法:)

【讨论】:

    猜你喜欢
    • 2010-10-20
    • 1970-01-01
    • 2010-10-29
    • 2017-07-05
    • 2011-07-08
    • 1970-01-01
    • 2016-06-23
    • 2011-12-27
    相关资源
    最近更新 更多