Columns and types
By default Hibernate makes a column from every field: the name of the field, a suitable type, varchar(255) for strings. That is enough to start, but a real table needs more: required columns, string lengths, its own names. All of this is set with annotations on the fields.
A richer Post
// src/main/java/com/example/blog/PostStatus.java
package com.example.blog;
public enum PostStatus {
DRAFT, PUBLISHED, ARCHIVED
}// src/main/java/com/example/blog/Post.java
package com.example.blog;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import java.time.LocalDateTime;
@Entity
@Table(name = "posts")
public class Post {
@Id
@GeneratedValue
private Long id;
@Column(nullable = false, length = 200)
private String title;
@Column(length = 10_000)
private String body;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private PostStatus status = PostStatus.DRAFT;
@Column(name = "published_at")
private LocalDateTime publishedAt;
private int views;
@Transient
private boolean selected;
protected Post() {
}
public Post(String title, String body) {
this.title = title;
this.body = body;
}
public void publish() {
status = PostStatus.PUBLISHED;
publishedAt = LocalDateTime.of(2026, 3, 15, 10, 0);
}
// getters
}Hibernate creates this table:
Hibernate: create table posts (views integer not null, id bigint not null, published_at timestamp(6), title varchar(200) not null, body varchar(10000), status enum ('ARCHIVED','DRAFT','PUBLISHED') not null, primary key (id))| Annotation | What it does |
|---|---|
@Table(name = "posts") | Table name instead of the class name |
@Column(name = "published_at") | Column name instead of the field name |
nullable = false | Adds not null |
length = 200 | String length: varchar(200) |
@Enumerated(EnumType.STRING) | Store the enum constant by its name |
@Transient | Do not store the field at all |
H2 and MySQL have a special enum column type; in PostgreSQL the same mapping gives a varchar column with a check of allowed values.
Java types and SQL types
| Java | SQL (H2) |
|---|---|
String | varchar(255) |
int, long | integer not null, bigint not null |
boolean | boolean not null |
BigDecimal | numeric(38,2) |
LocalDate | date |
LocalDateTime | timestamp(6) |
Instant | timestamp(6) with time zone |
Primitive types always get not null, because a primitive cannot hold null. For money use BigDecimal, not double. For long texts there is also @Lob, which gives a clob column.
Checking the stored values
// src/main/java/com/example/blog/Main.java
emf.runInTransaction(em -> {
Post post = new Post("Column mapping", "Types, enums and dates");
post.publish();
em.persist(post);
});
Object[] row = emf.callInTransaction(em -> (Object[]) em
.createNativeQuery("select title, status, published_at, views from posts")
.getSingleResult());
IO.println(java.util.Arrays.toString(row));The program reads the row with plain SQL and prints it:
[Column mapping, PUBLISHED, 2026-03-15T10:00, 0]The status is stored as the text PUBLISHED, the date as a timestamp.
Common mistake
Without @Enumerated(EnumType.STRING) an enum is stored as its position number:
Hibernate: create table posts (status tinyint not null check ((status between 0 and 2)), ...)DRAFT becomes 0, PUBLISHED becomes 1. If someone later adds a constant at the start of the enum, all old rows silently change their meaning. Always write @Enumerated(EnumType.STRING).