DBMS Array type usage in Entity Views

Example of how @MappingSingular enables the use of array types with Blaze-Persistence Entity Views

By Christian Beikov on 19 December 2018

Introduction

The previous post took a little detour to showcase the filtering and sorting mechanism of Blaze-Persistence Entity Views, but this time we will take a look at mapping a DBMS array as collection in the entity model and also as collection in the entity view.

The use case for this example is that you want to be able to label/tag an object but want to avoid the join to a separate table because of performance reasons. Ideally, you would use an array type in the DBMS, but if the DBMS doesn’t offer such a type, you encode the array as string. Encoding it as string has a few problems though. You need to implement dedicated functions for operating on such values and have to choose a separator character that is not contained in values.

We have plans to provide abstractions that work across different array type implementations, but right now, it is necessary to implement custom converters or Hibernate type implementations for a specific format/DBMS.

In the JPA metamodel, such array attributes are represented as singular attributes, but in order for Blaze-Persistence Entity-Views to treat such attributes as singular, one must use the @MappingSingular annotation.

The source code for the following examples can be found on GitHub, so you can play around with it.

Entity model

This time, we use a simplified post model that doesn’t have comments, but just labels.

@Entity
public class Post {

    @Id
    @GeneratedValue
    Integer id;
    @Temporal(TemporalType.DATE)
    Date publishDate;
    @Convert(converter = LabelConverter.class)
    List<String> labels = new ArrayList<>();
    String title;
    String text;
    @ManyToOne(fetch = LAZY, optional = false)
    User poster;
}

@Entity
public class User {

    @Id
    @GeneratedValue
    Integer id;
    String userName;
    String email;
    @Temporal(TemporalType.DATE)
    Date registrationDate;
}

The converter simply splits and joins based on a separator

public class LabelConverter implements AttributeConverter<List<String>, String> {

    @Override
    public String convertToDatabaseColumn(List<String> attribute) {
        return String.join(",", attribute);
    }

    @Override
    public List<String> convertToEntityAttribute(String dbData) {
        if (dbData == null) {
            return new ArrayList<>();
        }
        return Pattern.compile(",").splitAsStream(dbData).collect(Collectors.toList());
    }
}

Entity View model

The entity view for this will simply have to annotate the labels attribute as singular by annotating @MappingSingular.

@EntityView(Post.class)
public interface NormalPostView {
    @IdMapping
    Integer getId();

    String getTitle();

    @Mapping("SUBSTRING(text, 1, 100)")
    String getText();

    @MappingSingular
    List<String> getLabels();
}

That’s it! Now you can make use of the labels in entity views as well!

Conclusion

With a little more sophisticated type checking we could probably avoid the need for a @MappingSingular annotation, nevertheless the annotation is currently required to map such attributes.

The upcoming article will be about using @CollectionMapping to customize the way collections are mapped in entity views.

Stay tuned!

blaze-persistence jpa entity-view mapping-singular array postgresql
comments powered by Disqus