How to set user_id on Entity update? - java

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.

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.

Spring Boot - Avoid update DB when using setters

I would like to use the setter of my Entity without updating the database.
credentials.setPassword(null);
return credentials;
While doing this, it automatically update the DB and set the password to null.
I would like to return the object credentials with password null, without updating the DB
Anyone knows how to do it?
You could (and maybe should) covert this Entity into a DTO before exposing it to wherever you are sending your data. That way you can fulfill whatever requirement you want (like not exposing the password) without updating your database.
if the entity is still associated with the session then there is a possibility of updating the database table when there is a change in the entity params.
refer this post for more details.
make sure that the entity detached and then make the changes or use DTO with the information you want to pass on to UI or destination.

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

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.

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 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.

Categories