0
votes

I have a form - Workflow where there are fields like wfName, assignedUser, dueDate, turnAroundTime. etc.

It is backed by an entity Workflow with a reference to the User entity as Many-to-One.

When a change is made to the assignedUser field( it is an email address) and the form is submitted, I get a Unique-constraint violation error on the USER entity.

I am not trying to achieve this. I only want to replace the User in the Workflow entity.

The save function is performed by a Stateful session bean, with an EXTENDED persistence context.

Am I missing something here? Is this the correct way to updated information in a referenced field?

While setting the updated User I am doing

User user = workflow.getUser();
//This user has its email address changed on the screen so getting a fresh reference of the new user from the database.
user = entitManager.createQuer("from User where email_address=:email_address").setParameter("email_address", user.getEmailAddress).getSingleResult();
//This new found user is then put back into the Workflow entity.
workflow.setUser(user);
entityManager.merge(workflow);

No exception is thrown at the time these lines are executed, but later in the logs I find that it threw a

Caused by: java.sql.SQLException: ORA-00001: unique constraint (PROJ.UK_USER_ID) violated

There is no cascading configuration present in the entities.

The following is the association code for the entities- The workflow-User relation

    @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "USER_ID", nullable = false)
@NotNull
public GwpsUser getUser() {
    return user;
}

public void setUserByUserId(User user) {
    this.user = user;
}

The User-Workflow Relation

@OneToMany(fetch = FetchType.LAZY, mappedBy = "User")
public Set<Workflow> getWorkflowsForUserId() {
    return workflowsForUserId;
}

public void setWorkflowsForUserId(
        final Set<Workflow> WorkflowsForUserId) {
    this.workflowsForUserId = workflowsForUserId;
}

In the SFSB I have two methods loadWorkflow() and saveWorkflow().

    @Begin(join = true)
    @Transactional
    public boolean loadProofData(){

//Loading the DataModel here and the conversation starts
}

If I add flushMode = FlushModeType.MANUAL inside @Begin. The saveWorkflow() method saves the data properly, only for the first time. I have to go somewhere else and then come back to this page if I want to make any further changes.

The saveWorkflow() method looks like

@SuppressWarnings("unchecked")
    public boolean saveWorkflow() throws FileTransferException {
//Do some other validations
for (Workflow currentWorkflow : workflowData) {
    User user = currentWorkflow.getUser();
//This user has its email address changed on the screen so getting a fresh reference of the new user from the database.
user = entitManager.createQuery("from User where email_address=:email_address").setParameter("email_address", user.getEmailAddress).getSingleResult();
//This new found user is then put back into the Workflow entity.
currentWorkflow.setUser(user);
}
//Do some other things
}

Not using the merge() method here, but still the problem persists.

1
Have you tried specifying CascadeType.MERGE in your association.Nayan Wadekar
Yes I tried it, still the same result. I do feel its more of a configuration thing.Amit
Then include association code for entities, will help others to understand.Nayan Wadekar
I did some more digging into this and I found out that, JPA will try to persist all dirty entities whenever it does a flush for flush mode AUTO. Now when the user makes a change to the text field at the UI, the entity(since it is managed) becomes dirty. Even if I assign a new entity to the reference, a dirty entity is still present in the memory, which is getting flushed. Any solutions on how to get past this??Amit
This explains why it goes for the User entity when all I want to change is the user in the Workflow entity, as the worfklow entity will get updated correctly though a dirty User entity will still remain in the memory.Amit

1 Answers

0
votes

Why are you calling merge? Is the workflow detached (serialized)?

If it is not detched, you should not call merge, just change the object and it should be updated.

You should have a setUser method, not setUserByUserId? Not sure how this is working, perhaps include your full code. Your get/set method might be corrupting your objects, in general it is safer to annotate fields instead of method to avoid code in your get/set method to cause odd side-effects.

Ensure you are not creating two copies of the object, it seems your merge is somehow doing this. Enable logging and include the SQL. Calling flush() directly after your merge will cause any errors to be raise immediately.