当前位置:Java -> 如何在Spring Boot应用程序中使用JPA Hibernate集成NCache进行缓存
Hibernate 是Java和Spring应用程序中最受欢迎的对象关系映射 (ORM)库之一。它帮助开发人员连接并在Java应用程序中使用关系型数据库,而无需编写SQL查询。该库实现了JPA (Java持久化API)规范,并提供了几个额外的功能,帮助更快、更容易地开发应用程序中的持久性。
Hibernate支持的一个很酷的特性是缓存。Hibernate支持两级缓存 — L1和L2。L1缓存默认启用,并且在应用程序范围内工作,因此无法跨多个线程共享。例如,如果你有一个规模化的微服务应用程序,在关系型数据库设置中读取和写入一个表,这个L1缓存在每个这样的容器中都维护独立状态。
L2缓存是一个外部可插拔的接口,通过它我们可以使用Hibernate在外部缓存提供者中缓存频繁访问的数据。在这种情况下,缓存是在会话之外维护的,并且可以在微服务堆栈之间共享(如上面的例子)。
Hibernate支持与大多数流行的缓存提供者(如Redis、Ignite、NCache等)的L2缓存。
NCache 是市面上最受欢迎的分布式缓存提供者之一。它提供了几个功能,并支持与流行的编程堆栈(如.NET、Java等)的集成。
NCache有几种版本 — 开源、专业和企业版,你可以根据它们提供的功能进行选择。
NCache支持作为L2缓存与Hibernate 集成,还支持查询缓存。通过使用外部分布式缓存集群,我们可以确保在规模化环境中频繁访问的实体被缓存并在微服务之间使用,而不会对数据库层产生不必要的负载。这样,数据库调用将尽可能保持最小化,并且应用程序性能也得到了优化。
要开始,让我们向Spring Boot项目中添加必要的包。为了演示,我将使用一个使用Hibernate ORM与关系性数据库(MySQL设置)一起工作的JPA Repository。
我的pom.xml文件中的依赖关系如下:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
我的JPARepository读取和写入一个名为books的表在我的MySQL数据库中。Repository和实体看起来如下:
package com.myjpa.helloapp.repositories;
import com.myjpa.helloapp.models.entities.Book;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface BookRepository extends JpaRepository<Book, Integer> {}
package com.myjpa.helloapp.models.entities;
import jakarta.persistence.*;
import java.util.Date;
import org.hibernate.annotations.CreationTimestamp;
@Entity(name = "Book")
@Table(name = "Book")
public class Book {
@Id @GeneratedValue(strategy = GenerationType.AUTO) private int bookId;
@Column(name = "book_name") private String bookName;
@Column(name = "isbn") private String isbn;
@CreationTimestamp @Column(name = "created_date") private Date createdDate;
public Book() {}
public Book(String bookName, String isbn) {
this.bookName = bookName;
this.isbn = isbn;
}
public int getBookId() {
return bookId;
}
public String getBookName() {
return bookName;
}
public String getIsbn() {
return isbn;
}
public Date getCreatedDate() {
return createdDate;
}
}
一个BookService
与这个Repository进行交互,并提供GET
和INSERT
功能。
package com.myjpa.helloapp.services;
import com.myjpa.helloapp.models.entities.Book;
import com.myjpa.helloapp.repositories.BookRepository;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BookService {
@Autowired
private BookRepository repository;
public int createNew(String bookName, String isbn) {
var book = new Book(bookName, isbn);
// save the entity
repository.save(book);
// commit the changes
repository.flush();
// return the generated id
var bookId = book.getBookId();
return bookId;
}
public Book findBook(int id) {
var entity = repository.findById(id);
if (entity.isPresent()) {
return entity.get();
}
return null;
}
}
虽然这个设置运行得很好,但我们还没有为其添加任何缓存。让我们看看如何将缓存集成到具有NCache作为提供者的Hibernate中。
要将NCache与Hibernate集成,我们将向我们的项目中添加两个更多的依赖项。如下所示:
<dependency>
<groupId>com.alachisoft.ncache</groupId>
<artifactId>ncache-hibernate</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-jcache</artifactId>
<version>6.4.2.Final</version>
</dependency>
我们还将添加一个Hibernate.cfg.xml文件,在这里我们将配置第二级缓存和以下详细信息:
<hibernate-configuration>
<session-factory>
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.region.factory_class">JCacheRegionFactory</property>
<property name="hibernate.javax.cache.provider" >com.alachisoft.ncache.hibernate.jcache.HibernateNCacheCachingProvider</property>
<property name="ncache.application_id">booksapi</property>
</session-factory>
</hibernate-configuration>
在Entity Book的顶部,我们将添加一个注释,为该实体设置缓存状态:
@Entity(name = "Book")
@Table(name = "Book")
@Cache(region = "demoCache", usage = CacheConcurrencyStrategy.READ_WRITE)
public class Book {}
我指示我的实体将被缓存在区域demoCache
,这基本上是我的缓存集群名称。
我还将我的client.nconf和config.nconf文件放在我的项目的根目录,这些文件包含有关缓存集群及其网络详细信息的信息。
客户端.nconf如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Client configuration file is used by client to connect to out-proc caches.
Light weight client also uses this configuration file to connect to the remote caches.
This file is automatically generated each time a new cache/cluster is created or
cache/cluster configuration settings are applied.
-->
<configuration>
<ncache-server connection-retries="5" retry-connection-delay="0" retry-interval="1"
command-retries="3" command-retry-interval="0.1" client-request-timeout="90"
connection-timeout="5" port="9800" local-server-ip="192.168.0.108" enable-keep-alive="False"
keep-alive-interval="0" />
<cache id="demoCache" client-cache-id="" client-cache-syncmode="optimistic"
skip-client-cache-if-unavailable="False" reconnect-client-cache-interval="10"
default-readthru-provider="" default-writethru-provider="" load-balance="True"
enable-client-logs="True" log-level="info">
<server name="192.168.0.108" />
</cache>
</configuration>
当我使用这个设置运行我的应用程序并对单个书籍执行GET操作时,Hibernate会在 NCache集群中查找实体并返回缓存实体;如果不存在,它会显示缓存丢失。
Hibernate 的另一个特性,NCache 完全支持的是 查询缓存。在这种方法中,查询结果集可以被缓存以供频繁访问的数据。这样可以确保数据库不会频繁查询频繁访问的数据。这仅适用于 HQL(Hibernate 查询语言)查询。
要启用查询缓存,我只需在下面的 Hibernate.cfg.xml 中添加另一行:
<property name="hibernate.cache.use_query_cache">true</property>
在仓库中,我将创建另一个方法来运行特定的查询,并且结果将被缓存。
@Repository
public interface BookRepository extends JpaRepository<Book, Integer> {
@Query(value = "SELECT p FROM Book p WHERE bookName like 'T%'")
@Cacheable(value = "demoCache")
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY, region = "demoCache")
@QueryHints(value = { @QueryHint(name = "org.hibernate.cacheable", value = "true") })
public List<Book> findAllBooks();
}
在这个方法中,我查询所有以字母 T 开头的书籍,并且结果集将被缓存。为此,我将添加一个查询提示,将缓存设置为 true。
当我们调用这个方法的 API 时,我们可以看到整个数据集现在都被缓存了。
缓存是构建分布式应用程序中最常用的策略之一。在微服务架构中,一个应用程序基于负载可以扩展到 X 倍,在频繁访问数据库获取数据可能是代价高昂的。
NCache 等缓存提供程序为使用 Hibernate 查询数据库的 Java 微服务提供了一种简单且可插拔的解决方案。在本文中,我们已经看到了如何将 NCache 用作 Hibernate 的 L2 缓存 并将其用于缓存单个实体和查询缓存。
推荐阅读: 3.常用集合框架底层数据结构
本文链接: 如何在Spring Boot应用程序中使用JPA Hibernate集成NCache进行缓存