Check entity existence before calling merge - jpa

I have a simple abstract DAO, and I have created the following method:
protected T update(T entity) {
return em.merge(entity);
}
where the entity is just any object annotated with #Entity in my application. Now... I want to throw an exception if you try to update a non existing object. I was going to perform a find before the merge, throwing an exception if the find operation returns null and merging if the entity exists. I was wandering if a better way exists for doing this.

A possible solution: You can do a check based on your primary key. An entity must (should?) have an #Id field:
#Entity
public class Entity implements EntityInterface{
#Id
private Long id;
#Override
public Long getId(){
return this.id;
}
}
with the interface
public interface EntityInterface{
public Long getId();
}
By default, when you instantiate your entity, id is null and a value is assigned only after persisting in the database: The id will be generated by the method you defined via #GeneratedValue. Consequently, the following check should meet your requirement:
public abstract class AbstractService<T extends EntityInterface>{
protected T update(T entity){
// if by any chance you have to call this method on an entity with a null
// primary key, it means that the entity has not been persisted in the
// database yet
if(entity.getId() == null){
// or whatever
return null;
}
return em.merge(entity);
}
}
Hope this help
Source: JB Nizet's comment and personal code

Related

Transactional not working with JPA saveAll

I'm trying to save a list of entity objects. Here is my method which is defined in a Service class:
#Transactional
public void creteEmployee(List<Employee> employeeList) {
employeeRepository.saveAll(employeeList);
}
And here is my JPA repository:
public interface EmployeeRepository extends PagingAndSortingRepository<Employee, Long> {
Page<Employee> findByCompany(Pageable pageable, Company company);
}
I've a unique constraint defined in my entity. If any item from the employeeList violates the unique constraint, I expect no one from the employeeList will be saved. Unfortunately all items except the problematic one is saved and also an DataIntegrityViolationException is raised. What should be done to make it atomic?

how to filter out entity object inside entity in rest api

I am using Spring Boot to implement rest api. There are three entities SeqTb, PairTb, and GroupTb and they are nested. SeqTb has manytoone with PairTb. PairTb has onetomany relationship with SeqTb and also manytoone with GroupTb.
//SeqTb.java
#Entity
#Table(name="SEQ_TB")
public class SeqTb implements Serializable {
.......
#ManyToOne
#JoinColumn(name="PAIR_ID")
private PairTb pairTb;
......
}
// PairTb.java
#Entity
#Table(name="PAIR_TB")
#NamedQuery(name="PairTb.findAll", query="SELECT p FROM PairTb p")
public class PairTb implements Serializable {
#ManyToOne
#JoinColumn(name="GROUP_ID")
private GroupTb groupTb;
#OneToMany(mappedBy="pairTb", cascade=CascadeType.ALL)
private List<SeqTb> seqTbs;
}
//GroupId.java
#Entity
#Table(name="GROUP_TB")
public class GroupTb implements Serializable {
//bi-directional many-to-one association to PairTb
#OneToMany(mappedBy="groupTb", cascade=CascadeType.ALL)
private List<PairTb> pairTbs;
}
In my controller GET request with analysisId was handled in the following way:
#RequestMapping(
value = "/api/seqs/{analysis_id}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<SeqTb> getSeqByAnalysisId(#PathVariable("analysis_id") String analysis_id) {
SeqTb seq = seqService.findByAnalysisId(analysis_id);
return new ResponseEntity(seq, HttpStatus.OK);
}
I also create a bean class SeqServiceBean that extends the interface SeqService which in turn calls methods from the following JPA repository for query.
//SeqRepository.java
#Repository
public interface SeqRepository extends JpaRepository<SeqTb, Integer> {
#Override
public List<SeqTb> findAll();
public List<SeqTb> findByAnalysisId(String analysisId);
}
When I query a SeqTb object with SeqTb.PairTb == null, the api works just fine. However, if the analysisId I put in the url belongs to a SeqTb record that associates with a pairId which in turn belongs to a groupId, the program would go nuts. Below is the output, the first part output is correct (bold text). After that it keeps printing PairTb and GroupTb in loops (repeating keywords pairTb, groupTb).
{"rowId":8,"analysisId":"cce8d2c2-a6dc-4ee9-ba97-768f058abb50","analyteCode":"D","center":"UCSC",
"pairTb":{"rowId":4,"pairCode":"01ad975d-c2ed-4e4d-bd3b-c9512fc9073c","groupTb":{"rowId":1,"groupName":"PAWG_pilot-50","pairTbs":[{"rowId":1,"pairCode":"00ad0ffe-2105-4829-a495-1c2aceb5bb31","groupTb":{"rowId":1,"groupName":"PAWG_pilot-50","pairTbs":
Meanwhile I got lots of errors from tomcat server:
Caused by: java.lang.IllegalStateException: getOutputStream() has already been called for this response
at org.apache.catalina.connector.Response.getWriter(Response.java:565) ~[tomcat-embed-core-8.0.32.jar:8.0.32]
at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:212) ~[tomcat-embed-core-8.0.32.jar:8.0.32]
How do I ignore the nested entity object inside an entity and get only the meaning columns?
You can also annotate a property with #JsonIgnore in order to not output that field.
Found the solution. Created a value object that only contains the specific columns from entity and leave out the nested entity object. And it works.

OpenJPA auditing capabilities

I have a webservice that will be persisting and deleting data to a Database. I want to track in the database which usernames touched which rows of the database. In each table there are columns for usernames to be stored (update columns if you will). There are also triggers on the tables that will take a userID for the transaction and update that table with the username and password that attempted to insert. Is there a way in open JPA where I can get the username (which will be passed from the client) and update some kind of JPA object so that when JPA persists data, that user name will be thrown into the table?
One of the cleanest ways is to implement a common "mapped" superclass for your entities and use a method with #PrePersist annotation to populate the fields.
#MappedSuperclass
public class AuditedEntity {
#Id protected Integer id;
protected String lastUpdatedBy;
// Setters and getters here
#PreUpdate
#PrePersist
public void onChange() {
String user = .... // Do whatever is needed to get the current user
setLastUpdatedBy(user);
}
}
#Entity
public class Employee extends AuditedEntity {
// ....
}
Another option is to use a separate listener:
public interface AuditedEntity {
public static void setLastUpdatedBy(String username);
}
#Entity
#EntityListeners({ MyLogger.class, ... })
public class Employee implements AuditedEntity {
// ...
}
public class MyLogger {
#PreUpdate
#PrePersist
public void onChange(Object o) {
if(o instanceof AuditedEntity) {
String user = .... // Do whatever is needed to get the current user
((AuditedEntity) o).setLastUpdatedBy(user);
}
}
#PostPersist
#PostUpdate
public void logChange(Object o) {
// Log the successful operation
}
}

EF foreign key constraints and repository pattern

I have this simple Delete Get and Post methods in a asp.net mvc application
public ActionResult Delete(int ehrId, int id)
{
EHR ehr = ehrRepository.FindById(ehrId);
PhysicalTest test = ehr.PhysicalTests.Where(t => t.ID == id).Single();
return View(test);
}
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(int ehrId, int id)
{
EHR ehr = ehrRepository.FindById(ehrId);
PhysicalTest test = ehr.PhysicalTests.Where(t => t.ID == id).Single();
ehr.PhysicalTests.Remove(test);
unitOfWork.Commit();
TempData["Success"] = "You have deleted the Physical Test Succesfully";
return RedirectToAction("Index");
}
the problem is that when I try to delete a child object this way and EF will complain
The operation failed: The relationship could not be changed because
one or more of the foreign-key properties is non-nullable. When a
change is made to a relationship, the related foreign-key property is
set to a null value. If the foreign-key does not support null values,
a new relationship must be defined, the foreign-key property must be
assigned another non-null value, or the unrelated object must be
deleted.
One answer is to use a PhysicalTest(child element) repository instead of a EHRRepository.. but that doesnt seem like a good solution cause I want to enforce the security of always querying through the parent object to avoid a user from editing/deleting a physicalTest that doesnt belong to him.
I would love to just limit my repositories to just aggregate roots.
Heres my current SqlRepository generic implementation.... Im open to suggestions.
public class SqlRepository<T> : IRepository<T>
where T : class, IEntity {
internal SummumnetDB context;
internal DbSet<T> _objectSet;
public SqlRepository(SummumnetDB context)
{
this.context = context;
this._objectSet = context.Set<T>();
}
public IQueryable<T> Find(Expression<Func<T, bool>> predicate) {
return _objectSet.Where(predicate);
}
public void Add(T newEntity) {
_objectSet.Add(newEntity);
}
public void Remove(T entity) {
_objectSet.Remove(entity);
}
public IQueryable<T> FindAll()
{
return _objectSet;
}
public T FindById(int id)
{
return _objectSet.Single(o => o.ID == id);
}
}
Your EHR and PhysicalTest forms aggregate where EHR is aggregate root for PhysicalTest because PhyscialTest cannot exist without EHR (your exception says that FK in PhysicalTest cannot be null). Repository should exist per aggregate root and it should offer specific method to deal with relations.
Yes it will not be generic because generic approach for entities with different configurations and requirements doesn't work.
What is the problem with your code? Calling ehr.PhysicalTests.Remove(test) will not delete test. It will only sets its FK to null. To delete test as well you must really call context.DeleteObject(test). To allow direct deleting you must use identifying relation.

JPA merge readonly fields

We have the simplest CRUD task with JPA 1.0 and JAX-WS.
Let's say we have an entity Person.
#Entity
public class Person
{
#Id
private String email;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(insertable = false, updatable = false)
private ReadOnly readOnly;
#Column
private String name;
#XmlElement
public String getEmail()
{
return email;
}
public void setEmail(String email)
{
this.email = email;
}
#XmlElement
public Long getReadOnlyValue()
{
return readOnly.getValue();
}
// more get and set methods
}
Here is scenario.
Client make Web Service request to create person. On the server side everything is straightforward.
And it does work as expected.
#Stateless
#WebService
public class PersonService
{
#PersistenceContext(name = "unit-name")
private EntityManager entityManager;
public Person create(Person person)
{
entityManager.persist(person);
return person;
}
}
Now client tries to update person and this is where, as for me, JPA shows its inconsistence.
public Person update(Person person)
{
Person existingPerson = entityManager.find(Person.class, person.getEmail());
// some logic with existingPerson
// ...
// At this point existingPerson.readOnly is not null and it can't be null
// due to the database.
// The field is not updatable.
// Person object has readOnly field equal to null as it was not passed
// via SOAP request.
// And now we do merge.
entityManager.merge(person);
// At this point existingPerson.getReadOnlyValue()
// will throw NullPointerException.
// And it throws during marshalling.
// It is because now existingPerson.readOnly == person.readOnly and thus null.
// But it won't affect database anyhow because of (updatable = false)
return existingPerson;
}
To avoid this problem I need to expose set for readOnly object and do something like this before merge.
Person existingPerson = entityManager.find(Person.class, person.getEmail());
person.setReadOnlyObject(existingPerson.getReadOnlyObject()); // Arghhh!
My questions:
Is it a feature or just
inconsistence?
How do you (or would
you) handle such situations? Please
don't advice me to use DTOs.
Is it a feature or just inconsistence?
I don't know but I'd say that this is the expected behavior with merge. Here is what is happening when calling merge on a entity:
the existing entity gets loaded in the persistence context (if not already there)
the state is copied from object to merge to the loaded entity
the changes made to the loaded entity are saved to the database upon flush
the loaded entity is returned
This works fine with simple case but doesn't if you receive a partially valued object (with some fields or association set to null) to merge: the null fields will be set to null in the database, this might not be what you want.
How do you (or would you) handle such situations? Please don't advice me to use DTOs.
In that case, you should use a "manual merge": load the existing entity using find and update yourself the fields you want to update by copying the new state and let JPA detect the changes and flush them to the database.