FetchNotFoundException after Upgrade to 6.6.X

Hello everybody,

we have recently upgraded Spring Boot in our project from 3.2.6 to 3.4.2 and therefore also Hibernate from 6.4.8.Final to 6.6.5.Final.

After the upgrade, we are experiencing an issue with a query that throws a FetchNotFoundException: org.hibernate.FetchNotFoundException: Entity 'MyBEntity' with identifier value 'B1' does not exist. We tried some older versions and found that the queries still worked as expected in Hibernate 6.5.3.Final and stopped working from 6.6.0.Final onwards.

We were able to reproduce it using the test case template. You can find the test case, queries and entities in the code sample below.

package org.hibernate.bugs;

import custom.MyAEntity;
import custom.MyBEntity;
import custom.MyCEntity;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.NonUniqueResultException;
import jakarta.persistence.Persistence;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;

/**
 * This template demonstrates how to develop a test case for Hibernate ORM, using the Java Persistence API.
 */
class JPAUnitTestCase {

	private EntityManagerFactory entityManagerFactory;

	@BeforeEach
	void init() {
		entityManagerFactory = Persistence.createEntityManagerFactory( "templatePU" );
	}

	@AfterEach
	void destroy() {
		entityManagerFactory.close();
	}

	// Entities are auto-discovered, so just add them anywhere on class-path
	// Add your tests, using standard JUnit.
	@Test
	void testFetchNotFoundException() throws Exception {
		EntityManager entityManager = entityManagerFactory.createEntityManager();
		// Given
		entityManager.getTransaction().begin();
		var newB = new MyBEntity("B1", 1);
		var newC = new MyCEntity("C1");
		entityManager.persist(newB);
		entityManager.persist(newC);
		entityManager.getTransaction().commit();

		// When
		entityManager.getTransaction().begin();
		getB(entityManager, "B1");
		getAListForB(entityManager, "B1");
		var newAEntity = new MyAEntity();
		newAEntity.setId("A1");
		newAEntity.setBId("B1");
		newAEntity.setCId("C1");
		entityManager.merge(newAEntity);

		// Then
		assertDoesNotThrow(() -> getAListForC(entityManager, "C1", 1));

		entityManager.getTransaction().commit();
		entityManager.close();
	}

	private MyBEntity getB(EntityManager entityManager, String bId) {
		var query = entityManager.createQuery("""
			SELECT b
			FROM MyBEntity b
			WHERE b.id = :id
			""", MyBEntity.class);
		query.setParameter("id", bId);
		var list = query.getResultList();
		if (list == null || list.isEmpty()) return null;
		else if (list.size() > 1) throw new NonUniqueResultException();
		else return list.get(0);
	}

	private List<MyAEntity> getAListForB(EntityManager entityManager, String bId) {
		var query = entityManager.createQuery("""
			SELECT DISTINCT a
			FROM MyAEntity a
			JOIN a.c c
			JOIN a.b b
			where a.bId = :bId
			AND c.deletedOn IS NULL
			""", MyAEntity.class);
		query.setParameter("bId", bId);
		return query.getResultList();
	}

	private List<MyAEntity> getAListForC(EntityManager entityManager, String cId, Integer someNumber) {
		var query = entityManager.createQuery("""
			SELECT DISTINCT a
			FROM MyAEntity a
			JOIN FETCH a.c c
			JOIN FETCH a.b b
			where b.someNumber = :someNumber
			and a.cId = :cId
			""", MyAEntity.class);
		query.setParameter("cId", cId);
		query.setParameter("someNumber", someNumber);
		return query.getResultList();
	}
}

@Getter
@Setter
@EqualsAndHashCode(of = {"id"})
@Entity
public class MyAEntity {

    @Id
    @Column(name = "ID", nullable = false)
    private String id;

    @Column(name = "B_ID", nullable = false)
    private String bId;
    @Column(name = "C_ID", nullable = false)
    private String cId;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "B_ID", nullable = false, insertable = false, updatable = false)
    private MyBEntity b;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "C_ID", nullable = false, insertable = false, updatable = false)
    private MyCEntity c;
}

@Setter
@Getter
@Entity
public class MyBEntity {

    @Id
    @Column(name = "ID", nullable = false)
    private String id;

    @Column(name = "SOME_NUMBER", nullable = false)
    private Integer someNumber;

    @Setter(AccessLevel.NONE)
    @OneToMany(mappedBy = "b", fetch = FetchType.LAZY)
    private List<MyAEntity> aList;

    public MyBEntity() {}

    public MyBEntity(String id, Integer someNumber) {
        this.id = id;
        this.someNumber = someNumber;
    }
}

@Setter
@Getter
@Entity
public class MyCEntity {

    @Id
    @Column(name = "ID", nullable = false)
    private String id;

    @Column(name = "DELETED_ON")
    private Date deletedOn;

    @OneToMany(mappedBy = "c", fetch = FetchType.EAGER)
    private List<MyAEntity> aList;

    public MyCEntity() {}

    public MyCEntity(String id) {
        this.id = id;
    }
}

Side note: if we do a simple join without the FETCH in the query in getAListForC no exception is thrown.

Now for the question:
Did we stumble across a bug, is this a known behaviour change or are we doing something that is not supposed to be done this way? If so, could somebody provide an explanation or link to some piece of documentation.
The latter might be possible as this is part of some greater project with lots of legacy code (lots of weird stuff happening there).

Thank you for your help

Hi @J.Thiele, at first glance I don’t see anything wrong with what you’re trying to achieve here. If this used to work with the latest Hibernate 6.5 version, it might be a bug introduced in version 6.6. If the bug persists with the latest stable release, please open a new ticket in our issue tracker and attach that reproducer test case, thanks.

Thank you for the quick answer @mbladel. The issue persists in 6.6.11.Final. The ticket can be found under Jira.

1 Like

@mbladel Could you please share in which release this could be fixed?

@J.Thiele Did you find a fix for this fetchNotFound issue? In my application if I go back to 6.5.3 I am getting ehcache related exception.