JPA - update more fields on entity properly - java

I would like to do a simple update on a managed JPA entity on many fields.
First I query the entity which I would like to update:
MyEntity managedEntity = entityManager.createQuery(query).getSingleResult();
Then I need to update this entity with a new "MyEntity" object which is not managed by JPA:
MyEntity notManagedEntity = new MyEntity();
Of course it would be possible to read each field from notManagedEntity and set them to managedEntity which would do the update.
But is there a more elagant way to update the managed entity without needing to get and set every field one by one?

AFAIK, it's not a good idea to change a reference to a managed entity, because the persistence context will still reference the old entity, so any flush would trigger an exception... It's even more important when working with complex entities holding collections or ManyToOne references.
A related topic: What is the best way to update the entity in JPA

I would suggest to read the question:
Copy all values from fields in one class to another through reflection
This looks like the answer to your question. You then have to create a copy of the entiy you got from
MyEntity managedEntity = entityManager.createQuery(query).getSingleResult();
After you created the copy of you java object, you have to set the identifier from the entity an new value an inset this new Object to the persistent layer again.
From desinge perpective it looks a bit confusing, but if this is the request, at least from technical perspective it should work.

Related

Does the most optimal way to update an entity by ensuring that it's existing is the combination of getOne() and save() in Spring Data JPA?

I know there are several ways that we can follow to update an existing entity. I will mention two ways below.I need to clarify if my opinion regarding these two methods is correct. The ultimate goal is to find the most optimal way that we can follow to update an entity by ensuring the primary key is valid before the updation. So feel free to state any other mechanism.
Method 1:
First get and proxy object related to updating entity with the aid of getOne() method.
Then setting the necessary fields to be updated by setters to that proxy object.
Use save method to update the entity.
In here I am using getOne() method before the save to ensure that I am updating an existence entity.Otherwise according to my knowledge if the entity's primary key is not an auto generated field any new primary key inserted to the save() method will create a new entity in the database. So by following the getOne() method I can have an EntityNotFound exception in the end of the save() method call if the inserted id is not an existing one.
So basically following this way I can omit a database hit which will trigger generally to find() the given id is existing before saving the entity.According to my opinion this is the most optimal way that we can follow to update a given entity by ensuring the given id is always existing .The problem is I didn't see this method in any tutorial or website before.This was implemented by self.So I need to know if there any disadvantage that we can have by following this mechanism over the method two.
try {
CustomerCategory customerCategory = customerCategoryRepository.getOne(customerCategoryRequestDto.getCode());
customerCategory.setStatus(customerCategoryRequestDto.getStatus());
CustomerCategory savedCustomerCategory = customerCategoryRepository.save(customerCategory);
CustomerCategoryResponseDto customerCategoryResponseDto = modelMapper.map(savedCustomerCategory, CustomerCategoryResponseDto.class);
return customerCategoryResponseDto;
} catch (EntityNotFoundException e) {
throw new EntityNotFoundException(ExceptionMessage.MSG_ENTITY_NOT_FOUND);
}
Method 2:
First see if the given id is existing in the database.Otherwise inform the end user that the given id is not existing in the database.
Then perform the updation. In here I am using an query to update the entity .But this can be easily achieve by setting the necessary fields to the found entity from the previous find() method call and by calling the save() method.
But the problem here I see is we need one additional query to ensure that the given id is valid.So I think this will definitely decrease the database performance.But in most websites and tutorials most of the authors follow this mechanism.I don't see any practical use case or need that we need this mechanism over the first one.
Optional<CustomerCategory> searchedCustomerCategory = customerCategoryRepository.findById(customerCategoryRequestDto.getCode());
if (!searchedDbpCustomerCategory.isPresent()) {
throw new EntityNotFoundException(ExceptionMessage.MSG_ENTITY_NOT_FOUND);
}
customerCategoryRepository.updateCustomerCategory(CustomerCategoryStatus.DELETED.toString(), customerCategoryRequestDto.getCode());
Actually, there are cases and cases.
When you only need a reference (e.g. to maintain a relationship), then use JpaRepository#getOne(ID id).
When you need to update the entity, use CrudRepository#findById(ID id).
But why?
Be aware that doing this:
Entity ref = repo.getOne(1l);
ref.setAttribute("value");
has only one difference from doing this:
Entity entity = repo.findById(1l);
entity.setAttribute("value");
The difference is that the load operation is Lazy in getOne and Eager in findById.
But there's no performance gain when you need to update a column from the entity.
When you call a setter or a getter on the reference, the persistence provider DOES hit the database in order the bring the data. This is what Lazy load mean - only load WHEN YOU NEED. By calling a setter, you do need the data, then the persistence provider will perform a select on the database.
So there's no real gain on using getOne over findById when your goal is to update the entity.
Also, be aware that getOne is deprecated in favor of getById, which does just the same.

JPA declare an entity as not new to avoid duplication

my JPA implementation is EclipseLink 2.3.0. I get one object from a cache system, when I try to update an object that contains the object retrieved from cache, JPA saves a new record of that even if the object has id set.
I suspect that the problem is because we don't retrieve that object using EntityManager, in that case the object would be set not new. Is there a way to flag the object as not new in EntityManager even if I didn't retrieve it from there?
Thanks

JPA SortedSet is not being resorted after persist

I have an entity with the following field:
#ManyToMany(cascade = { CascadeType.ALL }, targetEntity = Comment.class)
#JoinTable(name = "program_to_comment")
#OrderBy("position")
private Set<Comment> comments = new HashSet<Comment>();
but I have the problem that whenever I persist it using:
Program p = entityManager.persist(entity);
the field comes with the objects sorted as it was sorted in the entity object.
Suppose the entity object is configured as following: Program(comments:[Comment(position:15), Comment(position:10)], ...), persisting the entity (entityManager.persist), it will store both comments and the program entity itself to the database. But the resulted entity from the persist method invocation is an object as follows: Program(comments:[Comment(position:15), Comment(position:10)], ...), in the same order gave to the persist method.
From my point of view at this point the resulted entity should present the values following the specified #OrderBy rule, or am I missing something?
Additional information:
JPA2
Hibernate 4.2.0.Final
OrderBy simply add an order by clause to the query used to load the comments of a program. Nothing more. The rest is under your responsibility. So if you want the comments sorted by position when adding comments and persisting them, you have to take care of this by yourself.
I have personally never found this annotation to be really useful. I have also found it not to work in every case, particularly when using a query to fetch programs with their comments, with an order by clause already present in the query. I generally prefer not to use theis annotation, and provide a getSortedComments() method which returns a sorted set or list of comments, using a comparator.

Need a setter on a JPA/Hibernate entity for an ID AND a foreign entity

I have two entities with a many-to-one relationship between them. Many B entities are related to an A entity. My B table has an A_ID column. In some situations, we may have a B entity that has an A_ID but doesn't relate to any row in A. I know this isn't ideal but it's part of the old system and we can't really touch this portion of the code.
With proper Hibernate entities, I'm able to add B entities when we have an A entity to associate with using B's setA() method and B's getAs().add() method. I don't see any way to deal with the case where I'd like to add a B entity that isn't associated with an A entity (B's A_ID = 10, for example, where there's no A with an ID = 10). Is this possible or am I stuck breaking our entity hierarchy at this point and dealing with it manually?
Is there some way to add a setAId() to the B entity and either use that or the setA() method but not both? Will Hibernate allow this? When I try to add the getter and setter on B, I get the following error:
MyHibernateException: Hibernate SessionFactory creation failed, hibernateCfgFileNm=hibernate.xml
...
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: test.B column: A_ID (should be mapped with insert="false" update="false")
Sorry for not providing code. I think this explanation is sufficient but if code is required, I can add it.
With my understanding of entity relations (I assume you're mapping these with standard JPA #ManyToOne / #OneToMany relations?) I believe you're going to run into some fun issues here.
I would create my A entities using a sequence generated ID, and allow for 'placeholder' A entities which do nothing but carry the A_ID from the old system. Just make your code aware that not every A entity will be a full entity (it may just place-hold those A_IDs you mentioned above) and gracefully handle it, allowing you to always provide an A entity, with an ID decoupled from the legacy system.
I don't think you'll be able to map the relation to the same column and set a value in the column without the relation causing issues -- unless perhaps if you mark the relation as LAZY fetch and catch the exceptions which are bound to occur when it tries to resolve the non-existent Entity A. That may also work -- in that case, try to map the relationship column as read-only (insert="false" update="false"), and do everything with the setAId(id). You may need to have your setA(A) method only operate on IDs rather than managed collections, since reading a collection will by it's very nature fetch any related rows.
Your best bet is to decouple from the IDs of the legacy system if the semantics of the legacy IDs loosely translate to 'here, this ID may be a null object.'

Can I override generated IDs?

I'm using JPA 1, Hibernate and Oracle 10.2.0 and my entities are defined like this:
#Entity
#Table(name="TERMS")
public class Term implements Serializable {
#Id
#GenericGenerator(name = "generator", strategy = "guid", parameters = {})
#GeneratedValue(generator = "generator")
#Column(name="TERM_ID")
private String termId;
}
I have a situation where an XML representation of the Entity (and child entities) will be coming in through a web service to update/replace existing ones. My thought was to just delete the old ones and re-create it from the incoming XML.
However, doing a persist when my entities having existing IDs seem to make Hibernate very angry. So is this actually possible or is it better to avoid deleting them and just trying to do it with merge?
Angriness from hibernate:
org.hibernate.PersistentObjectException: detached entity passed to persist: com.idbs.omics.catalog.entity.Term
Thanks
My thought was to just delete the old ones and re-create it from the incoming XML. However, doing a persist when my entities having existing IDs seem to make Hibernate very angry..
Indeed, you cannot assign an Id when it is supposed to be generated, at least not with Hibernate that won't consider the entity as new but as detached (the JPA specification is a bit blurry on the exact rules in this case but that's how Hibernate behaves, see 5.1.4.5. Assigned identifiers for more hints).
So is this actually possible or is it better to avoid deleting them and just trying to do it with merge?
To make the delete/insert possible for the web service use case, you'd have to either:
not assign the id ~or~
use a special version of the entity without a generated identifier ~or~
use bulk operations(?)
The alternative if you're actually updating detached entities would be indeed to use a merge (but have a look at these previous questions just in case).
Which approach is better? I don't know, it think it depends on your needs. The later seems more natural if you're updating existing entities. With the former, you'd really get "new" entities (including a new value for the optimistic locking column). Depending on the exact implementation of the process, performances might also vary. And, by the way, what about concurrency (just to mention it, I'm not really expecting an answer)?
You can use EntityManager.merge to save an updated version of the entity. Be aware that this returns another object than the one you pass to it, because it basically fetches the entity from the database, updates the persistent properties from the object you pass and saves the persistent object.
See http://blog.xebia.com/2009/03/23/jpa-implementation-patterns-saving-detached-entities/ for more information on this problem.

Categories