我正在尝试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}
答案 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);
根据条件有三种代码路径:
经验教训如果定义了id,请使用框架方法根据ID进行查询,不要编写自定义好看的方法:)