I want a Product to have an optional ProductType. But during schema generation, Hibernate for some reason ignores my definition and creates a non-nullable column for a foreign-key. We are using Hibernate for schema management and SQL Server 16.
This is what Hibernate logs out during schema creation:
Hibernate: drop table if exists my_product
Hibernate: drop table if exists my_producttype
create table my_product (id uniqueidentifier not null, name varchar(255) not null, typeId uniqueidentifier not null, primary key (id))
create table my_producttype (id uniqueidentifier not null, name varchar(255) not null, primary key (id))
alter table pg_product add constraint FK123456789 foreign key (typeId) references my_producttype
This is the schema as defined in Java:
@Entity
@Table(name = "my_product")
public class Product extends CoreModel<Product> {
@NotNull
private String name;
@ManyToOne(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY)
@JoinColumn(name = "typeId", nullable = true)
@JsonIgnore
@org.springframework.lang.Nullable
private ProductType productType;
}
and this abstract model class
@MappedSuperclass
public abstract class CoreModel<T extends CoreModel<T>> {
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@Column(updatable = false)
@GeneratedValue(strategy = GenerationType.UUID)
@Id
private UUID id;
}
and finally the referenced entity
@Entity
@Table(name = "my_producttype")
public class ProductType extends CoreModel<ProductType> {
@NotNull
private String name;
}
the relevant application.yml
spring:
datasource:
driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
jpa:
database: sql_server
open-in-view: false
hibernate:
ddl-auto: create
naming:
implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
properties:
hibernate:
dialect: org.hibernate.dialect.SQLServerDialect
jdbc:
time_zone: UTC
What I tried:
System information
Found the culprit, and I have to apologize, since you couldn't have figured it out.
The property productType
is private and therefore has getters and setters, which I omitted. Crucially, during the refactor, I forgot to remove the @NotNull annotation there - and today I learned, that Spring/Hibernate introspects these as well during Schema generation, not just the field annotations.
Fix was to change from this:
public @NotNull ProductType getProductType() {
return productType;
}
public void setProductType(@NotNull ProductType productType) {
this.productType = productType;
}
to this
public ProductType getProductType() {
return productType;
}
public void setProductType(ProductType productType) {
this.productType = productType;
}
Sorry about that, but thanks for being my rubber-duck