Affects: spring-orm 6.1.0 / 6.1.1:

During a spring boot update (3.1.5 to 3.2), I noticed that fields in entities that are annotated as jakarta.validation.constraints.NotNull no longer lead to the correct DDL statements.

Example:

@Entity
public class TestEntity {
    @Id
    private Long id;

    @NotNull
    private String name;
}

should trigger ddl statement that contains the not null constraint for the name attribute:

create table test_entity (
    id bigint not null,
    name varchar(255) not null,
    primary key (id)
);

but it does generate the following ddl statement without the not null constraint for the name attribute:

create table test_entity (
    id bigint not null,
    name varchar(255),
    primary key (id)
);

Note that @Column(nullable = false) does still work. Only @NotNull does not.

I was able to boil it down to the spring-orm update from 6.0.13 to 6.1.0 (which happens when updating spring boot as mentioned above).

Please see the following git project where I reproduced the issue: https://github.com/cowclaw/spring-orm-problem-demo

The commits tell the whole story: 1. Show it works on spring boot 3.1.5 2. Show it is broken on spring boot 3.2.0 3. revert to spring boot 3.1.5 4. Show its broken when selectively updating spring-orm

Included is a test that fails when the DDL statement does not meet the expectations.

Thanks for the great job you do on spring framework and thanks for having a look into the issue! :smile:

Comment From: bclozel

Spring is not responsible for the generation of the schema, this is a hibernate feature.

Downgrading your sample to use ext['hibernate.version'] = "6.1.7.Final" in build.gradle and adding logging.level.org.hibernate.SQL=debug in application.properties shows the following:

2023-11-30T14:09:42.066+01:00 DEBUG 73356 --- [           main] org.hibernate.SQL                        : create table test_entity (id bigint not null, name varchar(255) not null, primary key (id))

Please reach out to the Hibernate community if you need help.

Comment From: cowclaw

Thanks for your reply @bclozel.

I can not reproduce what you see. I created a branch combining hibernate 6.1.7.Final with spring-orm 6.1.1. It shows the same behaviour as before. The generated ddl statement lacks the not null.

2023-11-30T14:38:26.666+01:00  INFO 3040342 --- [    Test worker] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 6.1.7.Final
2023-11-30T14:38:26.795+01:00  INFO 3040342 --- [    Test worker] SQL dialect                              : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
2023-11-30T14:38:27.065+01:00 DEBUG 3040342 --- [    Test worker] org.hibernate.SQL                        : 

    create table test_entity (
       id bigint not null,
        name varchar(255),
        primary key (id)
    )

Updating only hibernate to 6.3.1.Final and otherwise staying with the dependencies recommended by the spring boot plugin, the not null constraint is generated and the test is green as can be seen on the branch My first guess was hibernate as well - but I was not able to show it.

Comment From: bclozel

You should not upgrade to spring-orm 6.1.1, as this leads to an invalid combination of dependencies. Spring Boot 3.1.x depends on Spring Framework 6.0.x: your application ends of with a mix of Spring Framework 6.0.x and 6.1.x versions.

Just using Spring Boot 3.1.5 dependency management and downgrading to Hibernate 6.1.7.Final works as expected.

Comment From: cowclaw

Spring boot 3.1.5 does work without any changes to the hibernate version suggested by the spring boot plugin.

Comment From: bclozel

Reopening to further check for posssible regressions.

Comment From: bclozel

@cowclaw Sorry the conversation was confusing, I mixed things up locally with dependency management overrides and the dependency locking.

@jhoeller I've found that reverting locally #30549 makes it work. Is this a regression or is there a setup problem in the first place?

Comment From: jhoeller

So that processing reacts to the PersistenceUnitInfo.getValidationMode() setting? We set that to NONE if no validation provider is present, and to CALLBACK when a validation provider is present for the purpose of enforcing full error reporting for the provider bootstrap (e.g. failing when no EL provider is present which may otherwise remain unnoticed).

If some other code checks PersistenceUnitInfo.getValidationMode() and expects it to be something other than NONE even when no validation provider is present, I can see that being a problem indeed.

Comment From: bclozel

In this case the validation provider is present - with Spring Boot 3.1.5 I'm seeing AUTO and with 3.2.0 CALLBACK. I haven't figured out where this information is being picked up and how this influences the DDL generation or the metadata itself.

Comment From: jhoeller

Maybe DDL generation only checks for == AUTO instead of != NONE when deciding whether to kick in?

If this remains unsolvable on the DDL side, we can also roll back our change. It was just meant to help for error reporting when the validation provider setup is incomplete.

Comment From: bclozel

I looked into this a bit more and found that bean validation constraints are not applied at all to the generated schema if the ValidationMode is CALLBACK. See this code snippet in hibernate-orm:

https://github.com/hibernate/hibernate-orm/blob/4a6c26ca4be5d5d0d750a8b5999ecaecd1688a9d/hibernate-core/src/main/java/org/hibernate/boot/beanvalidation/TypeSafeActivator.java#L143-L146

I have found an issue that suggests that as well as other cleanup tasks related to this validation mode support: https://hibernate.atlassian.net/browse/HHH-12287

In light of that, I will revert #30549 and add a note about this Hibernate issue to prevent further changes on our side.

Comment From: jordanms

Is the fix released? I have this problem with Spring 6.1.4 (Spring Boot 3.2.3)

Comment From: bclozel

@jordanms yes this has been released with 6.1.2. You can create a new issue in this issue tracker if you manage to reproduce the problem with a minimal application (please attach this app as a zip to the issue or share the git repository URL).