How can I update every fields in Entity without write sql query:
(update Entity u set u.m1= ?1, u.m2= ?2, ... where u.id = ?3)
Class has 20+ fields and if I write sql query This will take a long time. And I often add new fields
Can I update everything automatically? Like this:
entityRepo.update(entity);
If i do entityRepo.save(); create unnecessary record in base.
No, you can use JpaRepository.save(S entity) that saves or updates the entity if existing.
To achieve that, make sure that the entity has its JPA #Id valued before invoking save() otherwise a new record will indeed be created.
This is an alternative to #davidxxx's answer.
If the transaction with which the entity was fetched is not yet closed (i.e. the entity is still attached), you can simply update the java-object and the changes will be committed to the database when the transaction is committed.
Related
I use spring data and hibernate. I have an Entity (TestEntity). I made a custom hibernate type that deserializes one String field to two columns.
If I persist an entity and then change it everything works fine and hibernate sends update query (it makes my type work and update query to DB "splits" my old column to two new).
But my goal is to make this king of migration for every record. I can't use an ordinary DB migration because there is some logic in my custom type.
I want to make something like this:
// here I persist all my entities
List<TestEntity> entities = entityRepository.findAll();
for (TestEntity entity : entities) {
// This piece of code does nothing, because when hibernate merges two entities, it understands, that nothing changed, so it won't send update query.
entityRepository.save(entity);
}
But I want him to send update query, although nothing has changed. Moreover, I want this hibernate behaviour to be in one place only (for example, I will create controller to execute this DB update). What is a solution to my problem? Is there any approach to its solving?
I don't understand why you need it but you need to detach the entity from the session for this to work.
As far as I understand, you need the EntityManger:
#PersistenceContext
private EntityManager entityManager;
...
List<TestEntity> entities = entityRepository.findAll();
for (TestEntity entity : entities) {
entityManager.detach(entity);
entityRepository.save(entity); // or entityManager.unwrap(Session.class).saveOrUpdate();
}
See Spring JpaRepository - Detach and Attach entity
This question already has answers here:
Spring Data JPA Update #Query not updating?
(5 answers)
Closed 2 years ago.
The community reviewed whether to reopen this question 1 year ago and left it closed:
Original close reason(s) were not resolved
Let's suppose to have this situation:
We have Spring Data configured in the standard way, there is a Respository object, an Entity object and all works well.
Now for some complex motivations I have to use EntityManager (or JdbcTemplate, whatever is at a lower level than Spring Data) directly to update the table associated to my Entity, with a native SQL query. So, I'm not using Entity object, but simply doing a database update manually on the table I use as entity (it's more correct to say the table from which I get values, see next rows).
The reason is that I had to bind my spring-data Entity to a MySQL view that makes UNION of multiple tables, not directly to the table I need to update.
What happens is:
In a functional test, I call the "manual" update method (on table from which the MySQL view is created) as previously described (through entity-manager) and if I make a simple Respository.findOne(objectId), I get the old object (not updated one). I have to call Entitymanager.refresh(object) to get the updated object.
Why?
Is there a way to "synchronize" (out of the box) objects (or force some refresh) in spring-data? Or am I asking for a miracle?
I'm not ironical, but maybe I'm not so expert, maybe (or probably) is my ignorance. If so please explain me why and (if you want) share some advanced knowledge about this amazing framework.
If I make a simple Respository.findOne(objectId) I get old object (not
updated one). I've to call Entitymanager.refresh(object) to get
updated object.
Why?
The first-level cache is active for the duration of a session. Any object entity previously retrieved in the context of a session will be retrieved from the first-level cache unless there is reason to go back to the database.
Is there a reason to go back to the database after your SQL update? Well, as the book Pro JPA 2 notes (p199) regarding bulk update statements (either via JPQL or SQL):
The first issue for developers to consider when using these [bulk update] statements
is that the persistence context is not updated to reflect the results
of the operation. Bulk operations are issued as SQL against the
database, bypassing the in-memory structures of the persistence
context.
which is what you are seeing. That is why you need to call refresh to force the entity to be reloaded from the database as the persistence context is not aware of any potential modifications.
The book also notes the following about using Native SQL statements (rather than JPQL bulk update):
■ CAUTION Native SQL update and delete operations should not be
executed on tables mapped by an entity. The JP QL operations tell the
provider what cached entity state must be invalidated in order to
remain consistent with the database. Native SQL operations bypass such
checks and can quickly lead to situations where the inmemory cache is
out of date with respect to the database.
Essentially then, should you have a 2nd level cache configured then updating any entity currently in the cache via a native SQL statement is likely to result in stale data in the cache.
In Spring Boot JpaRepository:
If our modifying query changes entities contained in the persistence context, then this context becomes outdated.
In order to fetch the entities from the database with latest record.
Use #Modifying(clearAutomatically = true)
#Modifying annotation has clearAutomatically attribute which defines whether it should clear the underlying persistence context after executing the modifying query.
Example:
#Modifying(clearAutomatically = true)
#Query("UPDATE NetworkEntity n SET n.network_status = :network_status WHERE n.network_id = :network_id")
int expireNetwork(#Param("network_id") Integer network_id, #Param("network_status") String network_status);
Based on the way you described your usage, fetching from the repo should retrieve the updated object without the need to refresh the object as long as the method which used the entity manager to merge has #transactional
here's a sample test
#DirtiesContext(classMode = ClassMode.AFTER_CLASS)
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = ApplicationConfig.class)
#EnableJpaRepositories(basePackages = "com.foo")
public class SampleSegmentTest {
#Resource
SampleJpaRepository segmentJpaRepository;
#PersistenceContext
private EntityManager entityManager;
#Transactional
#Test
public void test() {
Segment segment = new Segment();
ReflectionTestUtils.setField(segment, "value", "foo");
ReflectionTestUtils.setField(segment, "description", "bar");
segmentJpaRepository.save(segment);
assertNotNull(segment.getId());
assertEquals("foo", segment.getValue());
assertEquals("bar",segment.getDescription());
ReflectionTestUtils.setField(segment, "value", "foo2");
entityManager.merge(segment);
Segment updatedSegment = segmentJpaRepository.findOne(segment.getId());
assertEquals("foo2", updatedSegment.getValue());
}
}
I am working on concurrent update handling stuff, my scenario is- an employee can be marked inactive from UI, but if once marked inactive, it should not be allowed to marked inactive again. I want to achieve it by making the db update call with a condition update entity where status=active. I need the updated entity in return.
How to update an entity with a condition(where clause) and get the updated entity in method response in spring data jpa
employeRepository.save(employee); // is it possible to do something like this with a condition at the same time update method returns updated entity.
You are working against the grain of JPA here.
I suggest you do it the JPA way:
load the entity
If the status property is not inactive set it to active.
When ever the transaction ends this will be written to the database.
If you insist on using an update statement
You can use the following process:
If the entity is part of the current session:
flush all changes to the database.
evict the entity from the session.
perform an update either using SQL or JPQL with a where clause like this: ... WHERE id = :id and status <> 'inactive'
use the id to load the entity again.
You can use JPQL.
uery query = entityManager.createQuery(
"UPDATE employee SET status = \"inactive\"" +
"WHERE status=\"active\"");
query.executeUpdate();
or just spring data
#Modifying
#Query("update employee u status = `inactive` where status=`active` and ...")
void disableUsers(...);
however, to deal with concurrent queries a good way could be Optimistic locking and #Version annotation.
I have a critical section of code where I need to read and lock an entity by id with pessimistic lock.
This section of code looks like this right now:
MyEntity entity = entityManager.find(MyEntity.class, key);
entityManager.refresh(entity, LockModeType.PESSIMISTIC_WRITE);
It works OK, but as I understand in case when there is no entity in the hibernate's cache, we will use 2 read transactions to a database. 1st transaction to find the entity by id and another transaction to refresh and lock the entity.
Is it possible to use only one transaction in such scenario?
I would imagine something like:
boolean skipCache = true;
MyEntity entity = entityManager.find(MyEntity.class, key,
LockModeType.PESSIMISTIC_WRITE, skipCache);
But there is no such parameter like skipCache. Is there another approach to read an entity by id directly from the database by using EntityManager?
UPDATE:
This query will hit the first level cache in case the entity exists in the cache. Thus, it may potentially return the outdated data and that is why isn't suitable for critical sections where any read should be blocked:
MyEntity entity = entityManager.find(MyEntity.class, key, LockModeType.PESSIMISTIC_WRITE);
The question is about skipping the cache and not about locking.
I've just found a method getReference in the EntityManager which gets an instance, whose state may be lazily fetched. As said in the documentation:
Get an instance, whose state may be lazily fetched. If the requested
instance does not exist in the database, the EntityNotFoundException
is thrown when the instance state is first accessed. (The persistence
provider runtime is permitted to throw the EntityNotFoundException
when getReference is called.) The application should not expect that
the instance state will be available upon detachment, unless it was
accessed by the application while the entity manager was open.
As a possible solution to find and lock an up to date entity by id in one query we can use the next code:
MyEntity entity = entityManager.getReference(MyEntity.class, key);
entityManager.refresh(entity, LockModeType.PESSIMISTIC_WRITE);
This query will create an entity (no database query) and then refresh and lock the entity.
Why not directly pass the requested lock along with the query itself?
MyEntity entity = entityManager.find(MyEntity.class, key, LockModeType.PESSIMISTIC_WRITE);
As far as I understand this is doing exactly what you wanted. (documentation)
You can also set entityManager property just before you use the find method to address not hitting the cache.
Specifying the Cache Mode
entityManager.setProperty("javax.persistence.cache.storeMode", CacheStoreMode.REFRESH);
MyEntity entity = entityManager.find(MyEntity.class, key);
I'm working in a project right now, here is a piece of code:
public boolean getAll() {
TypedQuery<Tag> query = em.createQuery("SELECT c FROM Tag c WHERE (c.tagName !=?1 AND c.tagName !=?2 AND c.tagName !=?3) ", Tag.class);
query.setParameter(1, "Complete");
query.setParameter(2, "GroupA");
query.setParameter(3, "GroupB");
List<Tag> Tag= query.getResultList();
But when I try to do something like this:
Tag.get(2).setTagName = "Hello";
em.persist(Tag.get(2));
It considers it to be an update instead of a create? How can I make JPA understand that it's not database related, to detach the chains with the Database and create new register only changing its name for example?
Thanks a lot for any help!
Best regards!
EDIT:
Using the em.detach just before changing it values and persisting each of the list worked just fine!
Thanks everyone!
You haven't showed us how you are obtaining your list, but there are two key points here:
everything read in from an EntityManager is managed - JPA checks
these managed objects for changes and will synchronize them with the
database when required (either by committing the transaction or
calling flush).
Calling persist on a managed entity is a no-op - the entity is
already managed, and will be synchronized with the database if it
isn't in there yet.
So the first Tag.get(2).setTagName = "Hello"; call is what causes your update, while the persist is a no-op.
What you need do to instead is create a new instance of your tag object and set the field. Create a clone method on your object that copies everything but the ID field, and then call persist on the result to get an insert for a new Entity.
The decision whether to update or create a new entity object is done based on the primary key. You're probably using an ID on every object. Change or remove it and persist then. This should create a new entry.
If that doesn't work, you might need to detach the object from the Entity Manager first:
em.detach(tagObj);
and persist it afterwards:
em.persist(tagObj);
You can also force an update instead of creation by using
em.merge(tagObj)
There is no equivalent for forced creation AFAIK. persist will do both depending on PK.