Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: hibernate/hibernate-orm
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 6.2.35
Choose a base ref
...
head repository: hibernate/hibernate-orm
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 6.2.36
Choose a head ref
  • 3 commits
  • 4 files changed
  • 2 contributors

Commits on Mar 9, 2025

  1. Post-steps for release : 6.2.35.Final

    Hibernate-CI committed Mar 9, 2025
    Copy the full SHA
    7ae0973 View commit details

Commits on Mar 14, 2025

  1. HHH-19246 Traverse entity graph also for explicit join fetches

    beikov committed Mar 14, 2025
    Copy the full SHA
    e1b1b2d View commit details

Commits on Mar 16, 2025

  1. Copy the full SHA
    808b642 View commit details
9 changes: 9 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -3,6 +3,15 @@ Hibernate 6 Changelog

Note: Please refer to JIRA to learn more about each issue.

Changes in 6.2.36.Final (March 16, 2025)
------------------------------------------------------------------------------------------------------------------------

https://hibernate.atlassian.net/projects/HHH/versions/32878

** Bug
* [HHH-19246] - Fetch join makes partially covered EntityGraph ineffective


Changes in 6.2.35.Final (March 09, 2025)
------------------------------------------------------------------------------------------------------------------------

2 changes: 1 addition & 1 deletion gradle/version.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
hibernateVersion=6.2.35.Final
hibernateVersion=6.2.36.Final
Original file line number Diff line number Diff line change
@@ -7891,6 +7891,15 @@ private Fetch createFetch(FetchParent fetchParent, Fetchable fetchable, Boolean
joined = true;
alias = fetchedJoin.getExplicitAlias();
explicitFetch = true;

if ( entityGraphTraversalState != null ) {
// Still do traverse the entity graph even if we encounter a fetch join
traversalResult = entityGraphTraversalState.traverse(
fetchParent,
fetchable,
isKeyFetchable
);
}
}
else {
fetchablePath = resolvedNavigablePath;
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.test.entitygraph;

import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.IdClass;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.NamedAttributeNode;
import jakarta.persistence.NamedEntityGraph;
import jakarta.persistence.NamedSubgraph;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import org.hibernate.Hibernate;
import org.hibernate.jpa.SpecHints;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

@DomainModel(
annotatedClasses = {
EntityGraphAndJoinFetchTest.OrderItem.class,
EntityGraphAndJoinFetchTest.Order.class,
EntityGraphAndJoinFetchTest.Product.class
}
)
@SessionFactory
@JiraKey( value = "HHH-19246")
public class EntityGraphAndJoinFetchTest {

private static final Long ORDER_ID = 1l;
private static final Long PRODUCT_ID = 2l;

private static final String NAMED_GRAPH_NAME = "Order.fetchAll";
private static final String NAMED_SUBGRAPH_NAME = "OrderItem.fetchAll";


@BeforeAll
public void setUp(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
Order order = new Order( ORDER_ID, new BigDecimal( 1000 ) );
Product product = new Product( PRODUCT_ID, "laptop" );
OrderItem orderItem = new OrderItem( product, order );
order.getItems().add( orderItem );

session.persist( order );
session.persist( product );
session.persist( orderItem );
}
);
}

@Test
public void testJoinFetchBeingSubsetOfGraph(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
Order order = session.createQuery( "FROM Order e LEFT JOIN FETCH e.items", Order.class )
.setHint(
SpecHints.HINT_SPEC_LOAD_GRAPH,
scope.getSessionFactory().createEntityManager().getEntityGraph( NAMED_GRAPH_NAME )
)
.getSingleResult();
assertTrue( Hibernate.isInitialized( order.getItems() ), "OrderItems have not been fetched" );
assertEquals( 1, order.getItems().size(), "OrderItems have not been fetched" );
assertTrue( Hibernate.isInitialized( order.getItems().iterator().next().getProduct() ), "Product has not been fetched" );
}
);
}

@Entity(name = "OrderItem")
@IdClass(OrderItem.PK.class)
public static class OrderItem {

@Id
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "product_id")
private Product product;

@Id
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id")
private Order order;

public OrderItem() {
}

public OrderItem(Product product, Order order) {
this.product = product;
this.order = order;
}

public Product getProduct() {
return product;
}

public Order getOrder() {
return order;
}

public static class PK implements Serializable {
private Long product;
private Long order;

@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
PK pk = (PK) o;
return Objects.equals( product, pk.product ) && Objects.equals( order, pk.order );
}

@Override
public int hashCode() {
return Objects.hash( product, order );
}
}
}


@Entity(name = "Order")
@Table(name = "ORDER_TABLE")
@NamedEntityGraph(
name = NAMED_GRAPH_NAME,
attributeNodes = {
@NamedAttributeNode(value = "items", subgraph = NAMED_SUBGRAPH_NAME)
},
subgraphs = {
@NamedSubgraph(name = NAMED_SUBGRAPH_NAME, attributeNodes = {@NamedAttributeNode("product")})
}
)
public static class Order {

@Id
private Long id;

private BigDecimal total;
@OneToMany(mappedBy = "order")
private Set<OrderItem> items = new HashSet<>();

public Order() {
}

public Order(Long id, BigDecimal total) {
this.id = id;
this.total = total;
}

public Long getId() {
return id;
}

public BigDecimal getTotal() {
return total;
}

public Set<OrderItem> getItems() {
return items;
}
}

@Entity(name = "Product")
public static class Product {

@Id
private Long id;

private String name;

public Product() {
}

public Product(Long id, String name) {
this.id = id;
this.name = name;
}

public Long getId() {
return id;
}

public String getName() {
return name;
}

}
}