Using @ColumnTransformer In Hibernate

Overview

One of the lesser-known yet powerful annotations in Hibernate is @ColumnTransformer. This annotation let us apply SQL transformations on entity properties when interacting with the database. You can think of this annotation as a two way converter.

A real life use case

Let’s consider a weather app that initially use Kelvin scale to store the temperature. Later, it needs to display Celsius instead. That means when reading the value from database, we need to subtract the value to 273.15. In the opposite direction, when writing to database, we need to add the value to 273.15.

Hibernate provides a elegant solution for this use case by introduction @ColumnTransformer.

@Entity
public class WeatherData {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String location;
    
    @Column(name = "temperature_kelvin")
    @ColumnTransformer(
        read = "temperature_kelvin - 273.15",
        write = "? + 273.15"
    )
    private double temperatureCelsius; // Transformed property

    // Constructors, getters, setters...
}

Now we can observe the power of @ColumnTransformer in action:

public class WeatherDemo {

    private static final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
            .configure().build();
    private static final SessionFactory factory = new MetadataSources(registry).buildMetadata().buildSessionFactory();


    public static void main(String[] args) {

        //insert some weather data
        insertWeatherData("Hanoi", 30);
        insertWeatherData("New York", 20);
        insertWeatherData("London", 32);

        //get weather data by location
        var hanoi = findByLocation("Hanoi");
        System.out.println("Hanoi temperature: " + hanoi.getTemperatureCelsius());
    }

    public static WeatherData findByLocation(String location) {
        try (Session session = factory.openSession()) {
            return session.createQuery("from WeatherData where location = :location", WeatherData.class)
                    .setParameter("location", location)
                    .getSingleResult();
        }
    }

    private static void insertWeatherData(String location, int temperature) {

        var weatherData = new WeatherData();
        weatherData.setLocation(location);
        weatherData.setTemperatureCelsius(temperature);
        try (Session session = factory.openSession()) {
            Transaction tx = session.beginTransaction();
            session.persist(weatherData);
            tx.commit();
        }
    }
}

In this code snippet, I’ve inserted the data for three cities and try to read the data back.

ColumnTransformer in action

As you can see, I was able to write the data to and read as Celsius. However, in the database, the values are actually Kelvin:

Data stored in the database as Kelvin values

Other Potential Use Cases of @ColumnTransformer

Let’s consider some other potential use case

Enum Conversions

Imagine an entity class representing an Order with a status property stored as an enum in the database. You can use @ColumnTransformer to convert the enum to its corresponding database representation (e.g., as an integer or string) and vice versa.

JSON Data Transformation

For entities storing JSON data in the database, you can use @ColumnTransformer to handle conversions between JSON and string representations automatically.

Date and Time Zone Conversion

To handle date and time zone conversions, you can use @ColumnTransformer to adjust date values during database interactions.

Consider this example:

    @CreationTimestamp
    @Column(name = "created_at")
    @ColumnTransformer(
            read = "CONVERT_TZ(created_at, 'UTC', 'America/New_York')",
            write = "CONVERT_TZ(?, 'America/New_York', 'UTC')"
    )
    private java.sql.Timestamp createdAt;// Transformed property (Adjusted for time zone)

In this example,I used the “CONVERT_TZ” to convert a temporal value when reading from and writing to database. Note that CONVERT_TZ is database specific function (MySQL/MariaDB). This may break your application if you change the underlying database.

Conclusion

The @ColumnTransformer annotation in Hibernate is a powerful tool that let you customize data transformations and calculations without altering the underlying database schema.

Leave a Comment