Suppose a `CreditcardNumb.class` with a constructor that checks if the credit-card number is valid. I then create a `CreditcardNumbConverter.class` to convert credit-card numbers to Strings in the database:
public class CreditcardnumbConverter
implements AttributeConverter<CreditcardNumb, String> {
public String convertToDatabaseColumn(CreditcardNumb cn) {
if (cn== null) return null;
return cn.toString();
}
public CreditcardNumb convertToEntityAttribute(String cn) {
if ((cn == null) || cn.trim().isEmpty()) return null;
return new CreditcardNumb(cn);
}
}
This works ok, but now I want to use the Criteria API (the new or the old API, doesn't matter) to search for all credit-card numbers that start with "123":
CreditcardNumb cn = new CreditcardNumb("123");
createCriteria(Wallet.class)
.add(Restrictions.ilike("creditcard", cn, MatchMode.START))
.list();
However, since "123" is not a valid credit-card number, the object instantiation fails. Also, the ilike method doesn't accept CreditcardNumb objects, but only Strings. There are two ways of solving this (I prefer the second): 1) Let us locally bypass or disable the conversion, so that I can do something like:
createCriteria(Wallet.class)
.disable(CreditcardnumbConverter.class))
.add(Restrictions.ilike("creditcard", "123", MatchMode.START))
.list();
2) But, better yet, simply check if the value is already of the database type, and if so, bypass the conversion (or else, check if the value is of the Java type, and only if so, do the conversion). The exact place the Criteria fails (on Hibernate 5.2.7) is at org.hibernate.type.descriptor.converter.AttributeConverterSqlTypeDescriptorAdapter, line 77, where we get this exception:
java.lang.ClassCastException: java.lang.String cannot be cast to CpfOuCnpj.CpfOuCnpj
The failing line is:
convertedValue = converter.convertToDatabaseColumn( value );
This could be changed to something along the lines of:
Class javaClass = intermediateJavaTypeDescriptor.getJavaTypeClass();
Class valueClass = value.getClass();
if (javaClass.isAssignableTo(valueClass()) {
convertedValue = converter.convertToDatabaseColumn(value);
}
else {
convertedValue = value;
}
Note that this tiny modification doesn't break the AttributeConverter specification in any way, and would allow for very powerful queries that are simply not possible in the current implementation. I have a few other use cases that this modification would solve, and can provide them if it helps prove that this modification is very useful. |