-
Notifications
You must be signed in to change notification settings - Fork 2
Automatic Data Type Conversions
When writing data to JDBC, q2o relies on the driver to perform most conversions. q2o only calls Statement.setObject()
internally, and expects that the driver will properly perform conversions. For example, convert an int
or java.lang.Integer
into an INTEGER
column type.
If the @Convert
annotation is present on the field in question, the appropriate user-specified javax.persistence.AttributeConverter
will be called.
For fields where the @Enumerated
annotation is present, q2o will obtain the value to persist by calling ordinal()
on the enum
instance in the case of EnumType.ORDINAL
, and name()
on the enum
instance in the case of EnumType.STRING
.
When reading data from JDBC, q2o relies on the driver to perform most conversions. q2o only calls ResultSet.getObject()
internally, and expects that the driver will properly perform conversions to Java types. For example , for an INTEGER
column type, return a java.lang.Integer
from ResultSet.getObject()
.
However, if the Java object type returned by the driver does not match the type of the mapped member field, q2o permits the following automatic conversions:
Driver getObject() Java Type |
Mapped Member Java type |
---|---|
java.lang.Integer |
boolean (0 == false , everything else true ) |
java.math.BigDecimal |
java.math.BigInteger |
java.math.BigDecimal |
int or java.lang.Integer (via cast) |
java.math.BigDecimal |
long or java.lang.Long (via cast) |
java.util.UUID |
String |
java.sql.Clob |
String |
... |
Many more in 3.16 |
If the @Convert
annotation is present on the field in question, the appropriate user-specified javax.persistence.AttributeConverter
will be called.
For fields where the @Enumerated
annotation is present, q2o will map java.lang.Integer
values from the driver to the correct Enum
value in the case of EnumType.ORDINAL
, and will map java.lang.String
values from the driver to the correct Enum
value in the case of EnumType.STRING
.
Finally, q2o has specific support for the PostgreSQL PGobject
and CITEXT
data types. CITEXT
column values are converted to java.lang.String
. PGobject
"unknown type" column values have their getValue()
method called, and the result is attempted to be set via reflection onto the mapped member field.
In some cases the default conversion from JDBC type to Java type might not cut it. An enum, for example, can be saved to a Types.VARCHAR column, but would not automatically be marshaled back to an enum when retrieved.
In other cases, there may not be a way to set the field from the JDBC type. For example, JSR-310 java.time.* classes cannot be automagically set from the JDBC types. Enums also cannot be set from the JDBC types.
In these cases, you need to help provide conversion hints.
This is accomplished by adding the @Convert annotation to the field and identifying an AttributeConverter class that can handle the conversion.
Let's tackle using a java.time.LocalDate field that will be stored as a Types.DATE column.
First we need an AttributeConverter implementation:
import javax.persistence.AttributeConverter;
import java.sql.Date;
import java.time.LocalDate;
/**
* class LocalDateAttributeConverter: An attribute converter for LocalDate <-> Date conversion.
*/
public class LocalDateAttributeConverter implements AttributeConverter<LocalDate, Date> {
@Override
public Date convertToDatabaseColumn(LocalDate localDate) {
return localDate == null ? null : Date.valueOf(localDate);
}
@Override
public LocalDate convertToEntityAttribute(Date date) {
return date == null ? null : date.toLocalDate();
}
}
This class implements the JPA'a AttributeConverter interface and covers marshaling to/from LocalDate and Date objects. The implementation is pretty simple as the conversion is handled by Java classes themselves.
For our field, we will just need to decorate with the @Convert annotation. Using the Order class, we must add:
import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Id;
import javax.persistence.Table;
...
@Entity
@Table(name = "order")
class Order {
...
@Column(name = "order_dt")
@Convert(converter = LocalDateAttributeConverter.class)
LocalDate ordered;
...
}
Now when the Order class is persisted or retrieved, q2o will be able to translate the Types.DATE column to/from the LocalDate field.
You can apply a similar process to Enums that cannot be handled with integer @Enumerated handling (i.e. persisted as String representations rather than ints), conversion between other types (i.e. a string column parsed to a javax.money.MonetaryAmount, etc.).