How to Generate Values of Basic Entity Attributes with Hibernate


Take your skills to the next level!

The Persistence Hub is the place to be for every Java developer. It gives you access to all my premium video courses, monthly Java Persistence News, monthly coding problems, and regular expert sessions.


Hibernate and the JPA specification define multiple generators to create unique primary key values. An obvious question when learning about these generators is if you can apply them to basic attributes as well. Unfortunately, the answer is no.

But Hibernate’s ValueGenerator provides an easy-to-use alternative. As I will show you in this article, you can use it on all your basic entity attributes to generate their values before inserting or updating the entity object.

An Example Model

Before we take a closer look at the ValueGenerator implementation and its usage, I want to quickly show you the domain model we’ll use in this article. It consists of a simple ChessTournament entity, which implements the Event interface.

@Entity
public class ChessTournament implements Event {
    
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tournament_seq")
    @SequenceGenerator(name = "tournament_seq", sequenceName = "tournament_seq", initialValue = 100)
    private Long id;

    private String name;

    private LocalDate startDate;

    private LocalDate endDate;

    @GeneratorType(type = EventCodeGenerator.class, 
                   when = GenerationTime.INSERT)
    private String tournamentCode;

    @Version
    private int version;

    @ManyToMany
    private Set<ChessPlayer> players = new HashSet<>();

    @OneToMany
    private Set<ChessGame> games = new HashSet<>();

    ...
}

As you can see in the code sample, the ChessTournament entity uses a database sequence to generate unique primary key values, 2 associations to the ChessPlayer entity, and the basic attributes name, startDate, endDate, and tournamentCode. We will use the name and the startDate attributes in our ValueGenerator implementation to generate the value of the tournamentCode attribute.

The Event interface only defines the methods getName and getStartDate. The only reason I included the interface in this example is that it will provide us a simple way to implement a ValueGenerator that doesn’t depend on a specific entity class.

public interface Event {
    
    public String getName();

    public LocalDate getStartDate();
}

Implement a Custom Generator

The implementation of a custom generator is straightforward. You need to implement the ValueGenerator<T> interface, which only defines the method T generateValue(Session session, Object owner). Within that method, you can access the current Hibernate Session and the entity object to which the generator is applied with all its attribute values.

And before you ask, Hibernate’s primary key generators, unfortunately, don’t implement this interface and can’t be used as a ValueGenerator.

Let’s implement a ValueGenerator that generates the code of an Event.

public class EventCodeGenerator implements ValueGenerator<String> {

    Logger log = Logger.getLogger(EventCodeGenerator.class.getSimpleName());

    @Override
    public String generateValue(Session session, Object owner) {
        Event event = (Event) owner;
        String code = event.getName().replace(" ", "")
                        +"-"+event.getStartDate().getMonth().getValue()
                        +"-"+event.getStartDate().getYear();
        log.info("Generated event code: "+code);
        return code;
    }
    
}

In this simple example, I use the Event‘s nameand its start date to generate the code. When we assign this generator to an entity attribute, Hibernate will assign the value returned by this method to the attribute before it persists or updates it.

Use a Generator in Your Mapping

After defining the generator, you can use it with any entity attribute of a matching type. In the example of this article, I will apply it to the tournamentCode attribute of the ChessTournament entity class.

You can apply a generator to an entity attribute by annotating it with @GeneratorType and referencing the implementing class as the type attribute. The value of the optional when attribute defines when Hibernate shall apply the generator. You can choose between INSERT, ALWAYS, and NEVER.

@Entity
public class ChessTournament implements Event {

    @GeneratorType(type = EventCodeGenerator.class, 
                   when = GenerationTime.INSERT)
    private String tournamentCode;
    
    ...
}

After you applied the annotation, Hibernate will call the generateValue method of the referenced ValueGenerator depending on the provided GenerationTime enum value.

You can see that in the log output when I persist a new ChessTournament entity. Hibernate called the EventCodeGenerator and set the returned value as the value of the tournamentCode attribute.

18:08:05,323 DEBUG SQL:144 - select nextval ('tournament_seq')
18:08:05,334 DEBUG SQL:144 - select nextval ('tournament_seq')
Aug. 30, 2021 6:08:05 PM com.thorben.janssen.sample.model.EventCodeGenerator generateValue
INFO: Generated event code: MyTournament-8-2021
18:08:05,376 DEBUG SQL:144 - insert into ChessTournament (endDate, name, startDate, tournamentCode, version, id) values (?, ?, ?, ?, ?, ?)
18:08:05,380 TRACE BasicBinder:52 - binding parameter [1] as [DATE] - [null]
18:08:05,381 TRACE BasicBinder:64 - binding parameter [2] as [VARCHAR] - [My Tournament]
18:08:05,382 TRACE BasicBinder:64 - binding parameter [3] as [DATE] - [2021-08-01]
18:08:05,384 TRACE BasicBinder:64 - binding parameter [4] as [VARCHAR] - [MyTournament-8-2021]
18:08:05,384 TRACE BasicBinder:64 - binding parameter [5] as [INTEGER] - [0]
18:08:05,386 TRACE BasicBinder:64 - binding parameter [6] as [BIGINT] - [100]

Conclusion

By implementing Hibernate’s ValueGenerator interface, you can generate the values of basic entity attributes. As I showed you in this article, the implementation of the ValueGenerator is straightforward and usually only requires a few lines of code.

After you implemented the generator, you can assign it to an entity attribute by annotating it with @GeneratorType and referencing your generator implementation.