From CREATE TABLE to JPA Entity: the type mapping
Transcribing DDL into a JPA Entity is mechanical work full of small
decisions: which Java type for NUMERIC? IDENTITY or
SEQUENCE? Does nullable go in the annotation or do you
trust the database? Here's the mapping Schemint uses and the reasoning behind
each choice.
PostgreSQL to Java type table
| PostgreSQL | Java | Note |
|---|---|---|
BIGSERIAL / BIGINT | Long | generated id: @GeneratedValue(strategy = IDENTITY) |
SERIAL / INTEGER | Integer | |
VARCHAR(n) / TEXT | String | @Column(length = n) when there's a limit |
NUMERIC(p,s) | BigDecimal | never double for money |
BOOLEAN | Boolean | |
TIMESTAMP | LocalDateTime | |
TIMESTAMPTZ | Instant | a real instant, normalized to UTC |
DATE | LocalDate | |
UUID | UUID | |
JSONB | String | or @JdbcTypeCode(SqlTypes.JSON) with your own type |
Wrapper types (Long, not long): a nullable column
has to be able to represent absence, and a primitive with a magic 0 value is a
bug waiting to happen.
The generated skeleton
@Entity
@Table(name = "customers")
public class CustomerEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 120)
private String name;
@Column(nullable = false, unique = true, length = 180)
private String email;
}
- IDENTITY, not AUTO: with a Postgres
BIGSERIAL/identity column,IDENTITYreflects what the database actually does. AUTO lets Hibernate pick and can end up on a sequence table. - nullable/unique/length in the annotation: they don't
change runtime (the database is what validates), but they document the
contract on the class and let Hibernate validate the schema at boot
(
hibernate.ddl-auto=validate). - FK as a plain column first: generating
customer_id Longinstead of@ManyToOneavoids deciding fetch/cascade automatically; a relationship is a modeling decision, not a transcription one.