boutss
December 21, 2022, 11:25am
1
Hello,
I have a collection that has a return variable that can be of multiple types and therefore uses a ROLE (or D-TYPE) column.
For this return variable I cannot use @ManyToOne because it does not allow the use of ROLE columns.
So I use an @ANY
Everything works except that the ROLE column (here ELEMENTDETRAVAIL_ROLE) is not updated during the persisterCollection.
Do you have any idea how to handle this case?
Thanking you.
public class Agent implements IElementDeTravail
...
@OneToMany
@JoinColumn(name = "ELEMENTDETRAVAIL_ID")
private Set<Tache> taches = new HashSet<>();
public class Tache
....
@Any(metaDef = "IElementDeTravailMetaDef", metaColumn = @Column(name = "ELEMENTDETRAVAIL_ROLE"))
@JoinColumn(name = "ELEMENTDETRAVAIL_ID")
private IElementDeTravail elementDeTravail;
For information, we are migrating to hibernate 6.1.5 but I don’t think it changes much, except the @AnyValue and the metadef
22/12/2022 : small correction there was no mappedby which is not compatible with the @JoinColumn
beikov
December 21, 2022, 11:53am
2
boutss
December 21, 2022, 1:05pm
3
I do this as soon as possible. Thanks very much.
boutss
December 22, 2022, 4:43pm
4
In fact to be more precise the real problem is rather with the use of mappedBy (normal use).
And there it tells us the collection foreign key mapping has wrong number of column.
Logical because the primary key of Agent consists only of an ID column while the foreign key of two columns ID + ROLE
Caused by: org.hibernate.MappingException: collection foreign key mapping has wrong number of columns: org.hibernate.bugs.entity.Affaire.taches type: long
at org.hibernate.mapping.Collection.validate(Collection.java:379)
at org.hibernate.mapping.Set.validate(Set.java:51)
at org.hibernate.boot.internal.MetadataImpl.validate(MetadataImpl.java:380)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:301)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:415)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1425)
I add the source code of the TU.
Do I still create a JIRA ticket?
boutss
December 22, 2022, 4:45pm
5
package org.hibernate.bugs.entity;
import java.util.Set;
import jakarta.persistence.*;
@Entity
@DiscriminatorColumn
@DiscriminatorValue("affaire")
public class Affaire implements IElementDeTravail {
@Id
@GeneratedValue
private Long id;
@OneToMany(mappedBy = "elementDeTravail", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<Tache> taches;
public void setId(final Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public Set<Tache> getTaches() {
return taches;
}
public void setTaches(final Set<Tache> taches) {
this.taches = taches;
}
}
package org.hibernate.bugs.entity;
import jakarta.persistence.*;
import org.hibernate.annotations.*;
import org.hibernate.annotations.JavaType;
import org.hibernate.type.descriptor.java.*;
@Entity
public class Tache {
@Id
@GeneratedValue
private Long id;
@Any
@AnyDiscriminator(DiscriminatorType.STRING)
@AnyDiscriminatorValue(discriminator = "affaire", entity = Affaire.class)
@AnyKeyJavaClass(Long.class)
@Column(name = "ELEMENTDETRAVAIL_ROLE")
@JoinColumn(name = "ELEMENTDETRAVAIL_ID")
private IElementDeTravail elementDeTravail;
public void setId(final Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public IElementDeTravail getElementDeTravail() {
return elementDeTravail;
}
public void setElementDeTravail(final IElementDeTravail objetMaitre) {
this.elementDeTravail = objetMaitre;
}
}
package org.hibernate.bugs;
import java.util.Set;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;
import org.hibernate.Session;
import org.hibernate.bugs.entity.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* This template demonstrates how to develop a test case for Hibernate ORM, using the Java Persistence API.
*/
public class JPAUnitTestCase {
private EntityManagerFactory entityManagerFactory;
@Before
public void init() {
entityManagerFactory = Persistence.createEntityManagerFactory( "templatePU" );
}
@After
public void destroy() {
entityManagerFactory.close();
}
// Entities are auto-discovered, so just add them anywhere on class-path
// Add your tests, using standard JUnit.
@Test
public void hhh123Test() throws Exception {
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
// Do stuff...
Affaire affaire = new Affaire();
final Set<Tache> taches = Set.of(new Tache(), new Tache());
affaire.setTaches(taches);
entityManager.persist(affaire);
entityManager.flush();
entityManager.clear();
// KO here
final Affaire affaireReload = entityManager.unwrap(Session.class).byId(affaire.getClass()).load(affaire.getId());
entityManager.getTransaction().commit();
entityManager.close();
}
}
beikov
December 23, 2022, 12:51pm
6
You’re not the first one to ask for this. You can track this issue: [HHH-15722] - Hibernate JIRA
boutss
December 23, 2022, 1:43pm
7
It’s actually the same issue.
I wait then, no need to recreate a JIRA ticket that would be duplicated.
Thanks to you
boutss
October 13, 2023, 8:03am
8
Do you know if this topic will be taken into consideration?
Because in our project, we are in the process of replacing mappedBy
with @JoinColumn
everywhere for relationships with @Any
.
beikov
October 13, 2023, 12:57pm
9
Do you know if this topic will be taken into consideration?
Depends what you mean by “taken into consideration”. The Hibernate ORM team is willing to review and work through a PR contributed by the community, but we do not actively work on this or have plans to do so in the future.
boutss
October 13, 2023, 1:33pm
10
If it was on the to-do list or not at all ^^.
And also, if it’s expected to use ‘mappedBy’ with an ‘@Any ’ soon.
If yes, I can look into it with my knowledge to see if I can solve the issue, but I was worried it might be outside my expertise.
However, I can give it a try and provide feedback on this post if that’s okay with you
boutss
March 13, 2024, 3:39pm
12
I would have liked to know if this possibility of being able to use mappedBy with an @Any was on the list of planned features? Because on our side, we have more than a hundred cases like this.
What surprises me is that we seem to be the only ones, or few, asking for it.
I’m not demanding anything at all, just asking!
beikov
March 13, 2024, 4:04pm
13
No, it’s not planned at this time or anytime in the future. At this point, it’s up to the community to drive this forward.
boutss
March 13, 2024, 4:10pm
14
Ok, thank you, that’s clear ^^, I’ll check on my end.
boutss
April 3, 2024, 1:57pm
15
I note here my modification because i cannot modify a old post
/*
* 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.mapping.manytoone;
import java.util.Set;
import org.hibernate.Session;
import org.hibernate.annotations.Any;
import org.hibernate.annotations.AnyDiscriminator;
import org.hibernate.annotations.AnyDiscriminatorValue;
import org.hibernate.annotations.AnyKeyJavaClass;
import org.hibernate.cfg.JdbcSettings;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import static jakarta.persistence.DiscriminatorType.STRING;
/**
* @author Andrea Boriero
*/
@Jpa(
annotatedClasses = {
ManyToOneWithAnyTest.Library.class,
ManyToOneWithAnyTest.Book.class
},
integrationSettings = @Setting(name = JdbcSettings.SHOW_SQL, value = "true")
)
@JiraKey("HHH-15722")
public class ManyToOneWithAnyTest {
@Test
public void basicTest(EntityManagerFactoryScope scope) {
scope.inTransaction(
entityManager -> {
Library library = new Library();
final Set<Book> books = Set.of( new Book(), new Book() );
library.setBooks( books );
entityManager.persist( library );
entityManager.flush();
entityManager.clear();
library = entityManager.unwrap( Session.class )
.byId( library.getClass() )
.load( library.getId() );
Assertions.assertNotNull( library );
}
);
}
@Entity(name = "library")
@Table(name = "TLIBRARY")
public static class Library implements Store {
@Id
@GeneratedValue
private Long id;
@OneToMany(mappedBy = "store", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<Book> books;
public void setId(final Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public Set<Book> getBooks() {
return books;
}
public void setBooks(final Set<Book> books) {
books.forEach( book -> book.setStore( this ) );
this.books = books;
}
}
@Entity(name = "book")
@Table(name = "TBOOK")
public static class Book {
@Id
@GeneratedValue
private Long id;
@Any
@AnyDiscriminator(STRING)
@AnyDiscriminatorValue(discriminator = "library", entity = Library.class)
@AnyKeyJavaClass(Long.class)
@Column(name = "STORE_ROLE")
@JoinColumn(name = "STORE_ID")
private Store store;
public void setId(final Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public Store getStore() {
return store;
}
public void setStore(final Store store) {
this.store = store;
}
}
public interface Store {
}
}
boutss
April 4, 2024, 1:11pm
16
1 Like