Hibernate/JPA: ManyToMany and OneToMany relationship on same attribute - java

I have a problem on combining a ManyToMany with an OneToMany relationship.
I have entries and categories. Every entry has one main category and 0..* subcategories.
This is my implementation:
public class Entry extends AbstractEntity {
[...]
private Category mainCategory;
#ManyToMany(targetEntity = hello.Category.class)
private Set<Category> subCategories;
[...]
}
public class Category extends AbstractEntity {
[...]
#ManyToMany(targetEntity = hello.Entry.class, mappedBy = "subCategories")
private Set<Entry> entries;
[...]
}
The ManyToMany relationship is functional but i don't know how to implement the OneToMany relationship.

You cannot define two separate mapping on a single attribute. The data it should contain is not well-defined. Should it contain the Entries mapped by the subCategories field or by the mainCategory or both? Since there is not a singe sensible answer for all use cases, JPA disallows such multiple annotations.
You can however just add a field corresponding to the inverse (non-owning) side of the one-to-many relationship.
Define it like this:
public class Category ...
#ManyToOne(mappedBy="mainCategory")
private Set<Entry> entriesHavingThisCategoryAsMain;
I could not come up with a better name for the inverse side, so use your context :)
EDIT: you do not need to define the targetEntity attribute for typed Collections except you have multiple Category and Entry entities in different packages.

Related

Why does Hibernate create a Join Table by default for #OneToMany?

#OneToMany annotation, by default, creates a join table, unless the mappedBy element is specified.
What is the reason for this behaviour? For example, with the following entities:
#Entity
public class User {
// ...
#OneToMany
private List<UserDocument> documents;
// ...
}
#Entity
public class UserDocument {
// ...
#ManyToOne
private User user;
// ...
}
For the User entity, why doesn't Hibernate simply:
Find the field with type User in UserDocument by doing reflection on UserDocument entity.
Infer the value of mappedBy for the #OneToMany annotation by itself?
What is the reason for not doing this and generating a join table as the default behaviour? Why is Hibernate (or JPA) is designed this way?
A simple reason behind this is that Hibernate cannot known for sure that a filed of type User inside of UserDocument is corresponding to the specific User-UserDocument relation. Without a mappedBy property, Hibernate can only create a join table or insert a generated column in UserDocument table. However, the latter alters data model and introduces more problem than it may resolve ( distinguish generated or declared column; table schema mismatch model class; etc.). Thus Hibernate use a join table to store the mapping.
For example, if you want to track the last one who modifies a document, you may need another many-to-one relation in UserDocument. This cannot be infered and resolved just using reflection.
#Entity
public class UserDocument {
// ...
#ManyToOne
private User user;
#ManyToOne
private User lastModifiedBy;
// ...
}

#OnDelete with #OneToMany only

I have problem with annotation #OnDelete with #OneToMany relation.
public class Patent {
#OneToMany
#JoinCollumn(name = "parent_id")
#OnDelete(action = OnDeleteAction.CASCADE)
private List<Child> children;
}
public class Child {
}
When I run it I get this error: "only inverse one-to-many associations may use on-delete="cascade"". How I need to change code to get it functional, without bidirectional relation? I know that, it can be solved with adding #ManyToOne relation, with appropriate annotations, to Child class, but I do not want to use this solution.
Edit: Purpose for this is that i need to generate "on delete cascade" to foreign key constraint in exported ddl schema.
All you need is to use orphanRemoval parameter for your OneToMany relation. See https://docs.oracle.com/cd/E19798-01/821-1841/giqxy/ for reference.
Example:
#OneToMany(mappedBy="customer", orphanRemoval="true")
public List<Order> getOrders() { ... }
However I think that your mapping is quite wrong, for such relation you should add Patent field to your Child class, mark relation as ManyToOne, then use JoinCollumn and set the reference as parent_id. With mappedBy and orphanRemoval options inside Patent - usability will be the same as you want.

jpa map owned by the "one" side

I have a tree like data structure with some sort of composite pattern. With an abstract class Element, there is a CompositeElement and a SingleElement. It looks like this:
#Entity
#DiscriminatorValue("Composite")
public class CompositeElement extends Element {
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#JoinTable(name="Sub_Elements")
#MapKeyColumn(name="xxx")
protected Map<Integer, Element> subs;
...
}
Until now, the relation was unidirectional. It worked well. But now a use case popped up where I need to navigate from a sub element to the parent element. So what I'd love to do is this:
#Entity
#Inheritance
#DiscriminatorColumn("s_discriminator")
public class Element {
#ManyToOne(mappedBy="subs", fetch=FetchType.LAZY)
protected CompositeElement parent;
...
}
But the #ManyToOne Annotation doesn't allow a "mappedBy" attribute.
From a domain view, the parent owns the children objects in the data structure. Not the other way around. This is also emphasised by the eager fetch and the cascade rule.
If the ownership of the relationship was on the child side, then child.setParent(p) wouldn't really work because here I'm missing the key for the map.
Is there any way to keep the ownership of the relation on the side of the parent but still make it bidirectional?
Have a look at adding a #JoinColumn annotation to your #ManyToOne and the
javadocs for the #ManyToOne annotation.
Normally the mappedby would work the other way where the #OneToMany would be mappedby the #ManyToOne
it looks like it's not possible the way I want it.
I changed it so the relationship is owned by the child. And I added a property "childIndex" in the Element class. This property is referenced by #MapKey.
public class CompositeElement extends Element {
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER, mappedBy="parent")
#MapKey(name="childIndex")
protected Map<Integer, Element> subs;
and
public abstract class Element {
protected Integer childIndex;
#ManyToOne(fetch=FetchType.LAZY)
protected CompositeElement parent;
The solution works, but I don't like it. I don't like the child element knowing its "childIndex". When I re-order the children in the parent object, I need to modify each of the children. I would have loved to keep a separate database table representing the relationship and the index column being there.
I guess another option would have been to model the relation as an entity itself.

Hibernate Envers creating modified fields for foreign owned OneToMany relationships

I am using Hibernate 4.3.1.Final
If I have two Entities, let's say A and B. A contains a set of B objects that is annotated as a OneToMany association.
If I set "org.hibernate.envers.global_with_modified_flag" to true and "org.hibernate.envers.modified_flag_suffix" to "Modified", then Envers correctly adds columns for the all of the columns in that table with the specified suffix, but it also expects to find a modified column for each of the associations even though they are owned by the foreign side.
In the below case, Envers expects columns in A for "foo" "fooModified", and "bObjectsModified" when I would think that it should expect columns for "foo" and "fooModified" in A and "aIdModified" in B.
#Entity
#Table("A")
#Audited
class A {
private String foo;
private Set<B> bObjects;
#Column(name = "foo")
public getFoo( return foo; )
#OneToMany(fetch = FetchType.LAZY,
mappedBy = "a")
public Set<B> getBObjects() { return bObjects; }
}
#Entity
#Table("B")
#Audited
class B {
private A a;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "aId")
public getA(){ return a; }
}
Has anyone else seen this? How do I change that behavior other than annotating every one of my #ManyToOne relationships with #Audited(withModifiedFlag=false). I have many thousands of relationships, so even testing that part will be a huge pain.
The alternative is forcing the database to know details about our Java code that it has no business knowing and makes it much more difficult to add bi-directional associations.
For those who may come later, at least as of 4.3.1.Final, the only way to do this is to remove the global configuration flag and add that option to the #Audited annotation on every class so that it is #Audited(withModifiedFlag=true) and then add #Audited(withModifiedFlag=false) to every property (not column!) in that class for which you do not want a modified field to be created.
In the other Hibernate modules, global configuration options can be overridden at the class or attribute level. For Envers, global configuration options can never be overridden.
Also note that the modified field names are based on the attribute name in the Java class and not the value in the #Column annotation that the rest of Hibernate ORM uses.

JPA Embeddable can have ElementCollection<Embeddable> property

can JPA ElementCollection Embeddable have ElementCollection List Embeddable property?
#Entity
class Person {
#ElementCollection
private List<Address> addressList;
}
#Embeddable
public class Address {
private String city;
private String state;
private String countary;
#ElementCollection
private List<Phone> phones;
...
}
#Embeddable
public class Phone {
private String type;
private String areaCode;
private String number;
...
}
Yes you can have that, if you try your configuration you will have something like this.
http://postimg.org/image/pc0f3cxbp/
Also Embeddable types can have Collection Relationship to another entities this allowed by JPA.
Taking from specs JSR317
An embeddable class may be used to represent the state of another
embeddable class.
An embeddable class (including an embeddable class within another
embeddable class) may contain a collection of a basic type or other
embeddable class.
An embeddable class may contain a relationship to an entity or
collection of entities. Since instances of embeddable classes
themselves have no persistent identity, the relationship from the
referenced entity is to the entity that contains the embeddable
instance(s) and not to the embeddable itself.
An embeddable class that is used as an embedded id or as a map key
must not contain such a relationship
UPDATE
According to your question if embeddables can contain a list of embeddables this is possible and can be done as picture in this answer suggest, BUT regarding if and ElementCollection can contain an embeddables with another element collection the answer is NO
Trying to do that will cause.
Mapping contains an embeddable "examples.model.Address" with a
prohibited mapping "phones", element collections may not contain
embeddables with element collection mappings

Categories