Spring Data JPA simplifies database interactions in Java applications, by managing the complexity of object-relational mapping (ORM). At the hearth of this feature are annotation mapping Java objects to DB tables. In this reference, I explain how to use annotations such as @Entity, @Table, @Column, @Id, @ManyToOne, and more, using a practical example.

Entity: Task Class

Let’s walk through the key annotations using a practical example of a Task in a task management system. Here, the Task class is mapped to the tasks table.

@Entity
@Table(name = "tasks")
public class Task {
    // Fields and methods
}

By adding @Entity to the Task class, Spring Data JPA knows it should treat this class as a table in the database.

While @Entity tells Spring Data JPA that this class represents a table, @Table allows you to customize the table’s name and other settings. If @Table is omitted, the table name defaults to the entity class name.

Defining Primary Keys

In this example, the id field is a primary key, automatically generated and incremented by the database.

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;

Every JPA entity requires a primary key to uniquely identify each row in the table. The @Id annotation is used to mark the primary key field.

The @GeneratedValue annotation defines how the primary key is generated. Common strategies include:

  • GenerationType.IDENTITY: Relies on the database’s auto-increment feature.
  • GenerationType.SEQUENCE: Uses a database sequence (common in databases like PostgreSQL).
  • GenerationType.AUTO: Automatically selects the generation strategy based on the database.

Customizing Columns

Here, we see a field title mapped to a DB column named title, which is non-nullable, and has a length of 100 characters

@Column(nullable = false, length = 100)
private String title;

By default JPA maps fields to columns with the same name. However, the @Column annotation gives you control over column properties such as:

  • name: Define a custom column name.
  • nullable: Specifies whether the column can be null.
  • unique: Ensures the column contains unique values.
  • length: Sets the maximum length for String fields.

Persisted and Transient Fields

By default all the fields are saved to the database as columns even if you don’t define the @Column annotation. But you have a degree of freedom in defining what and how persistence is set.

@Data
@Entity
public class Task {
    //..

    // stored in the DB as boolean column "task_complete"
    private boolean taskComplete;

    // stored in the DB as text column "long_description"
    private String longDescription;

    // customized column name
    @Column(name = "deadline")
    private LocalDateTime dueDate;

    // Not stored in the DB
    @Transient
    private boolean isComputing;

    // Runtime computed property (not stored in DB)
    @Transient
    public int getDaysUntilDue() {
        return (int) ChronoUnit.DAYS.between(LocalDateTime.now(), this.dueDate);
    }
}

In absence of @Column annotation, each field in camelCase is persisted as DB column in snake_case.

If you want a column to have a different name than the default one, use @Column(name = "custom_column_name"). Otherwise, JPA/Hibernate handles the column naming automatically.

To avoid persisting fields or methods, use the @Transient annotation.

Entities Relations

Assume we have two entities: User and Task. In the example below, many Task entities are related to one User through a foreign key column named "user_id" on the Task entity.

@Entity
@Table
public class User {
    @Id
    private long id;
}

@Entity
@Table
public class Task {
    @Id
    private long id;

    @ManyToOne
    @JoinColumn(name = "user_id", nullable = false)
    private User user;
}

The @JoinColumn annotation is used to specify the foreign key column that establishes the relationship between two tables. You can customize the foreign key column name and ensure it is non-nullable, enforcing the relationship between the entities.

JPA, Hibernate, or Spring Data JPA?

JPA (Java Persistence API) is a specification, not an implementation that defines a standard for ORM in Java. E.g. it defines annotations like @Entity, @Id, @Column; a query language like JPQL, and entity lifecycle management. It lacks a specification, so you need an ORM framework like Hibernate, EclipseLink or other to use JPA features in your app.

Hibernate is a popular ORM framework and JPA provider. Beyond implementing the JPA specifications, it provides the Hibernate Query Language (HQL), which is an extension of JPQL for advanced query support. It also provides advanced features like: Caching, lazy loading, inheritance mapping, batch processing, and native SQL queries, and advanced configurations, e.g. a fine-grained control over SQL generation.

Spring Data JPA is a Spring Framework project built on top of JPA and Hibernate (or any other JPA provider). Its main features are:

  • Integration with Spring dependency injection, transaction management, and other features.
  • Repository abstraction: through the JpaRepository you can use common CRUD methods (e.g., save(), findById(), delete()).
  • Query method by convention: which allows to define queries directly in repository method names (e.g., findByTitle() or findByUserId()).
  • Custom query support, supporting JPQL, HQL, and native queries when custom data retrieval is needed.

Conclusion

Spring Data JPA (~ Hibernate ~JPA) annotations provide a powerful way to map Java obejects to DB tables and columns. In this post we explored some of the most used annotations, including: @Entity, @Table, @Id, @GeneratedValue, @Column, @ManyToOne, and @Transient.

By understanding these annotations, you can take control of your entity mapping ant tailor your DB interactions to meet your application’s needs.


0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *