I have an enum something like this
public Enum MyEnum {
NEW("NEW"),
OLD("OLD"),
IN_PROCESS("IN PROCESS");
}
The mapping on ibatis works fine for the NEW and OLD, but encounters an error when the IN_PROCESS is encountered since the value of my IN_PROCESS in the DB is IN PROCESS, and the error indicates that ibatis tries to find an enum with that value, can someone suggest a solution?
MyBatis use an EnumTypeHandler to do mappings with enums. In this Enum type handler it uses the name() method of the Enums which returns the string value of the variable name.
For example NEW -> "NEW" and IN_PROCESS -> "IN_PROCESS".
Otherwise, to get the value it uses Enum.valueOf(type, s); which gets the value of the Enum through the String value which corresponds with the variable name ("NEW" -> MyEnum.NEW, "IN_PROCESS" -> MyEnum.IN_PROCESS) and internally is used the method name().
You cannot overwrite name() because is marked as final so you options are:
The easy way is to use IN_PROCESS instead of IN PROCESS. I prefer this, is easier and fast.
The second option is create your TypeHanlder for this enum and check if the parameter is IN PROCESS and search with IN_PROCESS.
I had the same issue, I ended up writing a custom setter on my POJO to convert the string value.
public Enum MyEnum {
NEW("NEW"),
OLD("OLD"),
IN_PROCESS("IN PROCESS");
public static MyEnum fromValue(String v){
.... find the enum based on value
}
}
public class POJO {
private MyEnum myEnum;
public void setMyEnum(String strV){
myEnum=MyEnum.fromValue(strV)
}
}
Related
Let's assume we have an enum, that represents searchable fields:
enum SearchableFields {
ALL,
FIELD1,
FIELD2,
FIELD3;
}
This enum is displayed via a (combobox) selection inside a GUI. At runtime, I want to evaluate the selection of this combobox and search accordingly.
Depending on the selection, I need to retrieve the fields to search from a POJO (example below) via a getter.
class FieldPojo {
private String field1;
private String field2;
private String field3;
...
public String getField1() {
return field1;
}
...
}
I currently use a switch statement to evaluate the selection of SelectableFields and to then retrieve the correct field(s) to search:
private String retrieveField(FieldPojo f) {
switch (selectedField) {
case ALL:
return retrieveAll(); // method that retrieves all available fields
case FIELD1:
return f.getField1();
...
}
This does work, however I feel like it's clunky.
Question:
Is there a more concise way to do this without evaluating the enum via a switch? (Java 8)
You could store a reference to the getter in your enum constants:
enum SearchableFields {
ALL(FieldPojo::retrieveAll),
FIELD1(FieldPojo::getField1)
private final Function<FieldPojo, String> accessor;
SearchableFields(Function<FieldPojo, String> acccessor) {
this.accessor = accessor;
}
public String get(FieldPojo fp) {
return accessor.apply(fp);
}
}
You can create a static map instead of the switch-case.
private static final Map<SearchableFields,Supplier<String>> searchableFieldsToFieldPojo = Map.of(
ALL, this::retrieveAll,
FIELD1, FieldPojo::retrieveAll
);
And then you can access via:
searchableFieldsToFieldPojo.get(selectedField).get();
Given that you can modify all parts of the code, you have several options:
Put the retrieveField into the class FieldPojo and modify it's parameter so it takes the enum SearchableFields as parameter.
Put the fields of FieldPojo as values into a map with a key of type SearchableFields. You can then decide whether you want to have "ALL" as an extra entry of the map or handle it as special case in a method similar to retrieveField. You could use this to have a "default" handling if you want to update the enum but not the FieldPojo class.
You put retrieveField into the class FieldPojo together with the SearchableFields enum - since only FieldPojo knows, which fields it actually provides as searchable fields.
You use introspection to gather the list of possible searchable fields and also access their contents.
Depending on your real requirements (you only showed a very abstract and specific version of them) one or the other method might be "the right one" for you. I would actually prefer the "everything into FieldPojo" as the most robust one, but on the other hand if you are not able to change FieldPojo and have to handle many different classes like it, the introspection variant might be the right one. (Be aware that it is fragile in terms of security and also probably very slow.)
Enums can contain method definitions, so one way is to define the method that retrieves the field name based on the enum value. I assume you have the actual field name stored as a member field also. Then you can override the method for the special ALL value:
enum SearchableFields {
ALL("all") { // all is just a placeholder in this case
#Override
String retrieveField(FieldPojo f) {
// logic for all fields
}
},
FIELD1("field1"),
FIELD2("field2"),
FIELD3("field3");
SearchableFields(String fieldName) {
this.fieldName = Optional.of(fieldName);
}
SearchableFields() {
fieldName = Optional.empty();
}
private final Optional<String> fieldName;
String retrieveField(FieldPojo f) {
if (fieldName.isPresent()) {
return (String) f.getClass().getField(fieldName.get()).get(f);
} else {
// ...
}
}
}
Using JSON-B / Yasson is there any way to ignore case of enums when deserializing?
public class MyObject{
MyEnum condition;
//getters and setters
}
public enum MyEnum{
NEW, OLD, REFURBISHED;
}
part of incoming JSON: "condition" : "new"
The problem is that the incoming JSON uses the enums in lowercase.
I don't thing this should be available out of the box. Because you technically can have both old and OLD as valid values of your enum living together, allowing for out-of-the-box uppercase conversion can break roundtrip equivalence. Think of serializing a MyEnum.old value to end up with a MyEnum.OLD value on deserialization.
You can however force such a behavior by using an adapter.
public static class MyAdapter implements JsonbAdapter<MyEnum, String> {
#Override
public String adaptToJson(MyEnum value) {
return value.name();
}
#Override
public MyEnum adaptFromJson(String s) {
return MyEnum.valueOf(s.toUpperCase());
}
}
Next, annotate the enum with #JsonbTypeAdapter.
#JsonbTypeAdapter(MyAdapter.class)
public enum MyEnum {
NEW,
OLD,
REFURBISHED;
}
Alternatively, you create your Jsonb provider as follows.
Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withAdapters(new MyAdapter()));
I want to ask about nested enums. I am working with old code and i found very strange construction that i not really good understand.
I have this enum :
public enum DbEngines {
ORACLE("oracle", "set define on", "set define off")
, POSTGRESQL("postgresql", "--TODO set define on", "--TODO set define off");
private final String dbEngine;
private String setOn;
private String setOff;
DbEngines(String dbEngine, String setOn, String setOff) {
this.dbEngine = dbEngine;
this.setOn = setOn;
this.setOff = setOff;
}
public String getSetOn() {
return setOn;
}
public String getSetOff() {
return setOff;
}
public String toString() {
return this.dbEngine;
}
}
I added private String to this enum, that are engine specific, so it is good place for me here. The problem is, that in some places in method declaration i see something like that
public someMethod(Enum<DbEngines> engine, ...)
And it worked perfectly without methods, but now, after changing, I couldn't call public getters of this enum. But if i change to :
public someMethod(DbEngines engine, ...)
it works without any problems with all public getters. Maybe someone could explain that?
Enum in Java is the base class for all enumeration types. One can think of it as similar to Object class.
Just like one can hold reference of object of any class using the reference of type Object, one can refer to an enumeration type using the reference of type Enum.
Object o = new Integer(10);
Enum e = DBEngine.ORACLE;
One cannot invoke a method present in inherited class but absent in superclass using the reference of superclass.
Similar explanation over here.
Here's my situation,
I have a class with Enum type fields. I want to execute annotated validation for enum types, similar to annotations for strings, example: #Size, #NotNull etc.
Problem is, json deserializer fails on enum type before validation occurs.
public class myClass {
#JsonProperty
//#SomeCustomValidator -- ??
private EnumType enumValue;
}
public enum EnumType {
A,
B,
C
}
Few things:
I do not want to change the data type to String.
Tried steps in following threads, didn't fix my problem.
Tried this link, but get an error in deserialization before validation hits
Tried this link, but it works only when data Type is String
Validation works after the type of the argument is resolved. So I don't see a way how to use String validating annotations on enums. As workaround you can use #JsonCreator and do some validation before object creation.
public enum EnumType {
A,
B,
C;
#JsonCreator
public static EnumType from(String s) {
// add your logic here, for example
/*
if (Stream.of(values()).map(Enum::name).noneMatch(name -> name.equals(s))) {
throw new MyServiceException("Bad value " + s);
}
*/
return valueOf(s);
}
}
I use grails 1.3.2 and hbase..
I have domain class, one of which fields is in enum type:
class MyDomainClass{
MyEnum enumVal
//....
}
public enum MyEnum {
val1("val1"),
val2("val2")
final String value
MyEnum (String value) {
this.value = value
}
String toString() { value }
String getKey() { name() }
}
<g:form action="create">
<g:select name="enumVal" from="${MyEnum ?.values()}" optionKey="key" />
<g:submitButton name="createOb" value="CreateOb"/>
</g:form>
"create" action have to save selected value in db.
When I submit I get exception:
Cannot cast object 'val1' with class 'java.lang.String' to class 'myPack.MyEnum '
Is there any way to save enum value as a String?
The space after "MyEnum" in GSP and error message makes me doubt, can you remove it from GSP?
You don't need ?, as MyEnum class should always be there.
I believe you don't need optionKey, especially if you have overridden MyEnum.toString().
We write selects from enum this way:
<g:select from="${SomeEnum.values()*.toFriendlyString()}"
keys="${SomeEnum.values()*.name()}"
value="${xxxInstance.field.name()}" ...
/>
where toFriendlyString() is our Enum's method that returns user-readable String representation.
It seems to be a data-type conversion issue. You may try:
def domainObject = new MyDomainClass()
def enumValue = myPack.MyEnum.valueOf(params.enumVal) // This is the conversion.
After that, assign your domain object with the new enumValue.