Difference between HQL delete query and session.delete() - java

I'm quite new to Hibernate and have a question. What is the difference between deleting an object by using an HQL query and deleting an object by using the delete(...) Method of the Session Class?

Session.delete(...) is only useful if you already have a reference to the entity you want to delete.
delete-by-query is useful for deleting several objects according to certain criteria, objects that you may not have previously loaded into the session.
I believe that delete-by-query actually loads each entity into the session and deletes them individually - someone correct me if I'm wrong on this.

Related

What is the correct CascadeType in #ManyToMany Hibernate annotation?

I am trying to model a transient operations solution schema in Hibernate and I am unsure how to get the object graph and behavior I want from the model.
The table structure uses a correlation table (many-to-many) to create lists of users for the operation:
Operation OperationUsers Users
op_id op_id user_id
... user_id ...
In modeling the persistent class Operation.java using hibernate annotations, I created:
#ManyToMany(fetch=FetchType.LAZY)
#JoinColumn(name="op_id")
public List<User> users() { return userlist; }
So far, I have the following questions:
When a user is removed from the list, how do I avoid Hibernate
deleting the user from the Users table? It should just be removed
from the correlation table, not the Users table. I cannot see a valid
CascadeType to accomplish this.
Do I need to put anything more in the method body?
Do I need to add more annotation arguments?
I am expecting to do this without futzing with the User class.
Please tell me that I do not have to mess with User.java!
It's possible I'm overthinking this, but that's the nature of learning... Thanks in advance for any help you can offer!
From the documentation:
Hibernate defines and supports the following object states:
*Transient - an object is transient if it has just been instantiated using the new operator, and it is not associated with a Hibernate Session. It has no persistent representation in the database and no identifier value has been assigned. Transient instances will be destroyed by the garbage collector if the application does not hold a reference anymore. Use the Hibernate Session to make an object persistent (and let Hibernate take care of the SQL statements that need to be executed for this transition).
*Persistent - a persistent instance has a representation in the database and an identifier value. It might just have been saved or loaded, however, it is by definition in the scope of a Session. Hibernate will detect any changes made to an object in persistent state and synchronize the state with the database when the unit of work completes. Developers do not execute manual UPDATE statements, or DELETE statements when an object should be made transient.
*Detached - a detached instance is an object that has been persistent, but its Session has been closed. The reference to the object is still valid, of course, and the detached instance might even be modified in this state. A detached instance can be reattached to a new Session at a later point in time, making it (and all the modifications) persistent again. This feature enables a programming model for long running units of work that require user think-time. We call them application transactions, i.e., a unit of work from the point of view of the user.
As explained in this answer, you can detach your entity using Session.evict() to prevent hibernate from updating the database or simply clone it and make the needed changes on the copy.
It turns out that the specific answer to my primary question (#1 and the main topic) is: "Do not specify any CascadeType on the property."
The answer is mentioned sorta sideways in the answer to this question.

Should Hibernate Session#merge do an insert when receiving an entity with an ID?

This seems like it would come up often, but I've Googled to no avail.
Suppose you have a Hibernate entity User. You have one User in your DB with id 1.
You have two threads running, A and B. They do the following:
A gets user 1 and closes its Session
B gets user 1 and deletes it
A changes a field on user 1
A gets a new Session and merges user 1
All my testing indicates that the merge attempts to find user 1 in the DB (it can't, obviously), so it inserts a new user with id 2.
My expectation, on the other hand, would be that Hibernate would see that the user being merged was not new (because it has an ID). It would try to find the user in the DB, which would fail, so it would not attempt an insert or an update. Ideally it would throw some kind of concurrency exception.
Note that I am using optimistic locking through #Version, and that does not help matters.
So, questions:
Is my observed Hibernate behaviour the intended behaviour?
If so, is it the same behaviour when calling merge on a JPA EntityManager instead of a Hibernate Session?
If the answer to 2. is yes, why is nobody complaining about it?
Please see the text from hibernate documentation below.
Copy the state of the given object onto the persistent object with the same identifier. If there is no persistent instance currently associated with the session, it will be loaded. Return the persistent instance. If the given instance is unsaved, save a copy of and return it as a newly persistent instance.
It clearly stated that copy the state(data) of object in database. if object is not there then save a copy of that data. When we say save a copy hibernate always create a record with new identifier.
Hibernate merge function works something like as follows.
It checks the status(attached or detached to the session) of entity and found it detached.
Then it tries to load the entity with identifier but not found in database.
As entity is not found then it treat that entity as transient.
Transient entity always create a new database record with new identifier.
Locking is always applied to attached entities. If entity is detached then hibernate will always load it and version value gets updated.
Locking is used to control concurrency problems. It is not the concurrency issue.
I've been looking at JSR-220, from which Session#merge claims to get its semantics. The JSR is sadly ambiguous, I have found.
It does say:
Optimistic locking is a technique that is used to insure that updates
to the database data corresponding to the state of an entity are made
only when no intervening transaction has updated that data since the
entity state was read.
If you take "updates" to include general mutation of the database data, including deletes, and not just a SQL UPDATE, which I do, I think you can make an argument that the observed behaviour is not compliant with optimistic locking.
Many people agree, given the comments on my question and the subsequent discovery of this bug.
From a purely practical point of view, the behaviour, compliant or not, could lead to quite a few bugs, because it is contrary to many developers' expectations. There does not seem to be an easy fix for it. In fact, Spring Data JPA seems to ignore this issue completely by blindly using EM#merge. Maybe other JPA providers handle this differently, but with Hibernate this could cause issues.
I'm actually working around this by using Session#update currently. It's really ugly, and requires code to handle the case when you try to update an entity that is detached, and there's a managed copy of it already. But, it won't lead to spurious inserts either.
1.Is my observed Hibernate behaviour the intended behaviour?
The behavior is correct. You just trying to do operations that are not protected against concurrent data modification :) If you have to split the operation into two sessions. Just find the object for update again and check if it is still there, throw exception if not. If there is one then lock it by using em.(class, primary key, LockModeType); or using #Version or #Entity(optimisticLock=OptimisticLockType.ALL/DIRTY/VERSION) to protect the object till the end of the transaction.
2.If so, is it the same behaviour when calling merge on a JPA EntityManager instead of a Hibernate Session?
Probably: yes
3.If the answer to 2. is yes, why is nobody complaining about it?
Because if you protect your operations using pessimistic or optimistic locking the problem will disappear:)
The problem you are trying to solve is called: Non-repeatable read

Hibernate needed values to save a child

I currently have working code to save children to a parent entity. But I'm wondering if I'm doing things right since I now have an overload on select statements going thru hibernate. I do use caching so atm I don't have delay problems but I'm wondering if I can't be more efficient. Take this little extract as example
MbaLog.debugLog(logger, "Saving CodeType");
Site site = codeType.getSite();
if (site != null && site.isProxy())
codeType.setSite(siteRepository.loadSiteById(site.getId()));
Long recordId = codeRepository.saveCodeType(codeType);
I have an entity CodeType that I'm saving that has a child Site. This child is passed to the method as a proxy object with just it's id filled in. Then I fetch a fully loaded Site object from the database and set it on codetype. Next up I save the codeType with the sessionfactory of hibernate to the database (code not visible here, but it's behind the codeRepository).
This works but I'm loading a full site, that has childs of it's own so I see at least 5 queries passing before the insert. I could put a lot of stuff lazy on site, but for the time being I rather not do that due to possible code complications in deeper layers. I had to learn hibernate and JPA on the job and never had much training from experts in the past. So I'm wondering, is there a shortcut to save the site on codetype ? Do I need to have it fully loaded or is the id enough ? or just the id and version (I'm using #version annotation on all my entities for optimistic locking)
Thanks in advance
Instead of using Session.get() (or EntityManager.find()) to get a reference to the SIte entity, use Session.load() (or EntityManager.getReference()) to get this reference.
These methods will return a lazy-loaded proxy on the entity rather than executing a query to get the data of the site.
If all you want to persist is the relationship between Site and CodeType, a lazy instance is probably enough. So you could use EntityManager.getReference() (lazy load) instead of EntityManager.find().

How to persist entity to two different tables using Spring+Hibernate

I imagine this pertains to Hibernate only (I'm just now beginning to use these two frameworks). I have an application that tracks sessions for users. While a session is active, the Session entity is stored in a table for active sessions. When the user goes offline and the session ends, the session is moved to a secondary historical table.
How do I achieve this with Hibernate? Right now I have a Session.hbm.xml file that maps a Session object to the active sessions table. Can I map it to a secondary table and somehow specify to which table I want it to persist when I call saveOrUpdate?
My reputation currently won't allow me to answer my own question this quickly. I don't want anyone to waste their time on this though, since I found an answer, so I'm posting it here as an edit.
I can do this by making use of the entity-name attribute in a mapping file. I created a second mapping, identical to Session.hbm.xml, called HistoricalSession.hbm.xml. In this new mapping file I reference the same Session class, but add:
entity-name="HistoricalSession"
Then I map the object to my second (historical) table just like normal. Calling save() or saveOrUpdate() defaults to using the classname as the entity-name, and saves in my primary table as before. Now, when I want to save a session to the historical table I use the Hibernate API overrides that allow you to specify an entity-name:
saveOrUpdate("HistoricalSession",session);
This accomplishes exactly what I want without need to create another Java class for historical sessions
I can do this by making use of the entity-name attribute in a mapping file. I created a second mapping, identical to Session.hbm.xml, called HistoricalSession.hbm.xml. In this new mapping file I reference the same Session class, but add:
entity-name="HistoricalSession"
Then I map the object to my second (historical) table just like normal. Calling save() or saveOrUpdate() defaults to using the classname as the entity-name, and saves in my primary table as before. Now, when I want to save a session to the historical table I use the Hibernate API overrides that allow you to specify an entity-name:
saveOrUpdate("HistoricalSession",session);
This accomplishes exactly what I want without need to create another Java class for historical sessions
A couple of way to do this could be:
Use a database trigger when the session gets expired the trrigger will move the row to the historical table.
You can create a HistoricalSession extends Session and then do a second mapping for HistoricalSession and write the code to delete from Session and insert into historical session.
Your need sounds like more of an audit like.
Check project Hibernate Envers it might help solve your case in a better way.

Hibernate SaveOrUpdate with Delete

I am attempting to delete an item from a collection in a Hibernate Java object using the saveOrUpdate function on the parent object. Update and Inserts work properly, but objects are not Deleted properly. Does saveOrUpdate() have the ability to recognize and delete objects that have been removed from a parent's set?
As a side note, I have mappers that map from DB -> hibernate java object -> domain object, and the domain object is kept in session. Do I need to keep the hibernate java object in session for this to work properly?
UPDATE (ANSWERED): I just ended up using merge() instead of saveOrUpdate(). Merge called DELETE when necessary without having to store the java hibernate object in session.
You need to add delete-orphan to the mapping. This will tell hibernate to delete 'orphaned' objects from a one to many relationship. Here's a link to the specific item in the documentation.
You're looking for "delete-orphan". Check out the reference guide on parent-child relationships and the annotations guide for the annotation syntax for it.

Categories