Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ public Generator createGenerator(RuntimeModelCreationContext context) {
}

public Property copy() {
final Property property = new Property();
final Property property = this instanceof SyntheticProperty ? new SyntheticProperty() : new Property();
property.setName( getName() );
property.setValue( getValue() );
property.setCascade( getCascade() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@
import static java.util.Collections.unmodifiableMap;
import static org.hibernate.metamodel.internal.InjectionHelper.injectField;


/**
* Defines a context for storing information during the building of the {@link MappingMetamodelImpl}.
* <p>
Expand Down Expand Up @@ -274,9 +273,10 @@ public Map<String, IdentifiableDomainType<?>> getIdentifiableTypesByName() {
attribute = factoryFunction.apply( entityType, genericProperty );
if ( !property.isGeneric() ) {
final PersistentAttribute<X, ?> concreteAttribute = factoryFunction.apply( entityType, property );
@SuppressWarnings("unchecked")
final AttributeContainer<X> attributeContainer = (AttributeContainer<X>) entityType;
attributeContainer.getInFlightAccess().addConcreteGenericAttribute( concreteAttribute );
if ( concreteAttribute != null ) {
@SuppressWarnings("unchecked") final AttributeContainer<X> attributeContainer = (AttributeContainer<X>) entityType;
attributeContainer.getInFlightAccess().addConcreteGenericAttribute( concreteAttribute );
}
}
}
else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.orm.test.manytomany.generic;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.junit.jupiter.api.Test;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;

@Jpa(
annotatedClasses = {
ManyToManyGenericTest.NodeTree.class,
ManyToManyGenericTest.GenericNode.class
}
)
public class ManyToManyGenericTest {

@Test
void testSelfReferencingGeneric(final EntityManagerFactoryScope scope) {
final UUID treeId = scope.fromTransaction(em -> {
final NodeTree tree = new NodeTree();
final GenericNode<?> root = new GenericNode<>();
root.tree = tree;
final GenericNode<?> branch = new GenericNode<>();
branch.tree = tree;
tree.nodes.add(root);
tree.nodes.add(branch);
root.children.add(branch);
em.persist(tree);
return tree.id;
});

final NodeTree nodeTree = scope.fromEntityManager(em -> em.find(NodeTree.class, treeId));

assertThat(nodeTree, is(notNullValue()));
assertThat(nodeTree.id, is(treeId));
assertThat(nodeTree.nodes, iterableWithSize(2));
assertThat(nodeTree.nodes, containsInAnyOrder(List.of(
hasProperty("children", iterableWithSize(1)),
hasProperty("children", emptyIterable())
)));
}

@Entity(name = "tree")
public static class NodeTree {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
public UUID id;

@OneToMany(mappedBy = "tree", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
public Set<GenericNode<?>> nodes = new HashSet<>();
}

@Entity(name = "node")
public static class GenericNode<T> {

@Id
@GeneratedValue(strategy = GenerationType.UUID)
public UUID id;

@ManyToOne(optional = false)
@JoinColumn(name = "TREE_ID")
public NodeTree tree;

@ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.DETACH})
@JoinTable(name = "NODE_CHILDREN",
joinColumns = {@JoinColumn(name = "TREE_ID", referencedColumnName = "TREE_ID"), @JoinColumn(name = "NODE_ID", referencedColumnName = "ID")},
inverseJoinColumns = {@JoinColumn(name = "CHILD_ID", referencedColumnName = "ID")}
)
private final Set<GenericNode<?>> children = new HashSet<>();

public Set<GenericNode<?>> getChildren() {
return children;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.orm.test.manytomany.generic;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.junit.jupiter.api.Test;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.emptyIterable;
import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.iterableWithSize;
import static org.hamcrest.Matchers.notNullValue;

@Jpa(
annotatedClasses = {
ManyToManyNonGenericTest.NodeTree.class,
ManyToManyNonGenericTest.Node.class
}
)
public class ManyToManyNonGenericTest {

@Test
void testSelfReferencingGeneric(final EntityManagerFactoryScope scope) {
final UUID treeId = scope.fromTransaction(em -> {
final NodeTree tree = new NodeTree();
final Node root = new Node();
root.tree = tree;
final Node branch = new Node();
branch.tree = tree;
tree.nodes.add(root);
tree.nodes.add(branch);
root.children.add(branch);
em.persist(tree);
return tree.id;
});

final NodeTree nodeTree = scope.fromEntityManager(em -> em.find(NodeTree.class, treeId));

assertThat(nodeTree, is(notNullValue()));
assertThat(nodeTree.id, is(treeId));
assertThat(nodeTree.nodes, iterableWithSize(2));
assertThat(nodeTree.nodes, containsInAnyOrder(List.of(
hasProperty("children", iterableWithSize(1)),
hasProperty("children", emptyIterable())
)));
}

@Entity(name = "tree")
public static class NodeTree {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
public UUID id;

@OneToMany(mappedBy = "tree", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
public Set<Node> nodes = new HashSet<>();
}

@Entity(name = "node")
public static class Node {

@Id
@GeneratedValue(strategy = GenerationType.UUID)
public UUID id;

@ManyToOne(optional = false)
@JoinColumn(name = "TREE_ID")
public NodeTree tree;

@ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.DETACH})
@JoinTable(name = "NODE_CHILDREN",
joinColumns = {@JoinColumn(name = "TREE_ID", referencedColumnName = "TREE_ID"), @JoinColumn(name = "NODE_ID", referencedColumnName = "ID")},
inverseJoinColumns = {@JoinColumn(name = "CHILD_ID", referencedColumnName = "ID")}
)
private final Set<Node> children = new HashSet<>();

public Set<Node> getChildren() {
return children;
}

@Override
public String toString() {
return "node [%s] parent of %s".formatted(id, children.stream().map(n -> n.id).toList());
}
}
}
Loading