Consider the following entity:
@Entity
@Table(name = "my_entity")
public class MyEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "myEntitySeq")
@SequenceGenerator(name = "myEntitySeq", sequenceName = "my_entity_seq")
private Long id;
@OneToOne(optional = false)
@JoinColumn(name = "other_id")
private OtherEntity other;
@Column(nullable = false)
private String fieldA;
@Column(nullable = false)
private byte[] fieldB;
public MyEntity() {}
public MyEntity(OtherEntity other) {
this.other = other;
}
// public getter/setters for all fields...
}
All database fields should be non-null.
I then have a transactional method that should create or update this entity:
@Transactional
public void createOrUpdate(OtherEntity other, String fieldA, byte[] fieldB) {
var myEntity = myEntityRepository.findByOther(other)
// Create and get a new managed entity, missing fields will be updated right after:
.orElseGet(() -> entityManager.merge(new MyEntity(other)))
myEntity.setFieldA(fieldA);
myEntity.setFieldB(fieldB);
// Once the transaction ends I expect the new entity to be INSERTed, otherwise UPDATEd
}
But this code fails with an error saying it cannot insert the new entity because it tries to insert null fields into the table (fieldA
and fieldB
are both null when calling merge, even if I update the state later).
My understanding was that since SQL queries are deferred until the end of a transaction, calling merge first to get a managed instance followed by updates to the state would properly defer a complete insertion at the end.
Instead it seems to defer an incomplete INSERT
followed by an UPDATE
, which trips the database NOT NULL
constraints on the incomplete insert…
Removing the NOT NULL
constraint allow the previous code to not fail because it allows the incomplete insert to go through, and somehow it also updates the row with the properties set after. But this means my database schema allows null values in places where I don’t want them.
Am I wrong to expect entityManager.merge(...)
followed by sets to group everything into a single INSERT
statement? Is it expected that doing merge
will schedule an INSERT
with the state provided at this point, and only later UPDATE
using the state as changed later in the transaction?
Paul Maréchal is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.