Environment:
- Hibernate ORM version: 6.6.8.Final
- JPA provider: Hibernate
- Spring Boot version: 3.4.3
- Spring Data JPA version: (Managed via Spring Boot 3.4.3)
- Database: PostgreSQL 17.0
- JDBC Driver: PostgreSQL JDBC Driver 42.7.5
- JDK version: 21.0.4
- Operating System: Windows 11
Description:
When using hibernate.hbm2ddl.auto=create-drop
, Hibernate fails to generate the DDL column for a top-level entity field if there is a field with the exact same name inside a nested @Embeddable
structure that is itself mapped to a JSON-type column on the main entity table.
In my specific case:
- I have an entity
Order
with a fieldprivate Status status;
mapped to@Column(name = "status") @Enumerated(EnumType.STRING)
. - The
Order
entity also has a fieldprivate LogisticsInfo logisticsInfo;
mapped with@JdbcTypeCode(SqlTypes.JSON) @Column(name = "logistics_info", columnDefinition = "jsonb")
. LogisticsInfo
is an@Embeddable
class containing a fieldprivate LogisticsItem email;
.LogisticsItem
is also an@Embeddable
class and it contains a field namedprivate LogisticsStatus status;
.
During application startup with ddl-auto: create-drop
, Hibernate’s metadata processing seems to encounter a naming conflict between Order.status
and the property path Order.logisticsInfo.email.status
. Although logisticsInfo
is mapped to a single JSONB column, the internal field name status
appears to interfere with the DDL generation for the top-level Order.status
field.
The generated CREATE TABLE orders
statement omits the definition for the status
column entirely. This subsequently causes runtime PSQLException: ERROR: column "status" of relation "orders" does not exist
when attempting to persist an Order
entity.
Debug logs during startup showed messages like org.hibernate.mapping.BasicValue : Skipping column re-registration: orders.status
when processing the Order.status
field, strongly suggesting a naming conflict during metadata processing prevented the column from being correctly registered for DDL generation.
A simplified test entity (SimpleStatusTestEntity
) with only an ID and a status
field generated the correct DDL, confirming the base configuration and Hibernate DDL generation mechanism were working correctly in isolation. The issue appears specifically related to the combination of a top-level field and a same-named field within a nested Embeddable mapped to JSON.
Steps to Reproduce:
- Define necessary Enums:
// Example Enums enum Status { NORMAL, HIDDEN, DELETED } enum LogisticsStatus { Pending, Failed, Success } enum LogisticsType { Email }
- Define the nested Embeddables:
import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; //... other imports @Getter @Setter @NoArgsConstructor @Embeddable public static class LogisticsInfo { private LogisticsItem email; // Potentially other fields } @Getter @Setter @NoArgsConstructor @Embeddable public static class LogisticsItem { private LogisticsType type; private LogisticsStatus status; // <-- Field name conflict private String content; public LogisticsItem(String email) { this.type = LogisticsType.Email; this.status = LogisticsStatus.Pending; // <-- Field name conflict this.content = email; } }
- Define the main Entity with the conflict:
import dev.w0fv1.uclient.value.Status; import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.type.SqlTypes; //... other imports @Getter @Setter @NoArgsConstructor @Entity @Table(name = "orders") public class Order { // Simplified @Id @GeneratedValue private Long id; // <<< Top-level field >>> @Column(name = "status") @Enumerated(EnumType.STRING) private Status status = Status.NORMAL; // <<< Embeddable mapped to JSON >>> @JdbcTypeCode(SqlTypes.JSON) @Column(name = "logistics_info", columnDefinition = "jsonb") private LogisticsInfo logisticsInfo = new LogisticsInfo(); // Other necessary fields (e.g., Zone, Product, User FKs) for context // ... }
- Configure
application.yml
(or.properties
):spring: jpa: hibernate: ddl-auto: create-drop show-sql: true datasource: url: jdbc:postgresql://localhost:5432/yourdb username: youruser password: yourpassword driver-class-name: org.postgresql.Driver
- Start the Spring Boot application.
- Observe the application startup logs. The generated
CREATE TABLE orders ...
statement will be missing thestatus
column definition. - (Optional) Attempt to save an
Order
entity via its repository. This will trigger thePSQLException
.
Expected Behavior:
The generated CREATE TABLE orders (...)
statement should include a column definition for the top-level status
field, respecting the @Column
and @Enumerated
annotations, e.g.:
..., status varchar(255) check (status in ('NORMAL','HIDDEN','DELETED')), ...
Actual Behavior:
The generated CREATE TABLE orders (...)
statement completely omits the status
column. Example DDL generated (from logs):
create table orders (is_anonymous boolean not null, paid_price numeric(38,2) not null, payment_status smallint not null check (payment_status between 0 and 2), quantity integer not null, total_price numeric(38,2) not null, created_time timestamp(6) with time zone, id bigint not null, product_id bigint not null, updated_time timestamp(6) with time zone, zone_id bigint not null, title varchar(256), uuid varchar(256) unique, product_type varchar(255) not null check (product_type in ('Virtual','VirtualSubscription','VirtualAction','Tangible','Package','Service')), recipient_id varchar(255) not null, user_id varchar(255) not null, delivery_note jsonb, delivery_snapshot jsonb, logistics_info jsonb, primary key (id))
(Notice the absence of the status
column definition).
Workaround:
Renaming the conflicting field inside the nested @Embeddable
(LogisticsItem.status
to LogisticsItem.logisticsStatus
) resolves the issue, and the DDL for orders.status
is generated correctly.
Relevant Logs:
During metadata processing with the name conflict present, the following log line appeared when processing the Order.status
field:
DEBUG ... org.hibernate.mapping.BasicValue : Skipping column re-registration: orders.status
Disclaimer:
My primary language is not English, and this summary was partially generated with AI assistance from logs and debugging. I haven’t had the time to confirm if this issue persists in the very latest snapshot/release builds or if it has already been fixed. I’m reporting this based on version 6.6.8.Final. If this is confirmed as a bug in Hibernate, I hope this report helps in getting it fixed. If this is intended behavior, an issue with my configuration, or already fixed, please feel free to close this issue with an explanation.