Spring-Data Jpa Inheritance: Keeping Entity Id's in Children Entity - java

I'm dealing with a couple of Entities with Tree like structures that were getting more complicated so I decided to create an abstract class for it so code was a bit more mainainable:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class TreeStructure<T extends TreeStructure>
{
#ManyToOne
protected T parent;
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
protected Set<T> children = new HashSet<>();
//...
Then I have two Entities which extend it:
#Entity(name = "TreeStructureOne")
public class TreeStructureOne extends TreeStructure<TreeStructureOne>
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonProperty("TreeStructureOne_id")
private long id;
And I basically want the database to be completely unaware of this TreeStructure abstraction and save all of the fields in each Entities tableand expected InheritanceType.TABLE_PER_CLASS to deal with that. But it seems I need to define the Id in the TreeStructure Entity at least or I get:
Invocation of init method failed; nested exception is org.hibernate.AnnotationException: No identifier specified for entity: TreeStructure
And I don't want to add an ID into the abstract class since this makes three tables in the database called: HT_TREE_STRUCTURE, HT_TREE_STRUCTURE_ONE and HT_TREE_STRUCTURE_TWO with one field ID each one.
Is there any solution to that?

Since TreeStructure is not an #Entity use only #MappedSuperclass
#MappedSuperclass
public abstract class TreeStructure<T extends TreeStructure> {
instead of #Entity and #Inheritance for the parent class.
You can find #MappedSuperclass in the Oracle JEE API documentation.

Related

Failed to lazily initialize a collection of role. Simple JPA findById

I'm using Spring and JPA (Hibernate with MySQL) and Lombok also.
Hi have this part of my entities:
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "entitya")
public class EntityA implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="ea_id")
Long id;
....
#ManyToOne
#JoinColumn(name="g_id", nullable=false)
private Group group;
....
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "group")
public class Group implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="g_id")
private Long id;
#OneToMany(mappedBy="group")
private List<EntityA> enitiesA = new ArrayList<>();
...
}
I implemented also the repository extends JPARepository.
Into my controllers, if I try to retrieve an EntityA by Id I obtain this exception:
failed to lazily initialize a collection of role: com.mytest.entity.Group.enitiesA, could not initialize proxy - no Session
For me it's strange because I need to retrieve only the object. I not use some getter methods on this. So, in theory, using the default fetch types, I don't need to have also the group list.
What's wrong?
Are you debugging your object with toString()?
In case it could be an error caused by the #Data annotation.
The generated toString() method contains all fields, so it might call the enitiesA variable, producing the lazy initialization error.
https://mdeinum.github.io/2019-02-13-Lombok-Data-Ojects-Arent-Entities/
Likely it's because you're accessing group.enitiesA outside of the transactional boundaries. If you want to do this, you can eager fetch them by adding eager fetch type to your OneToMany mapping such as
#OneToMany(mappedBy="group", fetch = FetchType.EAGER)
This will load the entire object graph when the parent is loaded.
If you still want to do lazy loading, look to encapsulate all of the calls into the children under the session that loaded the parent.

#AssociationOverride does not create #JoinColumn from scratch

I have a group of tables, that are all identical apart from their owner table, and the corresponding foreign keys to that table. I made it all generic thanks to Hibernate/JPA, but cannot pass the #JoinColumn information via #AssociationOverride since the name value for it is ignored, or not overridden at all.
For example;
#Data
#Entity
#Table(name = "etc")
#AssociationOverride(name = "parent", joinColumns = #JoinColumn(name = "id"))
public class RealEntity extends BaseEntity<ParentEntity, String> {
}
with;
#Data
#MappedSuperClass
public class BaseEntity<K, P> implements Serializable {
#EmbeddedId
protected Key<K> key = new Key<>();
#MapsId("fk")
#ManyToOne
#JsonBackReference
protected P parent;
#Data
#Embeddable
public static class Key<K> implements Serializable {
protected K fk;
#Column(name = "sub_id")
protected String subId;
}
}
parent:
#Data
#Entity
#Table(name = "parentc")
public class ParentEntity implements Serializable {
#Column(name = "id")
protected String parentId;
}
So as you see, it works well except for the parent's fk reference definition, I get mapping error since Hibernate tries to find a parent_id, rather than just id for the foreignKey, since #JoinColumn override is ignored. It works if I put the parent information in the RealEntity directly (obviously), or if I put #JoinColumn(name = "id") on parent in BaseEntity but I want to keep it as generic as possible. Is there any solution to this issue? Or should I just give up?
edit: it seems when I put a proper #JoinColumn with acceptable mapping for joining on parent in BaseEntity, that does get overridden, so it needs something valid to override. I cannot just add an association from nothingness is that the case? I've seen many examples on the web where they were putting associations from scratch, my usage of #MapsId, might be breaking the usage I guess. But I cannot change my current structure, since it is necessary to be able to represent composite foreign key definition for dependent child tables... I feel like there is a very simple solution, or some hacky way to achieve what I want, and I cannot seem to find it!

Map Collection of Interface using annotation in Hibernate

I have an interface called Rule with 2 implementing classes who all share one Abstract base class.
#MappedSuperclass
public interface Rule { .. }
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class BaseRule implements Rule {
#Entity
public class ImlementingRule1 extends BaseRule {
#Entity
public class ImlementingRule1 extends BaseRule {
I'm using this Rule interface in a containgRules class as such:
#OneToMany
#JoinColumn(name = "RULES_ID")
private List<Rule> rules;
Whatever setup I try I always end up with:
Caused by: org.hibernate.MappingException: Cannot use identity column key generation with <union-subclass> mapping for: mynamespace.BaseRule
I personally have found no other solution than to use the abstract base class, instead of interface.
#OneToMany
#JoinColumn(name = "RULES_ID")
private List<BaseRule> rules;
It states right here:
Annotating interfaces is currently not supported.

jpa: inheritance with self references and non abstract superclass

In my current project I have a inheritance structure that looks like this:
#Entity
#Table(name="groups")
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorValue("G")
#DiscriminatorColumn(name="group_type")
public class Group{ //some annotations removed
private Long id;
private String name;
private Set<Subject> subjects;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="parent_group_id")
private Group parent; ##### tree parent ####
#OneToMany(cascade=CascadeType.ALL, mappedBy="parent")
private Set<Group> subGroups; ##### tree children #####
...
}
My Group objects can have kind of a tree like structure by containing a list of other Group objects.
As some groups are a bit special, there is a second class that extends this class:
#Entity
#DiscriminatorValue("C")
#Table(name="fix_groups")
public class FixGroup extends Group{
private Layout lay;
private Set<Person> instr;
...
}
I tried to use a joined multi table approach (as described here: http://en.wikibooks.org/wiki/Java_Persistence/Inheritance#Joined.2C_Multiple_Table_Inheritance) but it seems not to work with a non abstract superclass like Group!
I get the following exception:
Caused by: java.lang.ClassCastException: org.hibernate.mapping.JoinedSubclass
cannot be cast to org.hibernate.mapping.RootClass
Is there a solution apart from declaring Group as abstract and making a new class Group2 that only extends it?
And if I did so, would this self-reference Set<Group> subGroups still cause problems?
I was able to cause this error by setting the ID in the subclass when it is already mapped in the parent class (in this case Group). For example in the parent class:
#Entity
#Table(name="groups")
#Inheritance(strategy=InheritanceType.JOINED)
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
...
and then setting the id in the subclass like so:
#Entity
#Table(name="sub_groups")
public class SubGroup extends Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
...
In this case, Group does not need to be abstract, but you can't define the id on the subclass.
Also, as a side note, if you are using Hibernate with an inheritance of type "join", then the discriminator column and value are not needed. Hibernate only utilizes those if using a single table. Please reference this post for further information:
Hibernate 4: persisting InheritanceType.JOINED discriminator column values

Persist collection of interface using Hibernate

I want to persist my litte zoo with Hibernate:
#Entity
#Table(name = "zoo")
public class Zoo {
#OneToMany
private Set<Animal> animals = new HashSet<Animal>();
}
// Just a marker interface
public interface Animal {
}
#Entity
#Table(name = "dog")
public class Dog implements Animal {
// ID and other properties
}
#Entity
#Table(name = "cat")
public class Cat implements Animal {
// ID and other properties
}
When I try to persist the zoo, Hibernate complains:
Use of #OneToMany or #ManyToMany targeting an unmapped class: blubb.Zoo.animals[blubb.Animal]
I know about the targetEntity-property of #OneToMany but that would mean, only Dogs OR Cats can live in my zoo.
Is there any way to persist a collection of an interface, which has several implementations, with Hibernate?
JPA annotations are not supported on interfaces. From Java Persistence with Hibernate (p.210):
Note that the JPA specification
doesn’t support any mapping annotation
on an interface! This will be resolved
in a future version of the
specification; when you read this
book, it will probably be possible
with Hibernate Annotations.
A possible solution would be to use an abstract Entity with a TABLE_PER_CLASS inheritance strategy (because you can't use a mapped superclass - which is not an entity - in associations). Something like this:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class AbstractAnimal {
#Id #GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
...
}
#Entity
public class Lion extends AbstractAnimal implements Animal {
...
}
#Entity
public class Tiger extends AbstractAnimal implements Animal {
...
}
#Entity
public class Zoo {
#Id #GeneratedValue
private Long id;
#OneToMany(targetEntity = AbstractAnimal.class)
private Set<Animal> animals = new HashSet<Animal>();
...
}
But there is not much advantages in keeping the interface IMO (and actually, I think persistent classes should be concrete).
References
Annotations, inheritance and interfaces
using MappedSuperclass in relation one to many
Polymorphic association to a MappedSuperclass throws exception
I can guess that what you want is mapping of inheritance tree.
#Inheritance annotation is the way to go.
I don't know if it will work with interfaces, but it will definitely work with abstract classes.
I think you have to annotate the interface too with #Entity and we have to annotate #Transient on all getters and setters of interface.

Categories