diff --git a/hibernate-cache-tour-postgres/.gitignore b/hibernate-cache-tour-postgres/.gitignore
new file mode 100644
index 00000000..a2b7bde0
--- /dev/null
+++ b/hibernate-cache-tour-postgres/.gitignore
@@ -0,0 +1,5 @@
+target/
+.idea/
+*.iml
+.vscode/
+.DS_Store
diff --git a/hibernate-cache-tour-postgres/README.md b/hibernate-cache-tour-postgres/README.md
new file mode 100644
index 00000000..f4ade0c6
--- /dev/null
+++ b/hibernate-cache-tour-postgres/README.md
@@ -0,0 +1,52 @@
+# hibernate-cache-tour-postgres
+
+A Spring Boot 3 + Hibernate 6 + Postgres 16 reproducer that exercises two
+specific drift signatures in keploy's Postgres v3 logical codec:
+
+1. **Issue #1 — pgjdbc `prepareThreshold` flip.**
+ pgjdbc defaults to `prepareThreshold=5`: the same SQL handle is sent in
+ text format for the first 4 calls and switches to binary on the 5th
+ execution. Each `WHERE id = ?` JpaRepository finder hit `>= 8` times will
+ straddle that boundary — the recorded mocks for one SQL hash carry mixed
+ format codes; replay only matches when the v3 codec reconciles them.
+
+2. **Issue #3 — Hibernate StatementCache classification drift.**
+ With `hibernate.cache.use_query_cache` and `use_second_level_cache`
+ enabled (EHCache 3.x via JCache), per-test cache eviction triggered by
+ the `keploy.io/test-name` header changes the cache hit/miss pattern
+ between record and replay, which classifies the same SQL differently
+ in keploy's StatementCache.
+
+## Endpoints
+
+| Method | Path | Notes |
+|--------|----------------------------|------------------------------------|
+| POST | /customer | Create + return id |
+| GET | /customer/{id} | `WHERE id = ?` (single int4 bind) |
+| GET | /customer/{id}/tags | `WHERE customer_id = ?` |
+| GET | /tags?priority={p} | `WHERE priority = ?` |
+| POST | /tag | Create tag + return id |
+
+## Schema
+
+`customer(id SERIAL, name, email)` and `customer_tag(id SERIAL,
+customer_id REFERENCES customer, tag VARCHAR(64), priority INT)`.
+
+`init.sql` seeds 4 customers (ids 1..4) and 8 tags. The CI exerciser hits
+each id-keyed endpoint 8 times against `1 + (i-1) % 4`, so the same id
+recurs every 4 calls — the 5th call on a given handle is the
+prepareThreshold flip.
+
+## Build / run
+
+```
+mvn -DskipTests package
+SPRING_DATASOURCE_URL=jdbc:postgresql://127.0.0.1:5432/hibcache \
+SPRING_DATASOURCE_USERNAME=hibcache \
+SPRING_DATASOURCE_PASSWORD=hibcache \
+java -jar target/hibernate-cache-tour.jar
+```
+
+The CI harness lives in `keploy/integrations` at
+`.ci/scripts/java/hibernate-cache-tour-postgres/` and drives the full
+record → replay regression.
diff --git a/hibernate-cache-tour-postgres/pom.xml b/hibernate-cache-tour-postgres/pom.xml
new file mode 100644
index 00000000..2317f12a
--- /dev/null
+++ b/hibernate-cache-tour-postgres/pom.xml
@@ -0,0 +1,102 @@
+
+
+
+ 4.0.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.2.5
+
+
+
+ com.keploy
+ hibernate-cache-tour-postgres
+ 0.0.1-SNAPSHOT
+ hibernate-cache-tour-postgres
+ Repro for pgjdbc prepareThreshold + Hibernate StatementCache drift
+
+
+ 17
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-cache
+
+
+ org.hibernate.orm
+ hibernate-jcache
+
+
+ org.ehcache
+ ehcache
+ jakarta
+
+
+
+ org.postgresql
+ postgresql
+ runtime
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+ hibernate-cache-tour
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/hibernate-cache-tour-postgres/src/main/java/com/keploy/hibernatecachetour/HibernateCacheTourApplication.java b/hibernate-cache-tour-postgres/src/main/java/com/keploy/hibernatecachetour/HibernateCacheTourApplication.java
new file mode 100644
index 00000000..bb5a2cae
--- /dev/null
+++ b/hibernate-cache-tour-postgres/src/main/java/com/keploy/hibernatecachetour/HibernateCacheTourApplication.java
@@ -0,0 +1,18 @@
+package com.keploy.hibernatecachetour;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cache.annotation.EnableCaching;
+
+/**
+ * Entry point. EnableCaching switches on Spring's cache abstraction so the
+ * Hibernate L2/query cache wired in application.properties is actually used
+ * end-to-end.
+ */
+@SpringBootApplication
+@EnableCaching
+public class HibernateCacheTourApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(HibernateCacheTourApplication.class, args);
+ }
+}
diff --git a/hibernate-cache-tour-postgres/src/main/java/com/keploy/hibernatecachetour/config/TestBoundaryInterceptor.java b/hibernate-cache-tour-postgres/src/main/java/com/keploy/hibernatecachetour/config/TestBoundaryInterceptor.java
new file mode 100644
index 00000000..1349172f
--- /dev/null
+++ b/hibernate-cache-tour-postgres/src/main/java/com/keploy/hibernatecachetour/config/TestBoundaryInterceptor.java
@@ -0,0 +1,62 @@
+package com.keploy.hibernatecachetour.config;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.persistence.EntityManagerFactory;
+import org.hibernate.Cache;
+import org.hibernate.SessionFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * Reads the keploy.io/test-name header (and its alias X-Keploy-Test-Name)
+ * and evicts the entire Hibernate L2 cache when the test name changes.
+ * This is what creates the per-test classification boundary that drives
+ * issue #3 — StatementCache drift between record and replay caused by
+ * differing cache hit/miss patterns.
+ *
+ * The eviction is intentionally aggressive (cache.evictAllRegions): it
+ * forces Hibernate to issue WHERE id = ? and WHERE customer_id = ?
+ * against Postgres on the first request after each boundary, so the
+ * recorded mocks contain the full pgjdbc bind dance for that path.
+ * Without eviction every request after the first would hit the L2
+ * cache and never reach pgjdbc, defeating the prepareThreshold flip
+ * we want to surface for issue #1.
+ */
+@Component
+public class TestBoundaryInterceptor implements HandlerInterceptor, WebMvcConfigurer {
+
+ private final EntityManagerFactory emf;
+ private volatile String currentTestName = "";
+
+ public TestBoundaryInterceptor(EntityManagerFactory emf) {
+ this.emf = emf;
+ }
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
+ String name = request.getHeader("keploy.io/test-name");
+ if (name == null || name.isEmpty()) {
+ name = request.getHeader("X-Keploy-Test-Name");
+ }
+ if (name == null) {
+ return true;
+ }
+ if (!name.equals(currentTestName)) {
+ currentTestName = name;
+ SessionFactory sf = emf.unwrap(SessionFactory.class);
+ Cache cache = sf.getCache();
+ if (cache != null) {
+ cache.evictAllRegions();
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ registry.addInterceptor(this);
+ }
+}
diff --git a/hibernate-cache-tour-postgres/src/main/java/com/keploy/hibernatecachetour/controller/CustomerController.java b/hibernate-cache-tour-postgres/src/main/java/com/keploy/hibernatecachetour/controller/CustomerController.java
new file mode 100644
index 00000000..74c35233
--- /dev/null
+++ b/hibernate-cache-tour-postgres/src/main/java/com/keploy/hibernatecachetour/controller/CustomerController.java
@@ -0,0 +1,91 @@
+package com.keploy.hibernatecachetour.controller;
+
+import com.keploy.hibernatecachetour.model.Customer;
+import com.keploy.hibernatecachetour.model.CustomerTag;
+import com.keploy.hibernatecachetour.repository.CustomerRepository;
+import com.keploy.hibernatecachetour.repository.CustomerTagRepository;
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.PersistenceContext;
+import org.springframework.http.ResponseEntity;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * REST surface that exercises the prepared-statement bind paths the v3
+ * codec must reconcile.
+ *
+ * Endpoints:
+ * POST /customer create + return id
+ * GET /customer/{id} single-bind WHERE id = ? (the canonical
+ * format-flip trigger)
+ * GET /customer/{id}/tags WHERE customer_id = ?
+ * GET /tags?priority=N WHERE priority = ?
+ * POST /tag insert + return id
+ *
+ * Each GET is intentionally a JpaRepository finder that compiles to a
+ * single-int-bind prepared statement. The exerciser hits each one >=8
+ * times (CI script) so pgjdbc's prepareThreshold (default 5) trips and
+ * the bind-format byte flips text -> binary mid-recording.
+ *
+ * The X-Keploy-Test-Name header (forwarded by the keploy recorder when
+ * configured with keploy.io/test-name) is read by an interceptor that
+ * evicts the L2 cache between logical tests — that's how issue #3
+ * (StatementCache classification drift) gets exercised deterministically.
+ */
+@RestController
+public class CustomerController {
+
+ private final CustomerRepository customers;
+ private final CustomerTagRepository tags;
+
+ @PersistenceContext
+ private EntityManager em;
+
+ public CustomerController(CustomerRepository customers, CustomerTagRepository tags) {
+ this.customers = customers;
+ this.tags = tags;
+ }
+
+ @PostMapping("/customer")
+ @Transactional
+ public ResponseEntity