skip to content

Columns & AttributeConverters

Fine-tuning how a single attribute lands in a column, and plugging in your own Java-to-JDBC conversions. Interviewers ask about AttributeConverter to see whether you can map value types (money, encrypted strings, JSON) cleanly instead of polluting entities.

part ofHibernateoverview, primer and where to startread it →
on this pageshow

questions

6

What does the JPA @Column annotation control on a simple entity attribute, and what happens to an attribute that carries no @Column at all?

level: juniorimportance: must knowfreq 58%

answer

  1. name/table + insertable/updatable = runtime
  2. length, precision/scale, nullable, unique, columnDefinition = DDL only
  3. default string length 255
  4. no @Column ≠ not mapped — naming strategy supplies the name
  5. nullable=false is a constraint, not a Java check

basics

~20 s

@Column names the column and describes its shape — length, precision/scale, nullable, unique, columnDefinition — and controls whether it appears in INSERT/UPDATE via insertable/updatable. Omit it and the attribute still maps, using a default name and defaults such as length 255.

solid answer

~50 s

`@Column` is optional metadata on a mapped attribute. Its attributes fall into three groups. **Identification:** `name` (and `table`, to place the column in a secondary table). This is used in every generated statement. **Shape, used only when generating DDL:** `length` (default 255 for strings), `precision` and `scale` for `BigDecimal`, `nullable`, `unique`, and `columnDefinition`, which replaces the derived type with a raw SQL fragment you supply. **Statement participation, used at runtime:** `insertable` and `updatable`, which decide whether the column appears in generated `INSERT`s and `UPDATE`s. With no `@Column` the attribute is still persistent — mapping is opt-out, not opt-in. The column name is derived from the attribute name by the naming strategy, and the shape falls back to the defaults above. The common misconception is that `nullable = false` or `length` validates anything at runtime. They do not; they only shape generated DDL, and the database enforces the result.

go deeper

for a junior

Recall the main attributes and that @Column is optional; be able to name a column and set a length.

for a middle

Separate DDL-only attributes from the runtime ones, and explain the defaults — length 255, nullable true — and why precision/scale matter for BigDecimal.

for a senior

Explain what changes when schema generation is off, argue for startup schema validation to catch drift, and discuss the portability cost of columnDefinition.

for a principal

Position the annotation as documentation of a schema owned elsewhere, and set a team policy on whether mappings mirror migrations and how that mirror is verified.

## What @Column is and is not `@Column` describes the single database column behind a basic attribute. It is never required: an entity with no `@Column` anywhere still maps every non-transient attribute. What the annotation buys you is control over the column's name, its generated type and its participation in write statements. It is worth internalising the split between **what is used at runtime** and **what is used only when the provider generates DDL**, because most confusion about `@Column` comes from assuming the second group does something at runtime. ## The attributes **`name`** — the column name. Used in every `SELECT`, `INSERT`, `UPDATE` and `DELETE`. If omitted, the provider derives it from the attribute name via the naming strategy in force. Naming it explicitly protects the schema from a Java rename. **`length`** — the string length, default 255. DDL only: it becomes `varchar(255)` or similar. Nothing truncates or rejects a longer value in Java; an over-length string is sent to the database, which raises an error. **`precision` and `scale`** — for `BigDecimal` and `BigInteger`. `precision` is total significant digits; `scale` is digits after the decimal point. DDL only, but consequential: leaving them at the defaults for a money column often generates something that silently rounds or that stores far more digits than intended. For money, state them explicitly — for example `precision = 19, scale = 4`. **`nullable`** — default `true`. DDL only. `nullable = false` emits `NOT NULL`; it does not make the provider check for null before insert. If you want an application-level check that fails fast with a good message, that is Bean Validation's job; if you want a guarantee, that is the database constraint's job. **`unique`** — DDL only, a shorthand for a single-column unique constraint. Multi-column uniqueness belongs in the `@Table` annotation's `uniqueConstraints` (also DDL only). As with `nullable`, the provider never enforces it in memory. **`columnDefinition`** — a raw SQL fragment used verbatim as the column's type in generated DDL, e.g. `columnDefinition = "jsonb"` or `"text"`. It overrides the provider's type derivation, which makes it the escape hatch for vendor types — and simultaneously ties the entity to one database, since the fragment is not portable. It also affects only DDL; it does not change how the value is bound through JDBC. **`table`** — names a secondary table when the entity spans more than one, declared with `@SecondaryTable`. **`insertable` / `updatable`** — the two genuinely runtime attributes. They remove the column from generated `INSERT` and `UPDATE` statements respectively, while the column is still read on `SELECT`. ## When schema generation is off Most production systems own their schema with a migration tool and run with DDL generation disabled. In that world, everything in the DDL-only group is documentation: accurate or not, it changes nothing. Two consequences follow. First, don't reason about runtime behaviour from `nullable` or `length`. Second, if you keep those attributes as a mirror of the migration, enable **schema validation on startup** so drift is caught at boot rather than by a mystery error months later — validation compares the mapping against the real table and refuses to start on a mismatch. ## Common mistakes - Assuming `length = 50` truncates. It does not; the database rejects the row. - Assuming `nullable = false` throws a friendly Java exception. It produces a constraint violation from the database at flush time, potentially at commit, far from the code that set the null. - Leaving `precision`/`scale` unset on monetary amounts and discovering rounding in production. - Reaching for `columnDefinition` to fix a type problem and quietly making the mapping non-portable and invisible to the naming and type systems. - Putting `@Column` on the getter of a field-access entity, where it is silently ignored — annotation placement must match the entity's access type. - Believing `unique = true` prevents duplicates in an application whose schema was created by migrations that never included the constraint. ## A minimal habit Name the column explicitly, set `nullable` and `length`/`precision` to describe reality accurately even when generation is off, and let migrations create the schema. That gives you readable mappings, a validation check that catches drift, and no illusion that the annotation is enforcing anything.

  • If @Column(nullable = false) does not validate anything in Java, how does a null actually get rejected?
    The generated DDL carries a NOT NULL constraint, so the database rejects the INSERT or UPDATE when the statement is flushed, and the driver error surfaces as a constraint-violation exception wrapped by the provider. If the schema was created by migrations without that constraint, nothing rejects it at all. For an application-level check with a useful message, use a Bean Validation @NotNull, which runs before the SQL is generated.
  • When is columnDefinition the right tool, and what does it cost?
    It is the escape hatch when the provider's derived type is wrong for your database — a jsonb column, a specific text type, a vendor interval type. The cost is portability: the fragment is emitted verbatim, so the entity is now tied to one dialect, and the fragment is invisible to the provider's type system, so it will not change how values are bound. Prefer a proper type mapping or JDBC type code where one exists.

saying these in an interview costs you the question

  • Thinking @Column(length = n) truncates or validates the value in Java.
  • Thinking @Column(nullable = false) causes the provider to reject nulls before hitting the database.
  • Believing an attribute without @Column is not persisted.
  • Using columnDefinition routinely instead of the provider's type mapping.
  • Assuming unique = true guarantees uniqueness in a migration-managed schema.

context

open as a page

Explain how a JPA AttributeConverter works, how you attach one to an attribute with @Convert versus applying it automatically, and which kinds of attributes you are not allowed to convert.

level: middleimportance: must knowfreq 52%

basics

~20 s

AttributeConverter<X,Y> defines convertToDatabaseColumn and convertToEntityAttribute, translating between a Java type and a column type. Attach it per attribute with @Convert, or globally for the type with @Converter(autoApply = true). Identifiers, version attributes and associations cannot be converted.

open as a page

When would you map a column with the JPA @Column attributes insertable = false and updatable = false, and what does the provider do differently once you set them?

level: middleimportance: should knowfreq 45%

basics

~20 s

They remove the column from generated INSERT and UPDATE statements while it is still read on SELECT. Use them for database-maintained columns and for a foreign key mapped twice — once as an association, once as a plain field. The in-memory value can go stale.

open as a page

An entity maps a boolean to the characters 'Y' and 'N' through a JPA AttributeConverter. What can go wrong when you query, sort or index that column, and how do you work around it?

level: seniorimportance: should knowfreq 32%

basics

~20 s

Conversion happens at parameter binding and result extraction only. Bound query parameters are converted; native SQL is not, and any database-side expression — sorting, LIKE, functions, index order — sees the stored form ('Y'/'N'), not the Java value.

open as a page

How do you map a JSON document column — for example a PostgreSQL jsonb column — onto an entity attribute with Hibernate, and what must you watch out for once it is mapped?

level: seniorimportance: should knowfreq 38%

basics

~20 s

In Hibernate 6, annotate the attribute @JdbcTypeCode(SqlTypes.JSON) and give the column a json/jsonb definition; Hibernate serializes the object for you. Watch dirty checking of mutable payloads, whole-document rewrites, and that JPQL cannot navigate inside the document.

open as a page

What does the JPA @Lob annotation do to a String or byte[] entity attribute, and what problems do teams run into with it in production?

level: seniorimportance: should knowfreq 34%

basics

~20 s

@Lob tells the provider to map the attribute to a large-object type — CLOB for String, BLOB for byte[] — instead of varchar or varbinary. In production it costs memory: the whole value is loaded with the row and rewritten on update unless you split it out.

open as a page