Creating a JPA (Hibernate) proxy and set the Primary key later - java

Well, I'm working on a library where I need to create a JPA reference of some serialized entity objects.
The time I need to create the entity reference I don't have the ID field value so I somehow first need to create the proxy and then set the id. So I cannot use the
entitymanager.getReference
Do you guys have any suggestions?

You cannot do that. Without an id an entity is in "detached" state, which means it's not governed by hibernate. You need to fetch it from db (or get reference), persist or make managed in other way, so it's not possible.

Related

Update by id of an entity attribute which is another entity

When I need to update a relationship of an entity, normally I have something like this:
XEntity entityToSave = ....
YEntity relatedEntity = relatedEntityRepository.findById(relatedEntityId);
entityToSave.setRelatedEntity(relatedEntity);
repository.save(entityToSave);
I would like to skip the findById of the related entity, because as I understand the id is everything what JPA should need to make the update.
So, I want like this:
XEntity entityToSave = ....
entityToSave.setRelatedEntity(
YEntity.builder().id(relatedEntityId).build()
);
repository.save(entityToSave);
Someway JPA should be aware that I just want to set the related entity (without update any attribute of it)
Do you know any way to achieve this?
UPDATE:
I want to avoid inject the relatedEntityRepository. As I have the id of the related entity. which is everything that jpa should know to update the relatioship
When using Spring repository, use getOne(..). It is Spring data equivalent to EntityManager.getReference(..). So like:
entityToSave.setRelatedEntity(repo.getOne(relatedEntityId));
Method getOne kind a "stores" a reference for entity by the id you provide. It does not fetch the entity - so it is lazy - until you access properties of referenced (your relatedEntity) entity (except id that is not needed to fetch anyway).
When referencing (your related) entity is saved then reference to referenced entity is saved also and usually without ever fetching the referenced entity (so if you do not modify referenced entity).
See this also
There usually just is no point to "avoid injecting" something especially singleton (Spring default) beans. You could achieve this behavior with #Modifying and some native query on repository method or make it a bit "lighter" by injection entity manager and use that to handle all the stuff but it would be more complex and obfuscating so IMO you would shoot your own leg that way.

Why does Spring-data-jdbc not save my Car object?

I'm playing around with spring-data-jdbc and discovered a problem, with I can't solve using Google.
No matter what I try to do, I just can't push a trivial object into the database (Bean1.java:25):
carRepository.save(new Car(2L, "BMW", "5"));
Both, without one and with a TransactionManager +#Transactional the database (apparently) does not commit the record.
The code is based on a Postgres database, but you might also simply use a H2 below and get the same result.
Here is the (minimalistic) source code:
https://github.com/bitmagier/spring-data-jdbc-sandbox/tree/stackoverflow-question
Can somebody tell me, why the car is not inserted into the database?
This is not related to transactions not working.
Instead, it's about Spring Data JDBC considering your instance an existing instance that needs updating (instead of inserting).
You can verify this is the problem by activating logging for org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate. You should see an update but no insert.
By default, Spring Data JDBC considers an entity as new when it has an id of an object type and a value of null or of a primitive type (e.g. int or long) and a value of 0.
If your entity has an attribute with #Version annotation that attribute will be used to determine if the instance is a new one.
You have the following options in order to make it work:
Set the id to null and configure your database schema so that it will automatically create a new value on insert. After the save your entity instance will contain the generated value from the database.
Note: Spring Data JDBC will set the id even if it is final in your entity.
Leave the id null and set it in a Before-Convert listener to the desired value.
Let your entity implement Persistable. This allows you to control when an entity is considered new. You'll probably need a listener as well so you can let the entity know it is not new any longer.
Beginning with version 1.1 of Spring Data JDBC you'll also be able to use a JdbcAggregateTemplate to do a direct insert, without inspecting the id, see https://jira.spring.io/browse/DATAJDBC-282. Of course, you can do that in a custom method of your repository, as is done in this example: https://github.com/spring-projects/spring-data-examples/pull/441

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.

How to set user_id on Entity update?

I have entity with updatedAt and updatedBy audit fields. I want to set this fields only if entity state was changed. So, for dirty checking I can use either Hibernate EmptyInterceptor interface (overriding onFlushDirty() method) or JPA listeners (#PreUpdate). But how I can get current userId inside interceptor or listener? To my mind comes 2 solutions:
Pass userId to DAO layer, so I can create custom interceptor passing to it constructor userId and then use this interceptor when creating new Session.
Set current userId to public-static ThreadLocal variable so I have access to it from any place.
But I think both approaches are ugly. So may be there is exist some more elegant way to solve my problem?
Please note, I can't use Envers library (it don't fit our project requirements).
Thanks.
I recommend storing the Person object in the Http session when the user authenticates. This way you can grab it from the session and use the merge functionality in the entity manager to convert it back to an attached entity.
I personally also assign the entity to the requiring domain object in the dao and do not use the #PreUpdate because I do not want the entity to have to know how to retrieve the current user object.

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