我有一个Spring Boot应用程序,它通过Hibernate检索(并存储)数据。 Hibernate已连接到MySQL数据库。在从数据库中检索某些表之前,我使用一些实体图对数据库请求进行了一些优化,以加入一些表。现在,我希望将最常见的对象(不会经常更改)存储在缓存中。
我尝试通过Infinispan和Hazelcast进行缓存。我在这里和那里更改了配置,但是总是以某种方式从数据库中检索实体。
对于Halzelcast,我在pom.xml
中添加了以下三个依赖项。即使hazelcast-hibernate
似乎已集成在hazelcast-spring
中,我也需要第三个依赖项,因为否则,区域工厂将不可用。
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<version>3.12.2</version>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-spring</artifactId>
<version>3.12.2</version>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-hibernate53</artifactId>
<version>1.3.2</version>
</dependency>
以下设置对于使用Hazelcast作为Hibernate缓存应该足够了。但这行不通。我仍然在日志中看到与以前相同的SQL查询。
spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=com.hazelcast.hibernate.HazelcastLocalCacheRegionFactory
应使用HazelcastLocalCacheRegionFactory
,因为性能应稍好一些,并且ram(当前)不是一个大问题。不过,我也欢迎采用其他方法。
相关实体同时获得两个注释(@Cacheable
和@Cache
)。
@Entity(name = "business_units")
@Getter @Setter
@Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class BusinessUnit extends Auditable<String> implements Serializable {
private static final long serialVersionUID = -6994142588281279518L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator = "native")
@GenericGenerator(name = "native", strategy = "native")
@Column(nullable = false, name = "id")
private Integer id;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "allocated_responsibility_id", referencedColumnName = "id")
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private ProductManager allocatedResponsibility;
// Some other attributes, getters and setters...
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof BusinessUnit)) return false;
BusinessUnit that = (BusinessUnit) o;
return id != null && id.equals(that.id);
}
@Override
public int hashCode() {
return 31;
}
}
Infinispan也尝试了相同的方法(不同的依赖关系,但相同的不存在结果)。
我打算使用外部提供商,因为该应用程序应可扩展。如果不注意这一点,缓存将无法再正确镜像数据库。
应该使用嵌入式缓存,因为用于Hazelcast或Infinispan的额外服务器太难维护了。
我不知道为什么什么都没有改变(好的,我看到Hazelcast开始了)。我的另一个想法是使用查询缓存而不是Hibernate缓存。但这需要更多注意以确保同步数据库和缓存。此外,该应用可能会从中受益较少。
能否请您告诉我为什么它不起作用以及需要对其进行哪些更改?
我尝试了以下建议,并添加了@Nicolas建议的统计信息。
spring.jpa.properties.hibernate.generate_statistics=true
当我第一次加载这样的页面时,数据会从数据库中请求并放入缓存(L2C)。
1519200 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
1759800 nanoseconds spent preparing 1 JDBC statements;
4144000 nanoseconds spent executing 1 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
161900 nanoseconds spent performing 3 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
但是当我再次访问该站点(或执行任何其他应访问高速缓存的操作)时,数据再次从数据库中请求(并放入高速缓存中)。
再次访问该页面时的日志:
2019-10-08 12:51:38.399 INFO 17028 --- [nio-8080-exec-8] i.StatisticalLoggingSessionEventListener : Session Metrics {
0 nanoseconds spent acquiring 0 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
0 nanoseconds spent preparing 0 JDBC statements;
0 nanoseconds spent executing 0 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
}
2019-10-08 12:51:38.404 DEBUG 17028 --- [nio-8080-exec-8] org.hibernate.SQL :
select
businessun0_.id as id1_0_0_,
productman1_.id as id2_9_1_,
employee2_.id as id1_4_2_,
businessun0_.created_by as created_2_0_0_,
businessun0_.created_date as created_3_0_0_,
businessun0_.last_modified_by as last_mod4_0_0_,
businessun0_.last_modified_date_time as last_mod5_0_0_,
businessun0_.allocated_responsibility_id as allocate7_0_0_,
businessun0_.name as name6_0_0_,
productman1_.employee_id as employee3_9_1_,
employee2_.created_by as created_2_4_2_,
employee2_.created_date as created_3_4_2_,
employee2_.last_modified_by as last_mod4_4_2_,
employee2_.last_modified_date_time as last_mod5_4_2_,
employee2_.email_address as email_ad6_4_2_,
employee2_.location as location7_4_2_,
employee2_.name as name8_4_2_,
employee2_.team as team9_4_2_
from
business_units businessun0_
left outer join
responsibilities productman1_
on businessun0_.allocated_responsibility_id=productman1_.id
left outer join
employees employee2_
on productman1_.employee_id=employee2_.id
2019-10-08 12:51:38.412 INFO 17028 --- [nio-8080-exec-8] i.StatisticalLoggingSessionEventListener : Session Metrics {
901000 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
1075000 nanoseconds spent preparing 1 JDBC statements;
1766400 nanoseconds spent executing 1 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
166800 nanoseconds spent performing 3 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
}
我将Hazelcast的地区工厂更改为不能够连接管理中心的本地工厂。
spring.jpa.properties.hibernate.cache.region.factory_class=com.hazelcast.hibernate.HazelcastCacheRegionFactory
现在,有两个成员连接到管理中心(即使我仅启动一个应用程序)。
日志中显示的统计信息仍显示推送。但是,管理中心向我显示已从缓存中检索了一些数据。有时,我可以在统计信息中看到一个缓存命中而不是JDBC语句。
答案 0 :(得分:1)
我认为,无论缓存是否激活,SQL都将始终由Hibernate输出。如果要确保已使用缓存,请激活Hibernate统计信息:
spring.jpa.properties.hibernate.generate_statistics=true
在第一个请求上,它应该输出如下内容:
Session Metrics {
388816 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
2436908 nanoseconds spent preparing 1 JDBC statements;
2585533 nanoseconds spent executing 1 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
10276363 nanoseconds spent performing 1 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
54012289 nanoseconds spent performing 1 L2C misses;
0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
}
在以后的请求中,类似:
Session Metrics {
79940 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
0 nanoseconds spent preparing 0 JDBC statements;
0 nanoseconds spent executing 0 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
1665675 nanoseconds spent performing 1 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
}
请注意第二个请求上的缓存命中。
答案 1 :(得分:1)
如果您看到Hazelcast实例已启动,则在缓存提供程序端应该没有问题,并且L2C必须已准备就绪可以服务。我认为问题出在您的实体对象上。除非您为实体类使用@Cachable
注释或在<cache ... >
中定义entity_name.hbm.xml
,否则实体将不会保留在二级缓存中。
还
...在这种情况下,Hibernate是否仅缓存带注释的实体?
对于二级缓存,是的。
您对我应该给统计数据打电话的地方有建议吗?
如果启用统计信息,则在会话关闭后,将立即打印出详细的会话统计信息(有关Nicolas的回答)。
我认为无论缓存是否激活,Hibernate都将始终输出SQL。
否,除非SQL查询命中数据库,否则不应在日志中显示。