hibernate-core
5.6.9.Final
Odd behavior by Hibernate was discovered – we create a new object with null property value (the property has @NotNull annotation), persist the object, then fill the NotNull property and at the moment of flushing JdbcSQLIntegrityConstraintViolationException occurred. Looks like the object avoid the hibernate-validator in this case because the JdbcSQLIntegrityConstraintViolationException appears instead of ConstraintViolationException.
More details:
We have the Employee object with @NotNull property Name.
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@DynamicInsert(value = true)
@DynamicUpdate(value = true)
public class Employee {
@Id
@GeneratedValue
private int id;
@Column(nullable = false)
@NotNull
private String name;
private Double salary;
}
If execute the code below – everything seems ok, the created object inserting correctly.
@Test
public void whenSetNameBeforePersist_thenSuccess(){
Employee employee01 = new Employee();
employee01.setName(employeeName);
entityManager.persist(employee01);
int idEmployee = employee01.getId();
entityManager.flush();
entityManager.clear();
Employee employeeAfterCommit = entityManager.find(Employee.class, idEmployee);
Assert.assertEquals(employeeAfterCommit.getName(), employeeName);
}
If we set name after persist, despite the fact that the property in our object has a not null value we get an error.
@Test
public void whenSetNameAfterPersist_thenSuccess(){
Employee employee01 = new Employee();
entityManager.persist(employee01);
employee01.setName(employeeName);
int idEmployee = employee01.getId();
//There was expectation that the Employee object would be flushed successfully (like the whenSetNameBeforePersist_thenSuccess test does)
//but instead a "org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: NULL not allowed for column "NAME"; SQL statement" error occurred.
//It seems like Hibernate is trying to insert the version of the Employee object passed to persist method.
//This looks incorrect, shouldn't Hibernate inserts current version of the Employee object instead to avoid the error?
//In case if current behavior is correct
//then why is the error not the ConstraintViolationException we get in the whenNameIsNull_thenConstraintViolationException test?
//This looks like Hibernate is trying to insert the Employee object without any validation.
entityManager.flush();
entityManager.clear();
Employee employeeAfterCommit = entityManager.find(Employee.class, idEmployee);
Assert.assertEquals(employeeAfterCommit.getName(), employeeName);
}
It looks like the Hibernate is trying to generate sql insert statement with the created object that has not current version but the version generated at the persist moment.
Code below confirms the information. NotNull property was filled before persist. Now the error disappeared and we see sql insert statement and sql update statement in logs. Does not “update after insert” generate performance problems?
@Test
public void updateSalaryAfterPersist_thenSuccess(){
Employee employee01 = new Employee();
employee01.setName(employeeName);
entityManager.persist(employee01);
employee01.setSalary(100.0);
int idEmployee = employee01.getId();
entityManager.flush();
entityManager.clear();
Employee employeeAfterCommit = entityManager.find(Employee.class, idEmployee);
Assert.assertEquals(employeeAfterCommit.getName(), employeeName);
}
2023-11-14 19:10:29.189 DEBUG 1484 — [ main] org.hibernate.SQL :
insert
into
employee
(name, id)
values
(?, ?)
2023-11-14 19:10:29.189 TRACE 1484 — [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [Employee01]
2023-11-14 19:10:29.189 TRACE 1484 — [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [INTEGER] - [1]
2023-11-14 19:10:29.204 DEBUG 1484 — [ main] org.hibernate.SQL :
update
employee
set
salary=?
where
id=?
If behavior above is correct in case when hibernate-validator is enabled we should get ConstraintViolationException instead of JdbcSQLIntegrityConstraintViolationException. And even if we fix the situation with exceptions why we get ConstraintViolationException when property is filled with not null value.