Hibernate SaveOrUpdate with Delete - java

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.

Related

What is the best practice for updating database objects in Java Spring Hibernate project

I would like to update a field by a unique ID in a MySQL database.
First method: fetch the object (select * from) from the database using unique ID (by uniqueresult()), then set the desired value to object and saveOrUpdate is performed.
Second method is to write an update query in a DAO implementation (update table tab set tab.name=123 where..., executeUpdate()), also the same result.
Which is a good way to perform update operation and why?
Well if you are using Hibernate, why would you do it in native SQL when you can just use Hibernate Sessions's .get(), .load(), .merge() .update() methods.
Here's an example from Hibernate documentation to modify a persistent object:
DomesticCat cat = (DomesticCat) sess.load( Cat.class, new Long(69) );
cat.setName("PK");
sess.flush(); // changes to cat are automatically detected and persisted
For further reading you can check Modifying persistent objects and Modifying detached objects sections in Hibernate documentation.
And according to the documentation :
The most straightforward way to update the state of an object is to load() it and then manipulate it directly while the Session is open.
I hope this answers both your questions Which is a good way to perform update operation and why?.
There is no "good way" to perform the update you want to. It entierly depend on your needs.
Both method work, but the first will permit you to update more than one field without having to modify your sql query. It will be the responsability of the developper to take care at the state of the object before calling the saveOrUpdate method.
The second method will ensure that no other field will be update in database.
Answer you on your future needs. Do you only need this field to be updated? Can it change in the future? What will be the inpact of both method on the application?
Then you will have your answer.

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.

Exception while persisting a large object graph using JPA/Hibernate

We are creating a new web application backed by JPA to replace an old web application. As part of the migration we are converting the old application's database to a new, more sophisticated, JPA-managed database.
So I've written a 'script' that converts the old database to a set of JPA entities and subsequently saves them. It works like this:
Create an order of conversion based on the dependencies of the domain models
For each entity
Execute database query to legacy DB
Store new object for each obtained table row in a list in memory
Iterate over generated lists in the same order as the conversion, and persist each entity.
Now, the first two steps work well. Upon persisting, however I get an exception. The exception occurs when one entity has a relation to another entity. For example if one of our entities would be a Book and another would be Chapter defining a #ManyToOne(optional=false) relation to Book. Upon persisting the Chapter, it throws the exception java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation: models.Chapter.book -> models.Book.
Of course, this indicates that something is wrong with the state of the book: it seems it is either not set or has not yet been persisted. However, I can verify that the Book is set properly in the conversion of the Chapter, and I can also verify that all entities of type Book are persisted by the EntityManager before the entities of type Chapter get persisted. Obviously, my JPA provider does not behave as expected and does not truly persist my Book objects for some reason.
What solution would allow me to save the entire graph of objects that I have converted to the database? I use Hibernate as my JPA provider and I also use Spring 3.1 for injection of dependencies and EntityManagers.
EDIT 1: Some additional info: I've again verified that entityManager.persist() is called on each of the book objects before entityManager.persist() is called on the chapters. However, the id of the book object remains null, meaning it is not properly persisted. The database also remains empty, despite not using transactions.
EDIT 2: Because I don't think it's clear from the text above: the Book and Chapter story is just an example. It happens for any entity that references another entity. This makes it seem as if I'm not using JPA/Hibernate properly as opposed to not setting the values of my entities properly.
EDIT 3: The core issue seems to be that despite persisting Book properly, having all the right annotations, book.getId() remains null. Basically, Hibernate is not setting the ids on my entities after persisting them, leading to problems when I need to use those entities later.
I once battled with such an error from hibernate myself. It turned out that it was a combination of a circle in the object graph and the cascade settings that caused the problem.
It has been a while so the fowlling might not be 100% accurate but maybe it is enough information to track your problem:
Hibernate Wants to insert the chapter. Realizes it needs to insert the book first.
Wants to insert the book. Realizes it needs to insert another entity first (e.g. publisher)
Inserts publisher and performs cascades defined on publisher (e.g. authors)
Author has e.g. reference to his lastestBook. Because hibernate internally already marked the book as processed (in step 2) you would no get an exception stating that author.book references a transient instance.
To find out if this is your problem you can enable full hibernate debugging and follow the path hibernate is taking through your object graph.
I've found the answer thanks to the discussion I've had with user1888440.
The solution to this answer was that the Spring #Transactional annotation was nonfunctional in my application. This mean that everything Hibernate did didn't occur in the context of a transaction. This meant that Hibernate would not set ids after persisting and this meant that all conversions would break down.
The reason why #Transactional did not work is probably because of a fact I did not mention: this script is part of a Play 2.0 (actually 2.1) app and is thus built using SBT. SBT doesn't use a normal Java setup to build an application, but instead uses the Scala compiler to compile Java as well. My guess is that the Scala compile did not work well with the AspectJ that Spring requires to make #Transactional work.
Instead, I performed all of the database work involved in this conversion within a programmatically defined Spring transaction (section 11.6). Now everything behaves as expected.
Check he unsaved values for your primary key/Object ID in your hbm files.If you have automated ID creaion by hibernate framework and you are stting th ID somewhere it woudl throw this error.By defaut the unsaved-value is 0 , so if you set the ID as 0 you would see this error.
Sounds like you are forgetting to assign a Book to each Chapter before persisting it. Even if you have persisted the Book it needs to be assigned to the #book property of the Chapter instance before you can persist the Chapter. This is because you have specified the relationship as non-optional. #book can never be null.

how does toplink session.refreshObject() handle one to many mappings?

am using the Session.refreshObject(Object o) method to fresh an object that has a getList() method. This list is provided by toplink using a one to many mapping. When I refresh the object, I want this list to be refreshed too. Is this possible or I have to refresh something else?
Thanks in Advance!
refreshObject is equivalent to calling ReadObjectQuery.
If you want to refresh objects in cascade you can use ReadObjectQuery.cascadePrivateParts(). According to the doc this method
Cascade the query and its properties on the queries object(s) and all
privately owned objects related to the queries object(s)
.
I depends on the mapping of the entity in the list. But if you want to actually refresh those objects you must do a
refreshObject(o.getList());

Difference between HQL delete query and session.delete()

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.

Categories