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.
Related
When I use merge I expect that hibernate will copy not null values to a persisted entity and update the database. So in the bellow use-case, I expect that the merge will keep lastName with the value Last_0 and change firstName to the value First_1.
but actually, hibernate is just updating, meaning that lastName become null after the code execute
#Transactional
public void create() {
SomeEntity create = new SomeEntity();
create.setFirstName("First_0");
create.setLastName("Last_0");
this.getSession().save(create);
//now in the database id:1, firstName:First_0, lastName:Last_0
}
#Transactional
public void merge() {
SomeEntity merge = new SomeEntity();
merge.setId(1);
merge.setFirstName("First_1");
this.getSession().merge(merge);
// now in the database id:1, firstName:First_1, lastName:null
}
create();
merge();
I expect lastName to be with the value of Last_0 since it is merging. but it is null.
Thanks
You are confused about how Hibernate's merge() is implemented in practice. merge() is implemented under the hood in this case via a SQL UPDATE, meaning something like the following:
UPDATE some_entity
SET
firstName = 'First_1',
lastName = NULL
WHERE
id = 1;
That is, it will overwrite all the field values with whatever your detached entity happens to have. Note that if no record with this primary key id=1 existed, then Hibernate would have done an INSERT, not an update.
If you wanted to start off with the original values for the entity, then you should have used a get:
SomeEntity merge = (SomeEntity) this.getSession().get(SomeEntity.class, 1);
merge.setFirstName("First_1");
this.getSession().merge(merge);
Now the original values will "stick," except for the first name, which was overwritten to something else.
If you want hibernate to only save part of fields and not all of them
use #DynamicUpdate
Example here: https://www.baeldung.com/spring-data-jpa-dynamicupdate
As posted inside:
Actually, when we use #DynamicUpdate on an entity, Hibernate does not use the cached SQL statement for the update. Instead, it will generate a SQL statement each time we update the entity. This generated SQL includes only the changed columns.
When an object is written to database and the primary identifier (id) is known, it can be retrieved by the code below:
MyObject myObject = session.get(Class<MyObject>, id);
It seems, there is another way similar to get() method:
IdentifierLoadAccess<MyObject> ila = session.byId(Class<MyObject>);
MyObject myObject = ila.load(id);
I'm looking for a scenario which clarifies differences between them and describes the reason for having two similar methods for the same job in API.
same question can be asked about session.load() and session.byId().getReference().
Edit 1:
According to API documentation:
session.get() and session.byId().load() return persistent instance with given identifier, or null if there is no such persistent instance.
session.load() and session.byId().getReference() might return a proxied instance that is initialized in demand.
IdentifierLoadAccess allows you to specify:
LockOptions
CacheMode
even specifying both of them at once:
Post post = session
.byId( Post.class )
.with( new LockOptions( LockMode.OPTIMISTIC_FORCE_INCREMENT) )
.with( CacheMode.GET )
.load( id );
The same for getting a Proxy reference via getReference(id).
So, they are more flexible than the standard get or load which only take the entity identifier.
The similarity between
MyObject myObject = session.get(Class<MyObject>, id);
and
IdentifierLoadAccess<MyObject> ila = session.byId(Class<MyObject>);
MyObject myObject = ila.load(id);
is that both uses the copncept of hibernate cache mechanism but difference comes in fetching the data from database i.e
When we use session.get(Class,id) data from database comes in cache and you can make changes on that data and will be reflected back in database, as hibernate internally maintains a time stamp cache. This time stamp cache records the time at which a particular Hibernate managed table got modified and before returning the data from entity cache it validate whether the result cache are older with respect to table modification time.
But in case of session.byId().getReference() hibernate uses the concept of natural id in which data from database comes in cache but only onces.If you do any changes on that data using session.save(entity object) approach hibernate will throw an exception and if you do manually modification of table(insert,update,delete) it will not be reflected back when you fetch the data again as it always get the data from cache without checking whether that table for that entity has been modified again or not.
In case of session.get() and session.load() if there is any change in database like (insert,delete,update) of record it will get reflected either in the form of record or null pointer exception if record gets deleted.But in case of session.byId().load() and session.byId().getReference() it will first get the record from database when you try to fetch first time then it will save those record in session and will be shown to user from session only if any (insertion,deletion,updation) occurs then it will not be reflected back
It's mostly used in polymorphic association/queries. assume you have an entity named User with the BillingDetails association. If BillingDetails was mapped with
lazy="true" (which is the default), Hibernate would proxy the association target. In this case, you wouldn’t be able to perform a type-cast to the concrete class CreditCard (which is a subclass of BillingDetails) at runtime, and even the instanceof operator would behave strangely:
User user = (User) session.get(User.class, userid);
BillingDetails bd = user.getDefaultBillingDetails();
System.out.println( bd instanceof CreditCard ); // Prints "false"
CreditCard cc = (CreditCard) bd; // ClassCastException!
To perform a proxy-safe typecast, use load()
User user = (User) session.get(User.class, userId);
BillingDetails bd = user.getDefaultBillingDetails();
// Narrow the proxy to the subclass, doesn't hit the database
CreditCard cc =
(CreditCard) session.load( CreditCard.class, bd.getId() );
expiryDate = cc.getExpiryDate();
Note that you can avoid these issues by avoiding lazy fetching, as in the follow-ing code, using an eager fetch query
User user = (User)session.createCriteria(User.class)
.add(Restrictions.eq("id", uid) )
.setFetchMode("defaultBillingDetails", FetchMode.JOIN)
.uniqueResult();
// The users defaultBillingDetails have been fetched eagerly
CreditCard cc = (CreditCard) user.getDefaultBillingDetails();
expiryDate = cc.getExpiryDate();
Truly object-oriented code shouldn’t use instanceof or numerous typecasts. If you find yourself running into problems with proxies, you should question your design, asking whether there is a more polymorphic approach.
The key difference between get() and load() method is that load() will throw an exception if an object with id passed to them is not found, but get() will return null. Another important difference is that load can return proxy without hitting the database unless required (when you access any attribute other than id) but get() always go to the database, so sometimes using load() can be faster than the get() method. It makes sense to use the load() method if you know the object exists but get() method if you are not sure about object's existence.
I'm working on a legacy code base that uses JPA (not JPA-2), and have come across the following method in a DAO implementation class to retrieve a single entity by ID (which is also it's primary key):
public EmailTemplate findEmailTemplateById(long id) {
LOG.debug("Entering findEmailTemplateById(id='" + id + "')");
// Construct JPQL query
String queryString = "SELECT a FROM EmailTemplate a " +
"WHERE templateId = :templateId";
Query query = entityManager.createQuery(queryString);
query.setParameter("templateId", id);
LOG.debug("Using query " + queryString);
List<EmailTemplate> resultList = query.getResultList();
LOG.debug("Exiting findEmailTemplateByName(id='" + id + "') results size " + resultList.size() + " ( returns null if 0 )");
if (resultList.isEmpty() || resultList.size() == 0) {
return null;
} else {
return resultList.get(0);
}
}
I now need to write a similar DAO class for a different entity, and my method to find the entity by it's primary key looks a lot simpler! :
#Override
public EmailTemplateEdit findEmailTemplateEditById(long id) {
LOG.debug("Entering findEmailTemplateEditById(id={})", id);
return entityManager.find(EmailTemplateEdit.class, id);
}
The original author is not around to ask, so I'm wondering if anyone can suggest reasons as to why he constructed a JPQL query rather than simply using EntityManager#find(Class<T> entityClass, Object primaryKey)?
The javadoc for the find method says:
If the entity instance is contained in the persistence context, it is
returned from there.
which suggests some form of caching and/or delayed writes. The javadoc for the createQuery and getResultList methods don't say anything like this.
I am unaware of any business or technical requirement in this application that would preclude caching, or of any issues resulting from stale entities or similar. I will check these with the rest of the project team when available, but I just thought I'd canvas the opinion of the SO community to see if there might be other reasons why a query was constructed and executed instead of simply using find
(I've seen this: When use createQuery() and find() methods of EntityManager?. Whilst it answers the question re: difference between createQuery and find, it doesn't answer it in context of finding entities by primary key)
Updated with additional info
From looking at the other methods in the original DAO class, it looks like there has been a deliberate/conscious decision to not take advantage of JPA managed objects. As above, the method to find by primary key uses a JPQL query. The method to delete an entity also uses a JPQL query. And the method to update an entity makes a copy of the passed in entity object and calls EntityManager#merge with the copy (thus the copy is a managed object, but is never used or returned from the method)
Weird ....
Short answer, there is no difference between find and a select query.
Your question suggests that you are not entirely familiar with what an EntityManager and a Persistence context is.
EntityManager implementation are not required to be thread safe. If the EntityManager is injected by Spring or and EJB-container it is thread safe (because it is a thread-local proxy), if it is application managed (you created it by calling EntityManagerFactory.createEntityManager(), it is not thread safe, and you can't stor it in a variable, but have to create a new one every time.
The Persistence Context, is where entities live, whenever you create a new EntityManager you get a new Persistence context (there are exceptions to this rule). When you persist an Entity, or load an existing entity from the db (using find or query) it will be managed by the persistence context. When you commit a transaction JPA runs through ALL Entities managed by the Persistence context, and checks the state of the entity to find out which queries should be sent to the database.
The PersistenceContext can be seen as a first-level cache on top of the database. It is meant to have a short lifespan, typically no longer than the transaction. If you re-use the same entityManager for multiple transactions, the size could grow as more data is loaded, this is bad because every transaction has to run through all entities in the persistence context.
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.
Hibernate uses to classes to represent literals:
org.hibernate.jpa.criteria.expression.LiteralExpression
org.hibernate.jpa.criteria.expression.NullLiteralExpression
The first one will put a placeholder into the query and apply UserTypes to the value, the second one will simply put null into the query, thereby ignoring any configured UserType. CriteriaUpdateImpl will choose NullLiteralExpression if passed a null as second parameter to set(Path<Y>, X) and CriteriaBuilderImpl.literal(T) refuses null-values all together.
We use a UserType which stores Calendar-objects as AES-encrypted date-strings and are forced to replace null-values by 0-length data for compatibility reasons.
So the question is whether there is any way to archive the following using only JPA-APIs with a Hibernate-backend, as this is really ugly
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaUpdate<Entity> criteriaUpdate = criteriaBuilder.createCriteriaUpdate(Entity.class);
Root<Entity> root = criteriaUpdate.from(Entity.class);
criteriaUpdate.where(criteriaBuilder.equal(root.get("id"), id));
setPathToNull((CriteriaBuilderImpl) criteriaBuilder, criteriaUpdate, root.get("value"));
Query query = entityManager.createQuery(criteriaUpdate);
LOG.info("About to perform 2. update");
LOG.info("Updated " + query.executeUpdate() + " entities");
Helper:
private <Y> CriteriaUpdate<Entity> setPathToNull(CriteriaBuilderImpl criteriaBuilder, CriteriaUpdate<Entity> criteriaUpdate, Path<Y> value) {
return criteriaUpdate.set(value, new LiteralExpression<>(criteriaBuilder, (Y)null));
}
A full working example is provided at
https://github.com/TheConstructor/NullUpdate/tree/master/src/main/java/tc/vom
You may come up with other ways of accessing the property, but the property-name is provided as String and the entity can not be loaded beforehand, as this update is part of an reading-value-error-clean-up. If the name of the column would help you, it can be used as well.