I am using the JOOQ library in order to fetch the result from a select query into my custom DTO class. Because my Custom DTO class has an ENUM Type field mapped as an Integer Column in my database I am using a custom data type converter. The query I perform is just a basic select query:
public TestSuiteDto findByAuditTestSuiteId(Integer auditTestSuiteId) {
TestSuiteJTable ts = TestSuiteJTable.TEST_SUITE;
AuditTestSuiteJTable ats = AuditTestSuiteJTable.AUDIT_TEST_SUITE;
List<TestSuiteDto> result = dsl.select(
ts.ID,
ts.DESCRIPTION,
ts.PROFILE_TYPE_ID,
ts.CREATED,
ts.ACTIVE,
ts.DEPRECATION,
ts.FEEDBACK_QUESTIONNAIRE_ID)
.from(ts
.join(ats).on(ts.ID.eq(ats.TEST_SUITE_ID)))
.where(ats.ID.eq(auditTestSuiteId))
.fetchInto(TestSuiteDto.class);
//do some stuff before returning
return result.get(0);
}
My custom DTO class looks like this
@Data
public class TestSuiteDto {
private Integer id;
private String description;
private ProfileType profileType;
private LocalDateTime created;
private boolean active;
private LocalDateTime deprecation;
private Integer feedbackQuestionnaireId;
}
The problem is that during the fetching process the SETTER of the DTO class is never triggered for the ENUM type field e.g. profileType even though I have configured a custom data type converter:
@Slf4j
public class ProfileTypeConverter implements Converter<Integer, ProfileType> {
@Override
public ProfileType from(Integer databaseObject) {
log.info("ProfileTypeConverter.from {} -> {}", databaseObject, ProfileType.getFromId(databaseObject));
return ProfileType.getFromId(databaseObject);
}
@Override
public Integer to(ProfileType userObject) {
log.info("ProfileTypeConverter.to");
return userObject.getId();
}
@Override
public Class<Integer> fromType() {
log.info("ProfileTypeConverter.fromType");
return Integer.class;
}
@Override
public Class<ProfileType> toType() {
log.info("ProfileTypeConverter.toType");
return ProfileType.class;
}
}
I have added some logs just to check if the converter is triggered at all and I see that the converter is triggered as expected (from method of the Converter class is called during the execution of the JOOQ SQL query). I have also delombok my DTO class in order to add logs in SETTERs and GETTERs and see if those are also properly called. I found out that all the SETTERs are properly called except for the profileType one. Because of that when I retrieve the DTO from the result list the value of the profileType field is null. The column in my database (mysql) that maps to the profileType ENUM is called PROFILE_TYPE_ID and it is of type Integer. I have also configured a forcedType in the pom.xml following the examples on JOOQ documentation webpage.
<forcedType>
<includeExpression>${jdbc.database}.TEST_SUITE.PROFILE_TYPE_ID</includeExpression>
<userType>mypackage.type.ProfileType</userType>
<converter>mypackage.converter.ProfileTypeConverter</converter>
</forcedType>
and this is how I have configured the ProfileType Field in pom.xml
<field>
<expression>${jdbc.database}.TEST_SUITE.PROFILE_TYPE_ID</expression>
<fieldIdentifier>
<expression>PROFILE_TYPE_ID</expression>
</fieldIdentifier>
<fieldMember>
<expression>profileType</expression>
</fieldMember>
<fieldGetter>
<expression>getProfileType</expression>
</fieldGetter>
<fieldSetter>
<expression>setProfileType</expression>
</fieldSetter>
</field>
JOOQ version: 3.14.16, Java 8
CodePudding user response:
Why do things behave this way?
The reason is that you have a name mismatch:
Query
ts.PROFILE_TYPE_ID,
DTO
private ProfileType profileType;
If you want to rely on the reflection based DefaultRecordMapper
, then you must name those things accordingly, otherwise, they won't be mapped. The fact that you have a converter is irrelevant, if the names don't match. Imagine you had 20 columns of type ProfileType
. You wouldn't want to have DefaultRecordMapper
map values purely based on their type.
Regarding your comments:
I have added some logs just to check if the converter is triggered at all and I see that the converter is triggered as expected (from method of the Converter class is called during the execution of the JOOQ SQL query)
Yes of course. The Converter
belongs to the projected column. The conversion happens before the mapping (i.e. the into(TestSuiteDto.class)
call)
Solutions
There are multiple alternatives to solve this:
- Call your DTO attribute
profileTypeId
, or to add JPA annotations to it to map between SQL names and Java names - Rename your SQL column (in DDL)
- Alias your SQL column using
PROFILE_TYPE_ID.as("profile_type")
- Use a computed column
PROFILE_TYPE
and attach the converter to that, keeping thePROFILE_TYPE_ID
as it is (you can also use client side computed columns for that, in order not to affect your schema) - Use type safe constructor based mapping, rather than reflection based mapping, e.g. using
fetch(Records.mapping(TestSuiteDto::new))
There are probably more possible solutions.