Home > Enterprise >  How do I fetch OneToMany mappedBy when creating the child entity before the parent?
How do I fetch OneToMany mappedBy when creating the child entity before the parent?

Time:07-12

I have a model like this:

class Message {

    @Id
    private UUID id;

    // ...

    @OneToMany(mappedBy = "messageId")
    private List<Value> values;
}

class Value {
    private UUID messageId;
}

The Value entities are being created in a JPA session, then, in another session, I create a Message in which I provide the id myself (which matches the messageId of existing Value).

After I have persisted the Message, when I try to call getValues() from it, I get null. What's the best way to solve this? Can I programmatically fetch the relation? Should I open another session?

CodePudding user response:

Solution 1: explicitly initialize child entities during parent entity creation
Main idea of that solution is to create an additional method for loading Value entities by messageId in ValueRepository and use it explicitly to initialize values collection during Message entity creation.
Repository for loading Value entities:

public interface ValueRepository extends JpaRepository<ValueEntity, Long> {    
    @Query("SELECT v FROM ValueEntity v WHERE v.messageId = :messageId")
    List<ValueEntity> findByMessageId(Long messageId);
}

Mesage creation and values collection initialization:

    public Message createMessage() {
        Message message = new Message();
        message.setId(1L);
        message.setValues(valueRepository.findByMessageId(message.getId()));
        entityManager.persist(message);
        return message;
    }

Solution 2: perform Flush and Refresh
AfterMessage persists operation you can perform Flush operation, which will synchronize entity with database state, and then Refresh operation, which will reread the state of the given entity.

    public Message createMessage() {
        Message message = new Message();
        message.setId(1L);
        entityManager.persist(message);
        entityManager.flush();
        entityManager.refresh(message);
        return message;
    }

I think Solution 1 is preferable, it is better from a performance perspective because the flush operation can take additional time.

UPDATE:
In case of merge operation, use returned persisted entity for the refresh, instead of init object.

    public Message createMessage() {
        Message message = new Message();
        message.setId(1L);
        Message persistedMessage = entityManager.merge(message);
        entityManager.flush();
        entityManager.refresh(persistedMessage);
        return persistedMessage;
    }

Or better divide save and update operations

   public Message saveOrUpdateMessage() {
        Message message =  entityManager.find(Message.class, 1L);
        if (message == null) {
            //handle new entity save
            message = new Message();
            message.setId(1L);
            entityManager.persist(message);
            entityManager.flush();
            entityManager.refresh(message);
        }
        //handle existing entity update
        ValueEntity valueEntity = new ValueEntity();
        valueEntity.setId(2L);
        valueEntity.setMessageId(message.getId());
        entityManager.persist(valueEntity);
        message.getValues().add(valueEntity);
        return message;
    }
  • Related