[EN] Enumset in Der Datenbank

Julian | Nov 16, 2024 min read

Motivation

Even in this day and age, when storage is comparatively cheap, it can still make sense to have it not to be wasted. We were looking for a comparatively cheap alternative within our project, to give a data set additional information that we display within the frontend, i.e. we have to these are not in the database stored legibly for people. However, since a data set can have several additional pieces of information, it is simple Enum is not the method of choice here. This is how we came across the EnumSet and how we implemented the whole thing You can find out how you can implement it in the following article.

Digression Enum

Enums (short for “enumerations”) are a special class in programming languages such as Java that are a group defined by constant values. Enums help maintain a collection of related constants such as days of the week, colors or to manage status values in a more secure and readable way. They ensure type-safe designs and improve them Code quality by limiting it to predefined values.

Example in Java:

public enum Day {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

In this case, the enum Day represents the seven days of the week. Enums improve readability and maintainability of the code by providing a clean and type-safe way to work with predefined constant values.

Some more theory

In the example implementation we use a Byte as the basis for our EnumSet. You can do the whole thing too Implement Short or Integer, this depends on how many versions of the enum you need. There a byte can have a maximum of 8 versions.

To give us a better idea of ​​the whole thing, I’ll try to show it in the following sketch:

Image that illustrates an EnumSet

The implementation

To save an EnumSet as a BitSet in a JPA Entity using a JPA Converter, you can use the approach from use the example mentioned. The process includes creating an AttributeConverter and converting it EnumSet to a numeric format. Here is a complete example showing how to do this:

Step 1: Define Enum

public enum DayOfWeek {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

Step 2: Create EnumSetConverter

You create the converter that converts between EnumSet and a numeric representation:

import javax.persistence.AttributeConverter;
import java.util.BitSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public abstract class EnumSetConverter<E extends Enum<E>, N extends Number> implements AttributeConverter<Set<E>, N> {
    private final E[] values;

    public EnumSetConverter(Class<E> enumType) {
        values = enumType.getEnumConstants();
    }

    @Override
    public Set<E> convertToEntityAttribute(N dbData) {
        if (dbData == null) {
            return null;
        }
        BitSet bitSet = map(dbData);
        return bitSet.stream().mapToObj(i -> values[i]).collect(Collectors.toSet());
    }

    @Override
    public N convertToDatabaseColumn(Set<E> attribute) {
        if (attribute == null) {
            return null;
        }
        BitSet bitSet = attribute.stream().collect(Collectors.toCollection(
                () -> {
                    BitSet bs = new BitSet();
                    attribute.forEach(e -> bs.set(e.ordinal()));
                    return bs;
                }));
        return map(bitSet);
    }

    protected abstract N map(BitSet bitSet);

    protected abstract BitSet map(N value);
}

Step 3: Implement ToByteConverter

Since our Enum only has seven values, a Byte is sufficient as a numeric type:

import java.time.DayOfWeek;
import java.util.BitSet;

public class DayOfWeekConverter extends EnumSetConverter<DayOfWeek, Byte> {
    public DayOfWeekConverter() {
        super(DayOfWeek.class);
    }

    @Override
    protected Byte map(BitSet bitSet) {
        return (byte) bitSet.toLongArray()[0];
    }

    @Override
    protected BitSet map(Byte value) {
        return BitSet.valueOf(new long[]{value});
    }
}

Step 4: Define JPA Entity

Define the JPA entity and use the converter:

import javax.persistence.*;
import java.util.EnumSet;
import java.util.Set;

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

    @Convert(converter = DayOfWeekConverter.class)
    private Set<DayOfWeek> recurringDays = EnumSet.noneOf(DayOfWeek.class);

    // Standard-Konstruktor
    public Alarm() {
    }

    // Getter und Setter
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Set<DayOfWeek> getRecurringDaysDays() {
        return recurringDays;
    }

    public void setRecurringDaysDays(Set<DayOfWeek> days) {
        this.recurringDays = days;
    }
}

Explanation:

  • EnumSetConverter: An abstract class that provides the general conversion operation.
  • DayOfWeekConverter: A specific implementation of the converter for the DayOfWeek enum, which has a BitSet in converts a byte and vice versa.
  • @Convert: The converter is applied to the JPA Entity to convert the EnumSet in the database as Byte save.

With this setup you can store EnumSet as BitSet in the database, which saves disk space as well improves efficiency.