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!