Tuesday, September 27, 2022
HomeJavaLearn how to implement an AttributeConverter to help customized sorts

Learn how to implement an AttributeConverter to help customized sorts


All JPA implementations, together with Hibernate, present default mappings for an enormous set of normal Java courses. You can mannequin the attributes of all of your entity courses utilizing these mappings, and you’d be capable to map all columns of your desk mannequin. However that is typically not the perfect match for what you are promoting logic. A mapping to a domain-specific sort or every other Java sort that provides semantics to your entity attribute is commonly extra useful. You can, for instance, map a column of sort varchar to your domain-specific sort Title, which extends the usual class String and provides domain-specific validation. Or you can map a 8-digit hexadecimal colour code to an object of sort java.awt.Colour.

JPA’s AttributeConverter interface offers a straightforward approach to outline such a mapping. You solely must implement the two strategies outlined by the interface and specify for which entity attributes you wish to use the converter. I’ll present you ways to do this on this article.

Supported conversions and limitations

The final idea of the AttributeConverter is easy. The two strategies of the AttributeConverter interface outline 2 conversions. One which converts the sort used on your entity attribute to a sort that will get dealt with by the JDBC driver when inserting or updating a report within the database. And one other one which converts the sort returned by the JDBC driver when studying a report from the database to the sort used as your entity attribute.

Primarily based on this straightforward idea, the capabilities and limitations of an attribute converter develop into apparent.

You should utilize it on all primary attributes mapped to 1 column in your desk mannequin and outlined by entity courses, mapped superclasses, or embeddable courses.

However the converter can’t deal with extra advanced sorts, like a whole ElementCollection, a to-many affiliation, or any attribute you wish to map to a number of database columns. It’s also possible to not use an AttributeConverter on major key attributes or model attributes. The JPA specification defines a selected dealing with for these attributes, which may trigger conflicts. And attributes which are annotated with @Temporal or @Enumerated are additionally not supported. That’s as a result of these annotations already outline a mapping to a database column. That you must resolve if you wish to use the AttributeConverter or the opposite sort mapping and solely add the corresponding annotations.

The listing of conditions in which you’ll’t use an AttributeConverter might sound for much longer than the one by which you need to use it. However don’t fear, the AttributeConverter is extremely helpful and may deal with nearly all commonplace use circumstances.

Implementing an AttributeConverter

Let’s implement an AttributeConverter that converts between an entity attribute of sort java.awt.Colour and a String containing a 6-digit hex worth.

Implementing an AttributeConverter requires a category that implements the javax.persistence.AttributeConverter (JPA 1 & 2) or jakarta.persistence.AttributeConverter (JPA 3) interface. Moreover the bundle identify, these 2 interfaces are similar. As you’ll be able to see within the code snippet, the AttributeConverter interface makes use of generics. These are the kind of the entity attribute and the sort dealt with by the JDBC driver. On this instance, the attribute will probably be of sort Colour and the JDBC driver will deal with a String.

@Converter(autoApply = true)
public class ColorConverter implements AttributeConverter<Colour, String> {

    Logger log = LogManager.getLogger(this.getClass().getName());

    @Override
    public String convertToDatabaseColumn(Colour attribute) {
        String hex = "#"+Integer.toHexString(attribute.getRGB()).substring(0,6);
        log.information("Convert "+attribute+" to "+hex);
        return hex;
    }

    @Override
    public Colour convertToEntityAttribute(String dbData) {
        Colour colour = Colour.decode(dbData);
        log.information("Convert "+dbData+" to "+colour);
        return colour;
    }
}

And also you additionally must annotate your converter class with JPA’s @Converter annotation. The @Converter annotation tells your persistence supplier, e.g., Hibernate, that that is an attribute converter. And you’ll set its autoApply attribute to true if you wish to use this converter for all entity attributes of sort Colour. When you don’t wish to try this, please verify the next part, the place I present you how you can activate the converter for a selected attribute.

The implementation of the AttributeConverter is fairly easy. The interface defines the strategies convertToDatabaseColumn and convertToEntityAttribute. Hibernate and every other JPA implementation name these strategies to both convert the worth of your entity attribute to the sort dealt with by the JDBC driver or vice versa.

Activating an AttributeConverter

You’ll be able to activate an AttributeConverter in 3 methods:

  1. The best one is to set the autoApply attribute of the @Converter annotation to true. Your persistence supplier will then use the converter for all entity attributes of the given sort.
  2. Or you’ll be able to annotate an entity attribute with the javax.persistence.Convert (JPA 1 & 2) or jakarta.persistence.Convert (JPA 3) annotation and reference your AttributeConverter implementation. Your persistence supplier then solely makes use of the converter for that attribute.
    The next code snippet reveals an instance of this strategy:
@Entity
public class Rectangle {

    @Id
    @GeneratedValue
    non-public Integer id;

    non-public Integer x;

    non-public Integer y;

    @Convert(converter = ColorConverter.class)
    non-public Colour colour;

    ...
}
  1. Since Hibernate 6.1, you may also use the @ConverterRegistration annotation to register your attribute converter. Utilizing that annotation allows you to separate the implementation of the AttributeConverter from its registration. This allows you to management the energetic converters in your utility, e.g. when utilizing a library that gives a number of converter implementations.
    Right here you’ll be able to see a package-info.java file that registers the beforehand outlined ColorConverter. When utilizing this annotation, you, after all, now not must set the autoApply attributer of the @Converter annotation to true.
@org.hibernate.annotations.ConverterRegistration(converter=com.thorben.janssen.mannequin.ColorConverter.class, autoApply=true)
bundle com.thorben.janssen.mannequin;

That’s all you could do to implement an AttributeConverter that gives a customized sort mapping.

The converter will get used transparently

After you activate the AttributeConverter for an attribute, your persistence supplier makes use of the converter transparently for all operations that have an effect on that entity attribute. That features all learn and write operations carried out for that entity class and all bind parameters in contrast with that attribute.

You’ll be able to see that within the following instance. It reads a Rectangle entity object with the colour white and modifications its colour to black.

EntityManager em = emf.createEntityManager();
em.getTransaction().start();

Rectangle r = em.createQuery("SELECT r FROM Rectangle r WHERE r.colour = :colour", Rectangle.class)
				.setParameter("colour", Colour.WHITE)
				.getSingleResult();

r.setColor(Colour.BLACK);

em.getTransaction().commit();
em.shut();

I used Hibernate as my JPA implementation for the next log output and activated my beneficial logging configuration for improvement methods. You’ll be able to see the executed SQL statements and the messages written by the AttributeConverter implementation within the log file. 

19:11:37,114 INFO  [com.thorben.janssen.model.ColorConverter] - Convert java.awt.Colour[r=255,g=255,b=255] to #ffffff
19:11:37,170 DEBUG [org.hibernate.SQL] - choose r1_0.id,r1_0.colour,r1_0.x,r1_0.y from Rectangle r1_0 the place r1_0.colour=?
19:11:37,171 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter [1] as [VARCHAR] - [#ffffff]
19:11:37,179 INFO  [com.thorben.janssen.model.ColorConverter] - Convert #ffffff to java.awt.Colour[r=255,g=255,b=255]
19:11:37,181 INFO  [com.thorben.janssen.model.ColorConverter] - Convert java.awt.Colour[r=255,g=255,b=255] to #ffffff
19:11:37,181 INFO  [com.thorben.janssen.model.ColorConverter] - Convert #ffffff to java.awt.Colour[r=255,g=255,b=255]
19:11:37,184 DEBUG [org.hibernate.stat.internal.StatisticsImpl] - HHH000117: HQL: SELECT r FROM Rectangle r WHERE r.colour = :colour, time: 39ms, rows: 1
19:11:37,192 DEBUG [org.hibernate.SQL] - replace Rectangle set colour=?, x=?, y=? the place id=?
19:11:37,193 INFO  [com.thorben.janssen.model.ColorConverter] - Convert java.awt.Colour[r=0,g=0,b=0] to #ff0000
19:11:37,193 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter [1] as [VARCHAR] - [#ff0000]
19:11:37,193 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter [2] as [INTEGER] - [10]
19:11:37,193 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter [3] as [INTEGER] - [20]
19:11:37,193 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter [4] as [INTEGER] - [1]
19:11:37,196 INFO  [com.thorben.janssen.model.ColorConverter] - Convert java.awt.Colour[r=0,g=0,b=0] to #ff0000
19:11:37,196 INFO  [com.thorben.janssen.model.ColorConverter] - Convert #ff0000 to java.awt.Colour[r=255,g=0,b=0]
19:11:37,203 INFO  [org.hibernate.engine.internal.StatisticalLoggingSessionEventListener] - Session Metrics {
    31200 nanoseconds spent buying 1 JDBC connections;
    26100 nanoseconds spent releasing 1 JDBC connections;
    191100 nanoseconds spent getting ready 2 JDBC statements;
    4859600 nanoseconds spent executing 2 JDBC statements;
    0 nanoseconds spent executing 0 JDBC batches;
    0 nanoseconds spent performing 0 L2C places;
    0 nanoseconds spent performing 0 L2C hits;
    0 nanoseconds spent performing 0 L2C misses;
    13747100 nanoseconds spent executing 1 flushes (flushing a complete of 1 entities and 0 collections);
    770600 nanoseconds spent executing 1 partial-flushes (flushing a complete of 0 entities and 0 collections)
}

Conclusion

An AttributeConverter offers a straightforward and moveable approach to outline a customized sort mapping. You should utilize it for all primary attributes you wish to map to 1 database column. On this article, I used that to to persist an entity attribute of sort java.awt.Colour as a 6-digit hex code. However that’s, after all, not the one form of mapping you’ll be able to implement. I used it in different articles to enhance Hibernate’s commonplace enum mapping and to map LocalDate and LocalDateTime in older Hibernate variations that didn’t help these sorts.

As you noticed on this article, implementing an AttributeConverter is easy. You solely must implement the AttributeConverter interface with its 2 conversion strategies and annotate that class with a @Converter annotation. When you set the autoApply attribute of that annotation to true, your persistence supplier will use the converter for all entity attributes of the supported sort. When you don’t set that attribute or set it to false, you could annotate every entity attribute on which you wish to use the converter with @Convert and reference your converter implementation.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments