I am looking a smart and easily readable way to get the id of a persisted entity using JPA. The id is an Integer.
One could think of the following solutions:
Without using GeneratedValue strategy. This requires looking for a free id before persisting, then putting it into the entity to be persisted: cumbersome, but works.
With a GeneratedValue strategy. The persistence provider will take care of the id generation. This looks smarter, but how to get the id?
See below for solution 2
MyEntity en = new MyEntity();
en.setName("My name");
em.persist(en);
System.out.println(en.getId());
This prints a null id!
Any suggestions? I am using MySql, EclipseLink, but need a portable solution.
persist is not guaranteed to generate the ID. The ID is guaranteed to be generated at flush time only. So if you really need the ID before the transaction ends (and the entity manager is thus flushed), call flush() explicitely to get the ID:
MyEntity en = new MyEntity();
en.setName("My name");
em.persist(en);
em.flush();
System.out.println(en.getId());
Related
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.
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);
THe title can't definitely reflect my question, however I don't know how to express. I have a JPA entity (VatOperatorBalance which has a field saleBalance), lets say I retrieve the entity at the first time, and get a entity (VatOperatorBalance#3d6396f5), its saleBalance is 100.0. Now there are other operations which has modified the saleBalance to 200, now I query from database and get a new entity (VatOperatorBalance#10f8ed), sure the saleBalance of this entity is 200.0. However what make me confused it the saleBalance of the old entity (VatOperatorBalance#3d6396f5) is also 200.0.
All these queries and operations are in a single transaction, and the query isn't by EntityManager.find(java.lang.Class<T> entityClass, java.lang.Object primaryKey) which will return entity from cache.
Below is my code
#Rollback(true)
#Test
public void testSale_SingleBet_OK() throws Exception {
// prepare request ...
// query the VatOperatorBalance first
VatOperatorBalance oldBalance = this.getVatOperatorBalanceDao().findByOperator("OPERATOR-111");
//this.entityManager.detach(oldBalance);
logger.debug("------ oldBalance(" + oldBalance + ").");
// the operation which will modify the oldBalance
Context saleReqCtx = this.getDefaultContext(TransactionType.SELL_TICKET.getRequestType(),
clientTicket);
saleReqCtx.setGameTypeId(GameType.VAT.getType() + "");
Context saleRespCtx = doPost(this.mockRequest(saleReqCtx));
RaffleTicket respTicket = (RaffleTicket) saleRespCtx.getModel();
this.entityManager.flush();
this.entityManager.clear();
// assert vat sale balance
VatOperatorBalance newBalance = this.getVatOperatorBalanceDao().findByOperator("OPERATOR-111");
logger.debug("------ newBalance(" + newBalance + ").");
assertEquals(oldBalance.getSaleBalance().add(respTicket.getTotalAmount()).doubleValue(), newBalance
.getSaleBalance().doubleValue(), 0);
}
This testcase will fail, I don't understand why this will happen. JPA entity manager will update all entities of same entity type? The oldBalance entity and newBlance entity have same entityId, however they are different Java instance, what happened in JPA entity manager? If I detach the oldBalance entity from EntityManager, testcase will pass.
Note: my test is using Spring4.0.5 and JPA2.1
#piet.t since the entityManager would recognize it is the same entity by its primary key (feel free to try it). So all changes made to this entity through the same entityManager will all affect the same java instance
So in a entity manager, a given entity type with given primary key, there should be only one java instance or managed entity(if query from entity manager, no matter what query criteria, by id or not, the same java instance(managed entity) will be returned).
However in my test case, the entity 'oldBalance' will be updated by "the operation which will modify the oldBalance", and then the call of entityManager.clear() will detach all entities managed by this entity manager, that says 'oldBalance' is detached too.
And the 'newBalance' is managed entity then, that is why they have different java instance identifier. If 'oldBalance' is managed, for example by call entityManager.merge(), it will be the same instance of 'newBalance'.
I think most of your confusion does arise from the flush()-call in your code.
Calling flush will always store the changed value to the database - that's the whoe point of calling flush. When using transactions the changed value might still not be visible via other connections due to the databases transaction machanism but your entityManager will only see the changed value.
Without the clear-call your query - even though it is not using find - would still return the same instance that was previously created (VatOperatorBalance#3d6396f5) since the entityManager would recognize it is the same entity by its primary key (feel free to try it). So all changes made to this entity through the same entityManager will all affect the same java instance while modifications through another entity manager will most likely cause an exception because the entity was update from another transaction.
Some queries might cause an implicit flush, since the cached changes might influence the query-result, so all changes have to be written to the database before executing the query to get a correct result-set.
I hope that does help a bit.
I have problems updating entities in Googles App Engine.
EntityManager em = ... // constructed like in the doc
MyEntity myE = new MyEntity();
myE.setType("1"); // String
em.persist(myE);em.refresh(myE);
myE.setType("2");
em.merge(myE);em.refresh(myE);
I expect a entity with type="2", but there is only one entity with type="1" :-(
That's the correct behaviour, let me explain (I assume that all your code runs in the same persistence context / transaction).
# This line sets the value in the in-memory object without changing the database
myE.setType("2");
# this line doesn't do anything here, as the entity is already managed in the current
# persistence context. The important thing to note is that merge() doesn't save the
# entity to the DB.
em.merge(myE);
# This reloads the entity from the DB discarding all the in-memory changes.
em.refresh(myE);
It's because merge creates a new instance of your entity, copies the state from the supplied entity, and makes the new copy managed. You can find more info on merge vs. persist here and a full discussion about it here
I was facing similar issue too. My issue is solved after my put the Reresh() after Commit().
It would be something like:
em.getTransaction().begin();
//Code to update the entity
em.persist(myE);
em.getTransaction().commit();
em.refresh(myE)
This will ensure the updated entity in JPA Cache gets refreshed with the updated data.
Hope this helps.
I have a object A which maps to table A in DB
class A {
Integer id;
String field2,field2;field3 ,... fieldN;
//lots of other attribute
}
Now i want to write a DAO api that just updates a single field.One approach is that i can first load the object then changes the attribute i need and then use merge api
//start transcation
A a = session.load(A.class, id);
A.setfieldP(newValue)
session.merge(A)
//commit transcation
Now if i use following code
//start transcation
A a = new A();
a.setId(id); //set a id by which object A exists in DB
A.setfieldP(newValue)
session.merge(A)
//commit transaction
Now second approach all fields except id and fieldP are set to null
1)Now is there any other approach?
2)Can i use update instead of merge ?
If you need to update lots of entities at once the most efficient way is to use a query:
Query query = session.createQuery("update EntityName set fieldP = 'newValue' "
+ "where id IN (75, 76)");
query.executeUpdate();
This allows you to change field values without loading the entity or entities into memory.
It is best practice is to use named queries and named parameters - the above implementation is just an example.
I usually prefer session.get vs session.load, as session.get will return null as opposed to throwing an exception, but it depends on the behavior you want.
loading the object, setting your field, and calling either
session.merge(myObject)
is the standard way, although you can also use
session.saveOrUpdate(myObject)
as long as the object hasn't been detached, which in your case, it won't have been detached. Here is a good article explaining the differences in merge and saveOrUpdate.
In your second example, you are editing the primary key of the object? This is generally bad form, you should delete and insert instead of changing the primary key.
Using JPA you can do it this way.
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaUpdate<User> criteria = builder.createCriteriaUpdate(User.class);
Root<User> root = criteria.from(User.class);
criteria.set(root.get("fname"), user.getName());
criteria.set(root.get("lname"), user.getlastName());
criteria.where(builder.equal(root.get("id"), user.getId()));
session.createQuery(criteria).executeUpdate();
One more optimization here could be using dynamic-update set to true for the entity. This will make sure that whenever there is an update, only field(s) which are changed only gets updated.