JPA cascade for unidirectional relationships without loading everything - java

So I have some entities that are used as the basis for a coordinate system, for the purpose of this post we'll call them A, B, C and D. Each of these entities has multiple #OneToMany relationships, and I want to cascade deletes. i.e. When some A is deleted, all entities in each of the #OneToMany relationships are deleted too. Fairly standard stuff.
However, I don't see the point in having these entities explicitly tracking these relationships when all I want to do is cascade a delete. I don't see the point in loading all these entities (potentially millions!) into memory each time a new entity is added to the #OneToMany relationship (i.e. using lazy loading only loads in when it's accessed, but it's of course accessed when a new entity in the relationship is added).
Let's add a little example:
#Entity
public class A {
#Id
private long id;
// ... other fields ...
#OneToMany
private Collection<SomeClass> collection;
}
#Entity
public class SomeClass {
#Id
private long id;
// ... other fields ...
#ManyToOne
A a;
#ManyToOne
B b;
// ... likewise for C, D ...
}
There can be multiple classes similar to SomeClass, and so multiple #OneToMany relationships in A (and B,C,D) that require tacking. This gets tedious FAST. Also, every time a new instance of SomeClass is added, I'd need to load the entire collection and this seems exceedingly inefficient (I'd pretty much end up with my entire database loaded into memory just to cascade a delete!!!).
How can I achieve what I want without modifying the underlying database (e.g. specfying ON DELETE CASCADE in the definition), surely the designers of JPA have considered such a use case? Maybe I'm incorrect that I'd need to load the entire collection when adding an entity to the relationship (if so, please explain why :) ).
A similar question was asked here: JPA: unidirectional many-to-one and cascading delete but it doesn't have a satisfactory solution, and it doesn't discuss whether or not the entire relationship gets loaded into memory.

To achieve a multi-level cascade without initializing all the entities you can only use a DB cascade.
There's no other way! That's why you couldn't find a satisfactory solution.
As for the:
Also, every time a new instance of SomeClass is added, I'd need to
load the entire collection and this seems exceedingly inefficient (I'd
pretty much end up with my entire database loaded into memory just to
cascade a delete!!!).
You need to understand the unidirectional Collections taxonomy:
Adding one element to a Set, requires the whole collection to be initializes to enforce the uniqueness Set contract.
a java.util.Collection or an unindexed List means you have a Bag, which are very inefficient in the unidirectional use case. For inverse collections they are fine, but that's out of your current context.
An indexed List (where the order is materialized in the database) is what you might be looking for:
#OrderColumn(name="orders_index")
public List<Order> getOrders() { return orders; }
The indexed list will use the index key for add/remove/update operations. As opposed to a Bag which simply deletes all elements and recreates the collection with the remaining elements, an index List will use the index key to only remove the elements that no longer belong to the List.

Related

Duplicated results in Hibernate OneToMany List

I have mapped an 1:N relation with a #OneToMany List, but when I access the list, the results are duplicated due to an OUTER JOIN.
This is how the mapping looks like:
#Entity
public class Programmer
#ElementCollection(fetch=FetchType.EAGER)
#CollectionTable(name="emails", joinColumns=#JoinColumn(name="id", nullable=false))
#Column(name="email", nullable=false)
protected Set<String> emails = new HashSet<String>();
#OneToMany(mappedBy="programmer", fetch=FetchType.EAGER)
private List <Game> games = new ArrayList<Game>();
When I get the attribute with prog.getGames(), the results comes duplicated because the Hibernate SQL makes an OUTER JOIN:
from programmer
left outer join emails on programmer.id=emails.id
left outer join game on programmer.id=game.id
where programmer.id=?
Is there any solution without transforming the List into a Set? I need to get the games with prog.getGames(), can not use a custom HQL or Criteria.
While the use of Set<> fundamentally resolves your issue, I'd argue that is simply a bandaid to get the expected results you're after but it doesn't technically address the underlying problem.
You should ultimately be using the default lazy fetch strategy because I'm of the opinion that eagerly loading any associations, particularly collection-based ones, are specific to a query and therefore should be toggled when you construct specific queries and not influenced as a part of your entity mapping model as you're doing.
Consider the future where you add a new query but you're only interestesd in attributes from the aggregate root entity. Your mapping model will still impose eagerly fetching those associations, you'll consume additional resources to by having a larger persistence context which means more memory consumption and impose unnecessary database joins for something which you aren't going to use.
If there are multiple collections that you need to hydrate, I would instead recommend you consider using FetchMode.SUBSELECT instead.
If we assume your query has 10 entities being returned, the default lazy strategy with 2 collections would issue 21 queries (1 for the base result set and 2 for each loaded entity).
The benefit of SUBSELECT is that Hibernate will actually only issue 3 queries (1 for the base result set and 1 for each collection to load all collection elements for all entities). And obviously, depending on certain queries, breaking one query with left-joins into 3 queries could actually perform better at the database level too.
Ive resolved this problem with #Fetch(FetchMode.SUBSELECT)
#OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Fetch(FetchMode.SUBSELECT)
private List<CompanyUserEntity> companyUserRelations;
I had the same problem. companyUserRelations had duplicate objects (I mean the same pointers to the same object, not duplicated data)
So after reading #dimitry response, I added #Fetch(FetchMode.SUBSELECT) and it worked

How to avoid persisting new object when oneToMany connection?

There is a three lines in returnreason-table and I don't want more lines there. User just choose one of the three reasons and I want only id to rma-table. Now it inserts a new line to reason-table every time when inserting new rma. I can take relation off between the tables but I wonder if there is better solution to avoid inserting new lines when persisting rma object? If I take cascadeType off from the class Rma, it is not helping/working. Then I got an error message, that jpa found an object, which is not persisted or something like that.
errormessage if I took cascadetype.ALL off
java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: com.entity.Returnreason[ returnreasonId=null ].
public class Rma implements Serializable {
#ManyToOne(cascade = CascadeType.ALL)
private Returnreason returnreasonReturnreasonId;
public class Returnreason implements Serializable {
#OneToMany(mappedBy = "returnreasonReturnreasonId", cascade = CascadeType.ALL)
private Collection<Rma> rmaCollection;
JPA methods require that the model use managed entities, while you seem to be attempting to associate a managed entity to something outside the context. This is a bad practice, and JPA is required to throw exceptions as it cannot tell what you intend it to do with the unmanaged instance.
You have two options
Read in your 3 Returnreason and use those entities for merging.
If there really are only ever 3, you can change your caching options
so they are always in the cache, so that an em.find operation
doesn't have to hit the database.
Remove the mapping in your Rma class to the Returnreason class and
map the returnreasonReturnreasonId field as a basic mapping. You
then can set the value in the Rma entity directly
The first option is probably the most frequently used, and really is required since you should have been maintaining the Returnreason rmaCollection anyway everytime you add a new Rma instance. While it may seem like you only need to set the Rma.returnreasonReturnreasonId, these are java objects, and your application is responsible for maintaining both sides of bidirectional relationships.

Hibernate: bidirectionality vs. unidirectionality, depending on relationship

Reading a wiki page about Hibernate I elaborated some perplexing conclusions:
1) Bidirectionality is reccomended in one-to-many
2) Bidirectionality is optional in many-to-one
3) Bidirectionality is normally present in many-to-many
4) Unidirectionality is reccomended in one-to-one relationships,
using as owner class the one with the primary key of the
relation (not the foreign key).
Are these statements true? Do you have any example to explain why in some cases unidirectionality is reccomended and in others bidirectionality is reccomended instead?
Here's the wiki page (read under "concepts"):
http://wiki.elvanor.net/index.php/Hibernate
Note that "bidirectionality" in the context of Hibernate means that in your Java classes, both sides of the relationship maintain a link to the other side. It has no impact on the underlying database schema (except in the case of indexed collections, see below), it's just whether or not you want the Java side to reflect that.
For all of your conclusions, "recommended" actually translates to "it usually ends up making sense, given your business logic, that you'd do it this way".
You really want to read through chapters 7 and 8 of the Hibernate Core Reference Manual.
It's recommended if you need it. A lot of convenience comes from specifying a bidirectional relationship; particularly it becomes possible to navigate the relationship from both ends in your business logic. However, if you don't actually need to do this, there's nothing to gain. Use whatever is most appropriate for the situation. In practice I've found that I want to specify both ends of the relationship to Hibernate more often than not -- but it is not a rule, rather, it reflects what I want to accomplish.
This is true. In a many-to-one (or one-to-many) relationship, it is optional. Consider the following schema:
table: users
fields: userId, userName
table: forumPosts
fields: postId, userId, content
Where forumPosts.userId is a foreign key into users. Your DAO classes might be (getters/setters omitted for brevity):
public class User {
private long userId;
private String userName;
}
public class ForumPost {
private long postId;
private User user;
private String content;
}
As you can see, this is a unidirectional many-to-one relationship (ForumPost-to-User). The ForumPost links to the user, but the User does not contain a list of ForumPosts.
You could then add a one-to-many mapping to User to make it have a list of ForumPosts. If you use a non-indexed collection like a set, this has no impact on the database schema. Merely by specifying both sides to Hibernate, you have made it bidirectional (using exactly the same schema as above), e.g.:
public class User {
private long userId;
private String userName;
private Set<ForumPost> forumPosts;
}
public class ForumPost {
private long postId;
private User user;
private String content;
}
Hibernate will now populate User.forumPosts when necessary (essentially with SELECT * FROM forumPosts WHERE userId = ?). The only difference between bidirectional and unidirectional here is that in one case Hibernate fills a set of ForumPosts in User, and in the other case it doesn't. If you ever have to get a collection of any given user's posts, you will want to use a bidirectional relationship like this rather than explicitly constructing an HQL query. Depending on your inverse/insert/update/cascade options in your relationship, you can also add and remove posts by modifying the User's set of posts, which may be a more accurate reflection of your business logic (or not!).
The reason I specified that non-indexed collections don't impact the underlying schema is because if you want to use an ordered, indexed collection like a list, you do have to add an extra list index field to the forumPosts table (although you do not have to add it to the ForumPost DAO class).
This is true, but is not a requirement and it's deeper than that. Same as above. Bidirectionality is usually present in many-to-many. Many-to-many relationships are implemented with a third join table. You specify the details of this table on both sides of the relationship. You can simply not specify the relationship on one side, and now it's a unidirectional relationship. Again, whether or not you tell Hibernate about the mapping is what determines if its unidirectional or bidirectional (in the context of Hibernate). In this case it also has no impact on the underlying schema unless you are using an ordered index collection. In fact, the many-to-many example in the Hibernate reference manual is a unidirectional setup.
In reality, it would be odd to have a unidirectional many-to-many relationship, unless perhaps you are working with an existing database schema and your particular application's business logic has no need for one of the sides of the relationship. Usually, though, when you've decided you need a many-to-many relationship, you've decided that because you need to maintain a collection of references on both sides of the relationship, and your DAO classes would reflect that need.
So the correct conclusion here is not merely that "bidirectionality is normally present in many-to-many", but instead "if you've designed a database with a join table, but your business logic only uses a unidirectional relationship, you should question whether or not your schema is appropriate for your application (and it very well may be)".
This is not true. Exactly the same as all the points above. If you need to navigate the one-to-one relationship from both sides, then you'd want to make it bidirectional (specify both sides of the mapping to Hibernate). If not, then you make it unidirectional (don't specify both sides of the mapping to Hibernate). This again comes down to what makes sense in your business layer.
I hope that helps. I left a lot of intricacies out. You really should read through the Hibernate documentation - it is not organized particularly well but Chapter 7 and 8 will tell you everything you need to know about collection mapping.
When I'm designing an application and a database from scratch, personally, I try to forget about Hibernate and the database entirely. I set up my DAOs in a way that makes sense for my business requirements, design a database schema to match, then set up the Hibernate mappings, making any final tweaks to the schema (e.g. adding index fields for ordered collections) at that point if necessary.

What is the difference between Unidirectional and Bidirectional JPA and Hibernate associations?

What is the difference between Unidirectional and Bidirectional associations?
Since the table generated in the db are all the same,so the only difference I found is that each side of the bidiretional assocations will have a refer to the other,and the unidirectional not.
This is a Unidirectional association
public class User {
private int id;
private String name;
#ManyToOne
#JoinColumn(
name = "groupId")
private Group group;
}
public class Group {
private int id;
private String name;
}
The Bidirectional association
public class User {
private int id;
private String name;
#ManyToOne
#JoinColumn(
name = "groupId")
private Group group;
}
public class Group {
private int id;
private String name;
#OneToMany(mappedBy="group")
private List<User> users;
}
The difference is whether the group holds a reference of the user.
So I wonder if this is the only difference? which is recommended?
The main difference is that bidirectional relationship provides navigational access in both directions, so that you can access the other side without explicit queries. Also it allows you to apply cascading options to both directions.
Note that navigational access is not always good, especially for "one-to-very-many" and "many-to-very-many" relationships. Imagine a Group that contains thousands of Users:
How would you access them? With so many Users, you usually need to apply some filtering and/or pagination, so that you need to execute a query anyway (unless you use collection filtering, which looks like a hack for me). Some developers may tend to apply filtering in memory in such cases, which is obviously not good for performance. Note that having such a relationship can encourage this kind of developers to use it without considering performance implications.
How would you add new Users to the Group? Fortunately, Hibernate looks at the owning side of relationship when persisting it, so you can only set User.group. However, if you want to keep objects in memory consistent, you also need to add User to Group.users. But it would make Hibernate to fetch all elements of Group.users from the database!
So, I can't agree with the recommendation from the Best Practices. You need to design bidirectional relationships carefully, considering use cases (do you need navigational access in both directions?) and possible performance implications.
See also:
Deterring “ToMany” Relationships in JPA models
Hibernate mapped collections performance problems
There are two main differences.
Accessing the association sides
The first one is related to how you will access the relationship. For a unidirectional association, you can navigate the association from one end only.
So, for a unidirectional #ManyToOne association, it means you can only access the relationship from the child side where the foreign key resides.
If you have a unidirectional #OneToMany association, it means you can only access the relationship from the parent side which manages the foreign key.
For the bidirectional #OneToMany association, you can navigate the association in both ways, either from the parent or from the child side.
You also need to use add/remove utility methods for bidirectional associations to make sure that both sides are properly synchronized.
Performance
The second aspect is related to performance.
For #OneToMany, unidirectional associations don't perform as well as bidirectional ones.
For #OneToOne, a bidirectional association will cause the parent to be fetched eagerly if Hibernate cannot tell whether the Proxy should be assigned or a null value.
For #ManyToMany, the collection type makes quite a difference as Sets perform better than Lists.
I'm not 100% sure this is the only difference, but it is the main difference. It is also recommended to have bi-directional associations by the Hibernate docs:
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/best-practices.html
Specifically:
Prefer bidirectional associations:
Unidirectional associations are more difficult to query. In a large
application, almost all associations
must be navigable in both directions
in queries.
I personally have a slight problem with this blanket recommendation -- it seems to me there are cases where a child doesn't have any practical reason to know about its parent (e.g., why does an order item need to know about the order it is associated with?), but I do see value in it a reasonable portion of the time as well. And since the bi-directionality doesn't really hurt anything, I don't find it too objectionable to adhere to.
In terms of coding, a bidirectional relationship is more complex to implement because the application is responsible for keeping both sides in synch according to JPA specification 5 (on page 42). Unfortunately the example given in the specification does not give more details, so it does not give an idea of the level of complexity.
When not using a second level cache it is usually not a problem to do not have the relationship methods correctly implemented because the instances get discarded at the end of the transaction.
When using second level cache, if anything gets corrupted because of wrongly implemented relationship handling methods, this means that other transactions will also see the corrupted elements (the second level cache is global).
A correctly implemented bi-directional relationship can make queries and the code simpler, but should not be used if it does not really make sense in terms of business logic.

JPA update many-to-many deleting records

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.

Categories