Skip to content

Commit 2c7b878

Browse files
committed
Improve hibernate example and add resilience example using failsafe
1 parent c2befe9 commit 2c7b878

File tree

25 files changed

+717
-35
lines changed

25 files changed

+717
-35
lines changed

.github/dependabot.yml

+4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ updates:
2424
directory: examples/hibernate
2525
schedule:
2626
interval: daily
27+
- package-ecosystem: gradle
28+
directory: examples/resilience-failsafe
29+
schedule:
30+
interval: daily
2731
- package-ecosystem: gradle
2832
directory: examples/graal-native
2933
schedule:

.github/workflows/examples.yml

+4-1
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,12 @@ jobs:
101101
- name: Coalescing Bulkloader
102102
working-directory: examples/coalescing-bulkloader
103103
run: ./mvnw test
104-
- name: Hibernate JCache
104+
- name: Hibernate (jcache)
105105
working-directory: examples/hibernate
106106
run: ./gradlew build
107+
- name: Resilience (failsafe)
108+
working-directory: examples/resilience-failsafe
109+
run: ./gradlew build
107110
- name: Prepare for Graal Native Image
108111
uses: ./.github/actions/run-gradle
109112
with:

examples/graal-native/build.gradle.kts

+4-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ application {
1515
mainClass = "com.github.benmanes.caffeine.examples.graalnative.Application"
1616
}
1717

18-
tasks.test {
19-
useJUnitPlatform()
18+
testing.suites {
19+
val test by getting(JvmTestSuite::class) {
20+
useJUnitJupiter()
21+
}
2022
}
2123

2224
graalvmNative {

examples/graal-native/settings.gradle.kts

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ pluginManagement {
44
gradlePluginPortal()
55
}
66
}
7+
plugins {
8+
id("org.gradle.toolchains.foojay-resolver-convention") version "0.6.0"
9+
}
10+
711
dependencyResolutionManagement {
812
repositories {
913
mavenCentral()

examples/hibernate/README.md

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
The [Hibernate ORM] can be configured to use a [second-level cache] to reduce the number of accesses
1+
[Hibernate] can be configured to use a [second-level cache] to reduce the number of accesses
22
to the database by caching data in memory to be shared between sessions.
33

44
In [hibernate.conf](src/main/resources/hibernate.properties) specify the JCache provider and enable
@@ -29,6 +29,14 @@ caffeine.jcache {
2929
}
3030
```
3131

32+
Enable caching on the entity.
33+
34+
```java
35+
@Entity
36+
@Cache(usage = READ_WRITE)
37+
public class User { ... }
38+
```
39+
3240
Hibernate will then manage the cache to transparently avoid database calls.
3341

3442
```java
@@ -41,5 +49,5 @@ sessionFactory.fromSession(session -> session.get(User.class, id));
4149
assertThat(sessionFactory.getStatistics().getSecondLevelCacheHitCount()).isEqualTo(1);
4250
```
4351

44-
[Hibernate ORM]: https://hibernate.org/orm/
52+
[Hibernate]: https://hibernate.org/orm/
4553
[second-level cache]: https://docs.jboss.org/hibernate/orm/6.3/introduction/html_single/Hibernate_Introduction.html#second-level-cache-configuration

examples/hibernate/build.gradle.kts

+5-3
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,18 @@ plugins {
55

66
dependencies {
77
implementation(libs.bundles.hibernate)
8+
implementation(libs.bundles.log4j2)
89
implementation(libs.caffeine)
9-
implementation(libs.slf4j)
1010
runtimeOnly(libs.h2)
1111

1212
testImplementation(libs.junit)
1313
testImplementation(libs.truth)
1414
}
1515

16-
tasks.test {
17-
useJUnitPlatform()
16+
testing.suites {
17+
val test by getting(JvmTestSuite::class) {
18+
useJUnitJupiter()
19+
}
1820
}
1921

2022
java.toolchain.languageVersion = JavaLanguageVersion.of(

examples/hibernate/gradle/libs.versions.toml

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ caffeine = "3.1.7"
33
h2 = "2.2.220"
44
hibernate = "6.3.0.CR1"
55
junit = "5.10.0"
6+
log4j2 = "2.20.0"
67
slf4j = "2.0.7"
78
truth = "1.1.5"
89
versions = "0.47.0"
@@ -14,11 +15,13 @@ hibernate-core = { module = "org.hibernate.orm:hibernate-core", version.ref = "h
1415
hibernate-jcache = { module = "org.hibernate.orm:hibernate-jcache", version.ref = "hibernate" }
1516
hibernate-hikaricp = { module = "org.hibernate.orm:hibernate-hikaricp", version.ref = "hibernate" }
1617
junit = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
17-
slf4j = { module = "org.slf4j:slf4j-jdk14", version.ref = "slf4j" }
18+
log4j2-core = { module = "org.apache.logging.log4j:log4j-core", version.ref = "log4j2" }
19+
log4j2-slf4j = { module = "org.apache.logging.log4j:log4j-slf4j-impl", version.ref = "log4j2" }
1820
truth = { module = "com.google.truth:truth", version.ref = "truth" }
1921

2022
[bundles]
2123
hibernate = ["hibernate-core", "hibernate-jcache", "hibernate-hikaricp"]
24+
log4j2 = ["log4j2-core", "log4j2-slf4j"]
2225

2326
[plugins]
2427
versions = { id = "com.github.ben-manes.versions", version.ref = "versions" }

examples/hibernate/settings.gradle.kts

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
plugins {
2+
id("org.gradle.toolchains.foojay-resolver-convention") version "0.6.0"
3+
}
4+
15
dependencyResolutionManagement {
26
repositories {
37
mavenCentral()

examples/hibernate/src/main/java/com/github/benmanes/caffeine/examples/hibernate/Project.java

-5
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,10 @@
1515
*/
1616
package com.github.benmanes.caffeine.examples.hibernate;
1717

18-
import static jakarta.persistence.AccessType.FIELD;
1918
import static org.hibernate.annotations.CacheConcurrencyStrategy.READ_WRITE;
2019

2120
import org.hibernate.annotations.Cache;
2221

23-
import jakarta.persistence.Access;
24-
import jakarta.persistence.Cacheable;
2522
import jakarta.persistence.Entity;
2623
import jakarta.persistence.GeneratedValue;
2724
import jakarta.persistence.Id;
@@ -31,8 +28,6 @@
3128
* @author [email protected] (Ben Manes)
3229
*/
3330
@Entity
34-
@Cacheable
35-
@Access(FIELD)
3631
@Cache(usage = READ_WRITE)
3732
public class Project {
3833

examples/hibernate/src/main/java/com/github/benmanes/caffeine/examples/hibernate/Skill.java

-5
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,10 @@
1515
*/
1616
package com.github.benmanes.caffeine.examples.hibernate;
1717

18-
import static jakarta.persistence.AccessType.FIELD;
1918
import static org.hibernate.annotations.CacheConcurrencyStrategy.READ_WRITE;
2019

2120
import org.hibernate.annotations.Cache;
2221

23-
import jakarta.persistence.Access;
24-
import jakarta.persistence.Cacheable;
2522
import jakarta.persistence.Entity;
2623
import jakarta.persistence.GeneratedValue;
2724
import jakarta.persistence.Id;
@@ -30,8 +27,6 @@
3027
* @author [email protected] (Ben Manes)
3128
*/
3229
@Entity
33-
@Cacheable
34-
@Access(FIELD)
3530
@Cache(usage = READ_WRITE)
3631
public class Skill {
3732
@Id

examples/hibernate/src/main/java/com/github/benmanes/caffeine/examples/hibernate/User.java

-5
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,13 @@
1515
*/
1616
package com.github.benmanes.caffeine.examples.hibernate;
1717

18-
import static jakarta.persistence.AccessType.FIELD;
1918
import static org.hibernate.annotations.CacheConcurrencyStrategy.READ_WRITE;
2019

2120
import java.util.ArrayList;
2221
import java.util.List;
2322

2423
import org.hibernate.annotations.Cache;
2524

26-
import jakarta.persistence.Access;
27-
import jakarta.persistence.Cacheable;
2825
import jakarta.persistence.Entity;
2926
import jakarta.persistence.GeneratedValue;
3027
import jakarta.persistence.Id;
@@ -35,8 +32,6 @@
3532
* @author [email protected] (Ben Manes)
3633
*/
3734
@Entity
38-
@Cacheable
39-
@Access(FIELD)
4035
@Cache(usage = READ_WRITE)
4136
public class User {
4237
@Id

examples/hibernate/src/main/resources/hibernate.properties

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
1-
hibernate.dialect=org.hibernate.dialect.H2Dialect
2-
3-
hibernate.connection.provider_class=org.hibernate.hikaricp.internal.HikariCPConnectionProvider
4-
hibernate.connection.driver_class=org.h2.Driver
51
hibernate.connection.url=jdbc:h2:mem:test
62
hibernate.connection.username=sa
73

84
hibernate.show_sql=false
9-
hibernate.format_sql=true
5+
hibernate.format_sql=false
6+
hibernate.highlight_sql=true
107
hibernate.globally_quoted_identifiers=true
118

129
hibernate.hbm2ddl.auto=create-drop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
rootLogger.level = info
2+
rootLogger.appenderRefs = console
3+
rootLogger.appenderRef.console.ref = console
4+
5+
logger.hibernate.name = org.hibernate
6+
logger.hibernate.level = warn
7+
8+
logger.hikaricp.name = com.zaxxer.hikari
9+
logger.hikaricp.level = warn
10+
11+
appender.console.name = console
12+
appender.console.type = Console
13+
appender.console.layout.type = PatternLayout
14+
appender.console.layout.pattern = %highlight{[%p]} %c %m%n

examples/hibernate/src/test/java/com/github/benmanes/caffeine/examples/hibernate/HibernateCacheTest.java

-4
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@
1717

1818
import static com.github.benmanes.caffeine.examples.hibernate.HibernateSubject.assertThat;
1919

20-
import java.util.logging.Level;
21-
import java.util.logging.Logger;
22-
2320
import org.hibernate.SessionFactory;
2421
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
2522
import org.hibernate.cfg.Configuration;
@@ -36,7 +33,6 @@ public final class HibernateCacheTest {
3633

3734
@BeforeEach
3835
public void beforeEach() {
39-
Logger.getLogger("").setLevel(Level.WARNING);
4036
sessionFactory = new Configuration().addAnnotatedClass(User.class)
4137
.addAnnotatedClass(Skill.class).addAnnotatedClass(Project.class)
4238
.buildSessionFactory(new StandardServiceRegistryBuilder().build());
+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
[Failsafe's][failsafe] retry, timeout, and fallback strategies can be used to make the cache
2+
operations resiliant to intermittent failures.
3+
4+
### Retry
5+
A [retry policy][retry] will retry failed executions a certain number of times, with an optional
6+
delay between attempts.
7+
8+
```java
9+
var retryPolicy = RetryPolicy.builder()
10+
.withDelay(Duration.ofSeconds(1))
11+
.withMaxAttempts(3)
12+
.build();
13+
var failsafe = Failsafe.with(retryPolicy);
14+
15+
// Retry outside of the cache loader for synchronous calls
16+
Cache<K, V> cache = Caffeine.newBuilder().build();
17+
failsafe.get(() -> cache.get(key, key -> /* intermittent failures */ ));
18+
19+
// Optionally, retry inside the cache load for asynchronous calls
20+
AsyncCache<K, V> asyncCache = Caffeine.newBuilder().buildAsync();
21+
asyncCache.get(key, (key, executor) -> failsafe.getAsync(() -> /* intermittent failure */));
22+
```
23+
24+
### Timeout
25+
A [timeout policy][timeout] will cancel the execution if it takes too long to complete.
26+
27+
```java
28+
var retryPolicy = RetryPolicy.builder()
29+
.withDelay(Duration.ofSeconds(1))
30+
.withMaxAttempts(3)
31+
.build();
32+
var timeout = Timeout.builder(Duration.ofSeconds(1)).withInterrupt().build();
33+
var failsafe = Failsafe.with(timeout, retryPolicy);
34+
35+
Cache<K, V> cache = Caffeine.newBuilder().build();
36+
failsafe.get(() -> cache.get(key, key -> /* timeout */ ));
37+
```
38+
39+
### Fallback
40+
A [fallback policy][fallback] will provide an alternative result for a failed execution.
41+
42+
```java
43+
var retryPolicy = RetryPolicy.builder()
44+
.withDelay(Duration.ofSeconds(1))
45+
.withMaxAttempts(3)
46+
.build();
47+
var fallback = Fallback.of(/* fallback */);
48+
var failsafe = Failsafe.with(fallback, retryPolicy);
49+
50+
Cache<K, V> cache = Caffeine.newBuilder().build();
51+
failsafe.get(() -> cache.get(key, key -> /* failure */ ));
52+
```
53+
54+
[failsafe]: https://failsafe.dev
55+
[retry]: https://failsafe.dev/retry
56+
[timeout]: https://failsafe.dev/timeout
57+
[fallback]: https://failsafe.dev/fallback
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
plugins {
2+
`java-library`
3+
alias(libs.plugins.versions)
4+
}
5+
6+
dependencies {
7+
implementation(libs.caffeine)
8+
implementation(libs.failsafe)
9+
10+
testImplementation(libs.junit)
11+
testImplementation(libs.truth)
12+
}
13+
14+
testing.suites {
15+
val test by getting(JvmTestSuite::class) {
16+
useJUnitJupiter()
17+
}
18+
}
19+
20+
java.toolchain.languageVersion = JavaLanguageVersion.of(
21+
System.getenv("JAVA_VERSION")?.toIntOrNull() ?: 11)
22+
23+
tasks.withType<JavaCompile>().configureEach {
24+
javaCompiler = javaToolchains.compilerFor {
25+
languageVersion = java.toolchain.languageVersion
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[versions]
2+
caffeine = "3.1.7"
3+
failsafe = "3.3.2"
4+
junit = "5.10.0"
5+
truth = "1.1.5"
6+
versions = "0.47.0"
7+
8+
[libraries]
9+
caffeine = { module = "com.github.ben-manes.caffeine:caffeine", version.ref = "caffeine" }
10+
failsafe = { module = "dev.failsafe:failsafe", version.ref = "failsafe" }
11+
junit = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }
12+
truth = { module = "com.google.truth:truth", version.ref = "truth" }
13+
14+
[plugins]
15+
versions = { id = "com.github.ben-manes.versions", version.ref = "versions" }
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
distributionBase=GRADLE_USER_HOME
2+
distributionPath=wrapper/dists
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-rc-3-bin.zip
4+
networkTimeout=10000
5+
validateDistributionUrl=true
6+
zipStoreBase=GRADLE_USER_HOME
7+
zipStorePath=wrapper/dists

0 commit comments

Comments
 (0)