How to update detached entities? - java

I want to perform multiple updates on an #Entity. But based on some evaulations, I have to execute long running tasks in between. The task itself is based on a property of that entity. Thus, I first have to fetch the entity, merge some content, run the long task, and merge again.
This would span the database transaction, which is probably not correct:
#Transactional
public void update(DTO dto) {
entity = findOne(dto.id);
updateEntityFieldsFromDTO(entity, dto);
if (entity.hasTaskCondition) {
result = runLongTask(entity.property); //spanning the tx
mergeResultIntoEntity(entity, result);
}
}
Problem: the runLongTask() will block the transaction and keep the database connection open maybe for a long time, without interaction on the db.
Thus I thought creating single #Transactional methods, and running the task ouside of those tx:
//wrapper method without a transaction
public void updateFacade(DTO dto) {
entity = service.update(dto.id);
if (entity.hasTaskCondition) {
//running outside the transaction
result = runLongTask(entity.property);
service.mergeResultIntoEntity(entity.id, result);
}
}
Then my transactional methods would be as follows:
#Transactional
public Entity update(DTO dto) {
//first select the entity
entity = findOne(dto.id);
//then merge the content
updateEntityFieldsFromDTO(entity, dto);
return entity;
}
#Transactional
public Entity mergeResultIntoEntity(id, result) {
//again select the entity
entity = findOne(dto.id);
//now merge result into entity within tx
return entity;
}
Question: is it correct to always pass the entity.id to the #Transactional methods, and then fetch the object from db again inside those tx methods?
I think I cannot pass the entity directly into the #Transactional methods from updateFacade, as the entity is detached. Correct?

Related

Hibernate EntityManager keeps old data

I have Java EE application with Hibernate. I want to implement a feature that every minute updates one of existing rows in database. I have following classes:
#Singleton
#Startup
public class TimerRunnerImpl implements TimerRunner {
#EJB
private WorkProcessor workProcessor;
private String jobId;
#Timeout
#AccessTimeout(value = 90, unit = TimeUnit.MINUTES)
#TransactionAttribute(value = TransactionAttributeType.NEVER)
public void doProcessing(Timer timer) {
jobId = workProcessor.doWork(jobId);
}
//other methods: startTimer, etc
}
#Stateless
public class WorkProcessorImpl implements WorkProcessor {
#EJB
private MyEntityDao myEntityDao;
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
#Override
public String doWork(String jobId) {
if (jobId == null) {
MyEntity myEntity = myEntityDao.oldestEntityToProcess();
String uuid = UUID.randomUUID().toString();
myEntity.setJobId(uuid);
myEntityDao.update(myEntity); // this invokes merge()
return uuid;
} else {
// line below can never find entity, although there is one in DB
MyEntity myEntity = myEntityDao.findByJobId(jobId);
myEntity.setSomeProperty("someValue");
// some other updates
myEntityDao.update(myEntity); // this invokes merge()
return jobId;
}
}
}
First run of doWork updates MyEntity with job ID. This is being persisted into database - I can query it manually from SQLDeveloper. Second run always fails to find entity by job ID. In case I try to retrieve it by entity_id in debug mode, the object retrieved from Entity Manager has job id with previous value.
This is not cache problem, I have tried on each run to evict all cache at the beginning and results are identical.
As far as I understand, transaction is around workProcessor.doWork(jobId). I find confirmation of this by the fact that when this method returns I can see changes in DB. But why does EntityManager keeps my unmodified object and returns it when I query for it?

Spring data saving entities in a batch AND updating entities with JPA repository

I have a service that has a #Transactional method that does a lot of operations with managed entities using JpaRepositiry interfaces such as updating values and deleting entities.
On another hand in the same transactional method I have a code that creates a lot of entities in a batch
private void methodA() {
......
fetch fieldsToModify and projectsToDelete
......
log.debug("Modifying fields {}", fieldsToModify.size());
issueTrackerFieldRepo.save(fieldsToModify);
log.info("Delete projects {}", projectsToDelete.size());
issueTrackerProjectRepository.delete(projectsToDelete);
log.info("Adding projects {}", projectsToAdd.size());
saveEntitiesInBatch(projectsToAdd);
}
private void saveEntitiesInBatch(List<?> entities) {
for (int i = 0; i < entities.size(); i++) {
Object p = entities.get(i);
log.debug("Creating entity {}", p.getClass().getSimpleName());
entityManager.persist(p);
if (i % batchSize == 0) {
log.debug("Flushing entity manager for {}", i);
entityManager.flush();
entityManager.clear();
}
}
entityManager.flush();
entityManager.clear();
log.debug("Flushing entity manager");
}
where fieldsToModify and projectsToDelete are lists of entities that got fetched using jpa repository in the same method and then modified.
The problem is that when calling a batch method saveEntitiesInBatch all modified before entities become 'forgotten'. This is also what entityManager.clear() javadoc says.
I want to have all this code run in the same transaction. How can I make batched insert and jpa repository live friendly together?

Jackson mapping a relationship

I'm using Jersey and am expecitng a POST as an entity. However thst POST will also contain the UUID for one of its relationships:
Jersey Resource:
#POST
public WorkstationEntity save (WorkstationEntity workstationEntity) {
//WorkflowProcessEntity workflowProcessEntity = workflowProcessService.findByUUID();
workstationService.save(workstationEntity);
return workstationEntity;
}
How can I adjust the following mapping so it'll recognize the relationship and save correctly? Currently the workflow_process_id is NULL when it's saved and I have to query for the entity manually.
The JSON being posted is... {name: Workstation 1; workflow_process_id: 1}
private WorkflowProcessEntity workflowProcess;
#ManyToOne
#JoinColumn(name = "workflow_process_id", referencedColumnName = "id")
public WorkflowProcessEntity getWorkflowProcess() {
return workflowProcess;
}
public void setWorkflowProcess(WorkflowProcessEntity workflowProcess) {
this.workflowProcess = workflowProcess;
}
workstationService
#Transactional
public void save(WorkstationEntity workstationEntity) {
workstationRepository.save(workstationEntity);
}
Can you show code for workstationService? are you using Hibernatr or simple jdbc or any other orm tool?
I think inside workstationService.save(workstationEntity); you will need to attach workstationEntity to session (in case of Hibernate Hibernate Session). and then save it..
If I understand the problem it is that the returning json has a null id for the attached WorkstationProcessEntity id field. This is most likely a problem when you are trying to persist / merge the entity the transaction is not being committed before returning the detached entity. If you are using a persist make sure that you commit the transaction otherwise the id's will be null. Otherwise if you are using a merge this will commonly fix the problem.
protected T persist(T obj) {
EntityManager em = ThreadLocalPersistenceManager.getEntityManager();
EntityTransaction tx = em.getTransaction();
try {
if (! tx.isActive()) {
tx.begin();
}
em.persist(obj);
} finally {
if (!tx.getRollbackOnly()) {
tx.commit();
}
}
return obj;
}
The other likely cause is that your fetch is not set to eager so the datastore will only fetch the entity when it is accessed and by the time you are returning from the post the child entity is not attached. This is the most likely cause for your problem. What you should try is to access the workstation entitites getWorkflowProcess before closing the entity manager. Otherwise the attached entities will be null. Or add the FetchType.Eager annotation to fetch the child entities from the database when the parent is accessed.

javax.jdo.JDODetachedFieldAccessException when I ask for a List<T> using JPA and Datastore

im having javax.jdo.JDODetachedFieldAccessException when i want to, after retrieve all of my Entites as a List in my DAO implementation, ask for one atrribute object from my Entity.
public List<T> findAll() {
this.entityManager = SingletonEntityManagerFactory.get().createEntityManager();
EntityTransaction tx = this.entityManager.getTransaction();
try {
tx.begin();
return this.entityManager.createQuery(
"select f from " + clazz.getName() + " as f").getResultList();
}finally {
tx.commit();
if (tx.isActive()) {
tx.rollback();
}
this.entityManager.close();
}
}
for instance, supposing T has a property of class A that is already an Entity persisted, i can't get A after having List
But i don't have this problem if I only look for a single Entity by Id. I obtain my entity and I can ask without problems for its attribute objects already persisted
public T getById(final Key id) {
return getEntityManager().find(clazz, id);
}
now i can do
A a= t.getA();
How can I write my implementation of findAll() avoiding this error? maybe another Component instead of EntityManager? How can i make it generic, and not having to implement specific code for specific type of entities?
What you do there doesn't make sure the field is loaded before leaving that method, so either access it, or make sure it is fetched by default.

How to persist a JPA autogenerated value before commit?

Hi I am beginner on the JPA world, I have a question on the auto-generated id. We are using OpenJPA, My application requires that one operation which creates bunch of related objects must be inside a single transaction which will be part of global transaction (XA). I am struggling in get the auto-generated id and use it to set values in other object. Here is the snapshot:
#ENTITY
#Table(name="TDepart")
class Department{
private long id;
#GeneratedValue(strategy= GenerationType.TABLE)
public long getId();
}
//And some classes like
class Professor {
void setDepartmentId(long id);
}
Now I have a business operation:
void doSomething()
{
Department depart = new Department();
handleProfessors (depart);
handleStudent (depart);
//and someother rountines need to refer department
}
//sample code which will getId
void handleProfessors(Department depart)
{
Professor p = new Professor ();
p.setDepartmentId(depart.getId);
}
So the Department.getId() will be called several times. The doSomething() will be in a single managed transaction, but the GeneratedValue will use an unmanaged tx. Now may problem is: whenever the getId is called, it will return a new value, and when the department is final persisted, the id is the latest number, so all other objects refer to an non-exists department. Is there anyway to handle this so that the id is (kindof) persist?
I have a loose requirement solution, which will create an dummy department first and persist it, so the ID is not change. The code is similar to this:
void doSomething()
{
Department depart = createEmptyDepartment(); // always new tx so department is created;
try {
reallyDoSomehing(); // tx required so it is part of global tx
}
catch (SomeException e) {
removeEmptyDepartment(depart);
}
Now I do not know how I can set the tx for removeEmptyDepartment(), if is required it will use the global request so it will be rollback as well. If it is new tx it will cause a deadlock since reallyDoSomething() will lock the db row.
Please, give me some ideas on how to solve it.
Thanks,
Howard.
I don't fully understand your issue, but I'm thinking that rather than setting the departmentId in your professor class, you should be setting the Department instead
i.e.
void setDepartmentId(long id);
change to
void setDepartment(Department d);
The id components should be handled automatically by the entity manager

Categories

Resources