I have similar problem like this [Hibernate Exception: Unknown name value for enum class
But in my case,
Unable to filter, so returning non filtered results.Unknown name value for enum class com.xxxx.enums.Status: DELIVERED
java.lang.IllegalArgumentException: Unknown name value for enum class com.xxxx.enums.Status: DELIVERED
at org.hibernate.type.EnumType.nullSafeGet(EnumType.java:128)
at org.hibernate.type.CustomType.nullSafeGet(CustomType.java:109)
at org.hibernate.type.AbstractType.hydrate(AbstractType.java:104)
#Enumerated(value = EnumType.STRING)
#Column(name = "status", length = 10)
#AuditableField
private Status status;
public enum ReleaseStatus {
DL("Delivered"),
}
Everything seems fine, still I am getting that exception.
You have the String DELIVERED in your table. And this string is supposed to be the name() of one of the ReleaseStatus instances. And ReleaseStatus doesn't have any instance named DELIVERED. The only one you posted is named DL.
So what should be in the table is DL not DELIVERED. Or you should rename your enum instance to DELIVERED, to match what is stored in the database table.
You could define a custom Hibernate user type and use it for this enum as well, so that when getting "DELIVERED" from the database, Hibernate finds the enum instance constructed with this value (and ignoring the case). But storing the correct value from the start looks like a betteridea to me.
I prefer defining a custom converter like:
#Column
#Convert(converter = StatusFirmaDocumentoConverter.class) <<<<< :)
#AuditableField
private Status status;
(note: do not include the #Enumerated attribute) and creating a converter to process the enumerator value like:
public class CustomConverter implements AttributeConverter<Status, String> {
#Override
public String convertToDatabaseColumn(Status attribute) {
return attribute.getValue() ;
}
#Override
public Status convertToEntityAttribute(String dbData) {
return StatusFirmaDocumento.fromString(dbData);
}
}
yeah, it's a shame that you can't tell to hibernate "translate DL to DELIVERED" and viceversa
Related
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
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 the PROFILE_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.
I am trying to execute Hibernate Filter.
Here is my POJO class:
#Entity
#Table(name="flight")
#FilterDef(name="f1",parameters=#ParamDef(name="status",type="String"))
#Filter(name="f1",condition="status=:p1")
public class Flight
{
#Id
#Column(name="flightno")
private int flightNumber;
#Column(name="src", length=10)
private String source;
#Column(name="dest",length=10)
private String destination;
#Column(name="status",length=10)
private String status;
//setter & getters
}
And here is my Main class code :
public static void main(String[] args)
{
//code for getting SessionFactory Object
Session session=factory.openSession();
Transaction tx=session.beginTransaction();
Query query=session.createQuery("from Flight f");
Filter filter=session.enableFilter("f1");
filter.setParameter("p1","DELAYED");
List list=query.list();
Iterator itr=list.iterator();
while(itr.hasNext())
{
Flight f=(Flight)itr.next();
System.out.println("FLIGHT NO:"+f.getFlightNumber());
System.out.println("SOURCE :"+f.getSource());
System.out.println("DESTINATION :"+f.getDestination());
System.out.println("STATUS :"+f.getStatus());
session.close();
}
But i am the output like this:
Exception in thread "main" java.lang.IllegalArgumentException: Undefined filter parameter [p1]
The error message in this case is somewhat misleading. Hibernate is trying to tell you that the filter parameter is misconfigured.
I ran into this problem when I had a similar mapping with a Long. The issue appears to be with the ParamDef's type definitions. For some reason using the class name in the type parameter doesn't work for Long and String.
It does correctly map the type if you specify it as a "primitive" by using lowercase "long" or "string"
#ParamDef(name="status",type="string")
i think you need also modify p1 to status as u define #ParamDef(name="status",type="String")
#Filter(name="f1",condition="status=:status")
Since upgrading my webapplication from Spring 3.0.5 to 3.1.1 I have to face some serious errors when validating my form-beans. My previously configured validator(s) doesn't work any more like they should. The problem is that the method getFieldValue(String fieldname) from Class org.springframework.validation.Errors does not return the original binded bean value like it should (and was before).
This is what my form-bean looks like:
public class PersonBean extends BaseFormBean {
private String firstname; // getters and setter omitted...
private String lastname; // getters and setter omitted...
private Integer age; // getters and setter omitted...
public PersonBean() {}
#Override
public void validateForm(Errors errors) {
WebValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstname", "validator.requiredvalidator.lbl", "field required");
WebValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastname", "validator.requiredvalidator.lbl", "field required");
WebValidationUtils.rejectInvalidIntValue(errors, "age", "validator.std.age", "invalid age", false);
}
}
The WebValidationUtils-class that gets invoked has some standard methods for checking bean properties. The error occurcs only on non-String values, like the property age which is of type Integer. It also happens on Collection(s).
The following snippet shows how Integer values are validated in my utils-class:
import org.springframework.validation.Errors;
...
public abstract class WebValidationUtils {
...
public static void rejectInvalidIntValue(Errors errors, String field, String errorCode, String defaultMessage){
Assert.notNull(errors, "Errors object must not be null");
Object value = errors.getFieldValue(field); // returns the string value (type: java.lang.String)
Class<?> fieldType = errors.getFieldType(field); // returns the class Integer!
if (value == null || !value.getClass().equals(Integer.class) || ((Integer)value).intValue() <= 0){
errors.rejectValue(field, errorCode, null, defaultMessage);
}
}
}
The bean itself has the correct value bound...
Do I have to configure some additonal spring beans in my context-servlet.xml do achieve the same bevahior like it was in 3.0.5?
Edit: The official Spring Doku for the method "getFieldValue(...)" says
Return the current value of the given field, either the current bean property value or a rejected update from the last binding.
So I don't have a clue why this method returns a String-value instead of the propagated bean value of type Integer...
Seem like you have a binding error so getFieldValue() return FieldError.getFieldValue() that return the value that causes the binding error. This is the expected behavior.
You can get the value that hold the property using getRawFieldValue() instead. This method always return the value using the PropertyAccessor.
In my datamodel a have many entities where attributes are mapped to enumerations like this:
#Enumerated(EnumType.STRING)
private MySpecialEnum enumValue;
MySpecialEnum defines some fixed values. The mapping works fine and if the database holds a NULL-value for a column I get NULL in the enumValue-attribute too.
The problem is, that my backend module (where I have no influence on) uses spaces in CHAR-columns to identify that no value is set. So I get an IllegalArgumentException instead of a NULL-value.
So my question is: Is there a JPA-Event where I can change the value read from the database before mapping to the enum-attribute?
For the write-access there is the #PrePersist where I can change Null-values to spaces. I know there is the #PostLoad-event, but this is handled after mapping.
Btw: I am using OpenJpa shipped within WebSphere Application Server.
You could map the enum-type field as #Transient (it will not be persisted) and map another field directly as String, synchronizing them in #PostLoad:
#Transient
private MyEnum fieldProxy;
private String fieldDB;
#PostLoad
public void postLoad() {
if (" ".equals(fieldDB))
fieldProxy = null;
else
fieldProxy = MyEnum.valueOf(fieldDB);
}
Use get/setFieldProxy() in your Java code.
As for synchronizing the other way, I'd do it in a setter, not in a #PreUpdate, as changes to #Transient fields probably do not mark the entity as modified and the update operation might not be triggered (I'm not sure of this):
public void setFieldProxy(MyEnum value) {
fieldProxy = value;
if (fieldProxy == null)
fieldDB = " ";
else
fieldDB = value.name();
}
OpenJPA offers #Externalizer and #Factory to handle "special" database values.
See this: http://ci.apache.org/projects/openjpa/2.0.x/manual/manual.html#ref_guide_pc_extern_values
You might end up with something like this: not tested...
#Factory("MyClass.mySpecialEnumFactory")
private MySpecialEnum special;
...
public static MySpecialEnum mySpecialEnumFactory(String external) {
if(StringUtils.isBlank(external) return null; // or why not MySpecialEnum.NONE;
return MySpecialEnum.valueOf(external);
}
I have an existing database that I am now connecting to using hibernate. I cannot change the data in it at the moment and have everything working apart from a single column.
I have a status column that has the values:
new
mailed
in
out
And the column is mapped as follows:
#Column(name = "STATUS", nullable = false, length = 50)
#Enumerated(EnumType.STRING)
private TeamMemberStatus status;
I would REALLY like (for application reasons) to have this column mapped as a Java Enum (TeamMemberStatus), but due to the fact that 'new' is a keyword in Java I cannot have that as an enum member.
If I have the enum contstants NEW, MAILED, IN and OUT hibernate fails as inside EnumType it does a Enum.valueOf().
Is there any way for me to map this to my Enum without having to write a complex UserType?
-- added content
My Enum like this:
public enum TeamMemberStatus {
NEW, MAILED, IN, OUT
}
is a valid Java enum, but not matching the case of the database. If I change it to match the database like:
public enum TeamMemberStatus {
new, mailed, in, out
}
It won't compile as 'new' is a Java reserved word.
If you can use a SQL UPPER statement at database, It will work without using any UserType
UPDATE
Well, It can not be The nicest solution but it solves what you want
#Entity
public class WrapperEntity {
private TeamMemberStatus memberStatus;
#Transient
private TeamMemberStatus getMemberStatus() {
return this.memberStatus;
}
public void setMemberStatus(TeamMemberStatus memberStatus) {
this.memberStatus = memberStatus;
}
#Column(name="STATUS", nullable=false, length=50)
public String getMemberStatusAsString() {
return memberStatus.name().toLowerCase();
}
public void setMemberStatusAsString(String memberStatus) {
this.setsetMemberStatus(TeamMemberStatus.valueOf(memberStatus.toUpperCase()));
}
}
If your Database values are "new", "mailed", "in" and "out" then your Enum need exactly the same names. - I believe that the problem is, that your Enums are in capital letters but your data base values not.