I am trying to use Hibernate to store family tree information. From what I have seen in the documentation, in order to connect two or more entities, they have to be from different classes. So, in order to create relationships between husband and wife, I would need to have two classes respectively. I think this is pointless because both classes would be identical (keep in mind that the tree can be quite large so I would have a lot of duplicate classes that way).
Is there a way to have a single class, for example Person and do the connections just from that class?
Also, if there is not way to achieve that, how would I connect siblings, for example
(p:Sibling)-[:SIBLING_OF]->(k:Sibling)
when they will both be from same class Sibling?
You can create relationships with entities of the same class the same way you create relationships with entities of different classes.
You can find an example of the mapping on the Hibernate OGM project sources:
https://github.com/hibernate/hibernate-ogm/blob/5.2.0.Alpha1/core/src/test/java/org/hibernate/ogm/backendtck/associations/recursive/TreeNode.java
and the realtive testcase:
https://github.com/hibernate/hibernate-ogm/blob/5.2.0.Alpha1/core/src/test/java/org/hibernate/ogm/backendtck/associations/recursive/RecursiveAssociationsTest.java
The tests map a tree structure with nodes having a parent node and many children, the mapping of the entity looks like like this:
#Entity
public class TreeNode {
#Id
private String name;
#ManyToOne
private TreeNode parent;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "parent",
cascade = CascadeType.ALL, orphanRemoval = true)
private List<TreeNode> children = new ArrayList<TreeNode>( 3 );
...
}
NOTE:
Based on your needs, you can create the association using native queries but I wouldn't recommend it. Hibernate OGM becomes unaware of the relationship and problems might occur.
You can use CYPHER query for creating relationship for same class entities
as follow
Match(u:sibling{name:'abc'}),Match(p:sibling{name:'xyz'})
CREATE (u)-[:SIBLING_OF]-(p)
executing CYPHER query can be found here
Related
I have a Hibernate entity that is comprised of many other entities that are used within the application. The other entities that make up this MainEntity are joined by using #ManyToOne and #JoinColumn. This MainEntity class has 5 columns (#Column) and 7 #ManyToOne/#JoinColumn entities that are used.
I seem to be running into performance issues when retrieving all of these MainEntity classes. We want to serialize the MainEntity to JSON as well as the other entities that are associated with it. Note that there aren't that many that we are retrieving - less than 30 total.
Below is an example of what the class looks like along with my findAll() method to retrieve these classes. I know that #ManyToOne is EAGER by default, so I'm wondering if there's a better way to get all of these entities that is easier on the system. Thank you in advance.
#Entity(name = "MainEntity")
#Table(name = "main_entity")
public class MainEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
// Other #Columns defined here
#ManyToOne()
#JoinColumn(name = "entity_1_id")
private Entity1 entity1;
#ManyToOne()
#JoinColumn(name = "entity_2_id")
private Entity2 entity2;
#ManyToOne()
#JoinColumn(name = "entity_3_id")
private Entity3 entity3;
// ... and so on, for a total of 7 #ManyToOne() columns
}
Here is the findAll() method that I have:
final List<E> findAllOrdered(Class<E> clazz, Order order) {
final Session session = sessionManager.openNewSession();
try {
return session.createCriteria(clazz)
.addOrder(order)
.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
.list();
} finally {
sessionManager.closeSession(session);
}
}
I found myself having to add the Criteria.DISTINCT_ROOT_ENTITY because we were getting duplicate MainEntity results if a child had multiple associated with it. I suspect this is big part of my performance problem.
If you are retrieving unwanted response and if you want to filter then you may use #JsonIgnore
eg:
#ManyToOne()
#JoinColumn(name = "entity_1_id")
#JsonIgnore
private Entity1 entity1;
Few pointers to consider:
Consider making associations Lazy by default unless you really want to load all the association data and its associations along the parent.
Use JOIN in HQL/criteria based on which association we really want to fetch and the depth of associations.
Or use EntityGraph to decide which associations to be fetch.
Enable show_sql as this show the number of SQLs and the exact SQLs that are getting fired to the DB. This would be a good starting point and subsequently you can tune you associations to LAZY/EAGER, SELECT/JOIN/SUBSELECT based on your use case.
You can run these queries against the DB and see if tuning the query/DB (indexes, partitioning etc) will help reduce the query times.
See if second level cache would help for your use case. Note that second level cache will come with its own complexity and extra overhead and especially if the data is of transactional type and not read-only mostly. With application deployed on nodes maintaining the cache coherence will be another aspect to think about. Need to validate if the extra overhead and complexity is really worth the efficiency outcome of the second level cache.
From an application design perspective, you can also consider and see if you really want to retrieve the MainEntity and the associations in a single request or UI. Instead we could first show the MainEntity with some paging and based on the selection we could fetch the associations for that MainEntity with paging.
Note that, this is not a complete list. But a good starting point and based on your use case you can see which one would fit for you and any other additional techniques.
How to make hierarhical mapping Hibernate?
For example:
Category
id parent_id name
1 0 Root
2 1 Sub-root 1
3 1 Sub-root 2
4 2 Sub-(sub-root 1)
Is it possible to make lazy mapping for such Category object?
It is not exactly clear what you are asking.
However it would appear you are talking about a self-referencing relationship rather than Inheritance so you can then map as below. The default fetch strategy should be same as for any other #OneToMany i.e. LAZY.
#Entity
public class Category{
#Id
private Long id;
#ManyToOne
#JoinColumn(name = "parent_id")
private Category parent;
#OneToMany(mappedBy = "parent")
private Set<Category> subCategories;
}
I believe you want to ask about inheritance of entities. I recommend using JPA inheritance strategies. There are 3 available.
Single Table: Uses only one database table. columns need to be nullable and hence wastes database space
Joined Strategy: Uses multiple table which can be joined for insertion and retrieval of entity data. Saves database space but performance becomes an issue when inheritance hierarchy becomes wide and deep
Table per concrete class: Uses separate database tables which are not joined.
Different strategies have different advantages and disadvantages. You can choose according to your need.
I have tow classes:
Parent
Child
In the database Child table has a column ParentId -> typical One (Parent) -> Many (Children) relation
Now I create two entities and they
public class Parent
{
#OneToMany(mappedBy="Parent", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
public Set<Child> getChildern()
{
...
}
}
public class Child
{
#ManyToOne(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
#JoinColumn(name="ParentId")
public Parent getParent()
{ ... }
}
Now I have two scenarios:
Parent gets deleted -> what should happen?
Child gets deleted -> what should happen?
Bonus questions:
Do I always need to create both parts of the key OneToMany and ManyToOne or can i just have ManyToOne and dont care in the parent where I have children?
What could cause a hibernate to give me a message foreign key constraint violation for a parent which has no children?
First of all I'm surprised this code works at all. IMO mappedBy="Parent" should actually be mappedBy="parent" (note the lower-case 'p') because the parent property of the Child class is called parent and not Parent.
Second, I suggest you place the annotations on the properties rather than on the accessor methods. I find it makes the whole code
more readable
easier to maintain because getters/setters can then be added behind the scenes by Lombok
Answers to your questions depend on what exactly you mean by "get deleted". I assume you mean "deleted through persistence manager".
BUT just in case you expect/want that a child is removed by the JPA provider if you do parent.getChildren().remove(x) then you need to set orphanRemoval = "true" on OneToMany.
Question 1
Parent and all children are deleted. That's the common case.
Question 2
Parent and all children are deleted. That's a rather odd use case. Usually cascade delete is only applied on the one-to-many relationship.
Bonus 1
All relationships in Java and JPA are unidirectional, in that if a
source object references a target object there is no guarantee that
the target object also has a relationship to the source object.
from the excellent Java Persistence wiki book.
Bonus 2
Dunno. Is the ConstraintViolationException coming from the underlying data base? Or put differently, how does the DDL for the two tables look like? Was it generated by Hibernate?
I have a #ManyToMany relationship between two entities. When I perform an update on the owning side, it appears that JPA deletes all the linked records from my database and re-inserts them. For me this is a problem because I have a MySQL trigger that fires before a record is deleted. Any ideas on how to get around this problem?
#Entity
public class User {
#Id
#Column(name="username")
private String username;
...
#ManyToMany
#JoinTable(name="groups", joinColumns=
#JoinColumn(name="username", referencedColumnName="username"),
inverseJoinColumns=#JoinColumn(name="groupname",
referencedColumnName="type_id"))
private List<UserType> types;
...
}
#Entity
public class UserType {
#Id
#Column(name="type_id")
private String id;
#ManyToMany(mappedBy="types")
private List<User> users;
...
}
Use Set instead of List solved the problem. But I have no idea why it works.
Another solution provided by Hibernate is to split the #ManyToMany association into two bidirectional #OneTo#Many relationships. See Hibernate 5.2 documentation for example.
If a bidirectional #OneToMany association performs better when
removing or changing the order of child elements, the #ManyToMany
relationship cannot benefit from such an optimization because the
foreign key side is not in control. To overcome this limitation, the
link table must be directly exposed and the #ManyToMany association
split into two bidirectional #OneToMany relationships.
Try this one:
1) change declaration to:
private List<UserType> types = new Vector<UserType>();
2) never call
user.setTypes(newTypesList)
3) only call
user.getTypes().add(...);
user.getTypes().remove(...);
Its probably related to this question. You have to ensure you have an appropriately defined hashCode an equals method in your mapped object so that Eclipselink can determine equality and thus determine that the existing objects map to existing objects in the DB. Otherwise it has no choice but to recreate the child objects every time.
Alternatively, I've read that this kind of join can only support efficient adding and removing of list items if you use an index column, but that's going to be EclipseLink specific, since the JPA annotations don't seem to support such a thing. I know there is an equivalent Hibernate annotation, but I don't know what it would be in Eclipselink, if such a thing exists.
It appears my problem was that I was not merging the entity.
Put another way: How do you model/map a heavily reused child class/table to many different parent entities?
I have several entity types each being persisted into its own table:
class A --> table A
class B --> table B
....
Now I need to make each of these classes the parent of a 1:M unidirectional child collection. The collection is a history of approvals the entity has gained over time. The Child domain class is called "ApprovalItem". The Approval class is exactly the same for all types of parents.
What is the best way to map this? If I create a single table to hold all ApprovalItems, then I can't enforce a FK relation to the PK of the entity and/or I am left with a bad database design.
On the other hand, I could create an ApprovalIems table for each entity type (e.g. A_ApprovalItems, B_ApprovalItems, etc.). This seems like a good schema on the database side, but then it seems I need to create a separate domain classes in Java for each entity approval (e.g. AAprrovalItem class, BApprovalItem class, etc.). This seems like a lot of hassle and complexity to create so many new classes in Java that do nothing other than allow me to put in different JPA mapping annotations.
Is there a mapping technique in Hibernate that will allow me to have one class in Java map to several different tables depending on who the parent owner of the collection is?
I could create an ApprovalItem table for each entity type (e.g. A_ApprovalItem, B_ApprovalItem, etc.). This seems like a good schema on the database side
But
It seems i need to create a separate domain classes in Java for each entity approval (e.g. AAprrovalItem class, BApprovalItem class, etc.).
You do not need it. you can create a single ApprovalItem class and create a #OneToMany relationship between your parent classes and your ApprovalItem. Hibernate takes care to create a linked table for each relationship.
#Entity
public class ClassA {
#Id
#GeneratedValue
private Integer id;
// Hibernate will create CLASSA_APPROVALITEM to link both class
#OneToMany
private List<ApprovalItem> approvalItemList;
}
#Entity
public class ClassB {
#Id
#GeneratedValue
private Integer id;
// Hibernate will create CLASSB_APPROVALITEM to link both class
#OneToMany
private List<ApprovalItem> approvalItemList;
}
And your ApprovalItem class
#Entity
public class ApprovalItem {
#Id
#GeneratedValue
private Integer id;
// Nothing else
}
But Let's see what Java Persistence with Hibernate book talks about it
You may have shared references to the Bid objects. As suggested earlier, a User may have a collection of references to the Bid instances they made. You can’t delete an item and all its bids without removing these references first. You may get an exception if you try to commit this transaction, because a foreign key constraint may be violated.
So keep it in mind when dealing with shared references.
In order to see how the target schema looks like, you can use the following
AnnotationConfiguration configuration = new AnnotationConfiguration();
configuration
.addAnnotatedClass(ClassA.class)
.addAnnotatedClass(ClassB.class)
.addAnnotatedClass(ApprovalItem.class)
.setProperty(Environment.USER, <TYPE_YOUR_USER>)
.setProperty(Environment.PASS, <TYPE_YOUR_PASSWORD>)
.setProperty(Environment.URL, <TYPE_YOUR_URL>)
.setProperty(Environment.DIALECT, <TYPE_YOUR_DIALECT>)
.setProperty(Environment.DRIVER, <TYPE_YOUR_DRIVER>);
SchemaExport schema = new SchemaExport(configuration);
schema.setOutputFile("schema.sql");
schema.create(<DO_YOU_WANT_TO_PRINT_TO_THE_CONSOLE>, <DO_YOU_WANT_TO_EXPORT_THE_SCRIPT_TO_THE_DATABASE>);
It will generate a file called schema.sql, which contains your target schema
regards,
Chapter 8. Inheritance Mapping of Hibernate Documentation might help.
Otherwise, I see no problem having multiple ApprovalItem derived class that "do nothing", like you say, since it does differentiate the Approval, it's like having a type of approval. Seeing your model like so, I would recommend using multiple classes, even if they only inherit from your base ApprovalItem class.
Have I well understood your question or am I missing something else more subtle?