I am using Hibernate custom user type to map enum to a table varchar/char. I am using custom user type code from here (https://community.jboss.org/wiki/Java5StringValuedEnumUserType). I am trying following annotation to do the mapping but its not working.
#Transient
#Type(type = "data.model.base.StringValuedEnumType", parameters = {
#Parameter(name = "enumClass", value = "data.common.TypeEnum"),
#Parameter(name = "identifierMethod", value = "dbCode") })
private TypeEnum typeEnum;
TypeEnum code:
public enum TypeEnum implements StringValuedEnum {
OFF("OFF", "O"),
ON("ON, "O"),
private String dbCode;
private String desc;
TypeEnum(String desc, String dbCode) {
this.desc=desc;
this.dbCode = dbCode;
}
#Override
public String dbCode() {
return dbCode;
}
public String desc() {
return desc;
}
}
I believe I am doing something wrong in the annotation but I am not able to figure out what is it. Any idea anyone?
I have found it. I updated the annotation by removing #Transient and adding in a #Column for the mapping. I also updated the code to take care of the passed name and removed defaultValue.
#Column(name = "TYP_CD", length = 1)
#Type(type = "data.model.base.StringValuedEnumType", parameters = {
#Parameter(name = "enumClass", value = "data.common.TypeEnum")})
private TypeEnum typeEnum;
Related
I have an entity like so:
#Entity
#Table(name = "MyTable", schema = "test")
#Getter #Setter
public class PurgeSystemsEntity {
#Id
#Column(name = "id", nullable = false)
private int id;
#Column(name = "system_name", nullable = false, length = 255)
private String systemName;
.
.
}
How do I validate that the string obtained from DB (like when doing a .findAll()) in systemName field is one of the possible options defined in the Enum System :
public static enum System {
PROD, DEV, QA;
}
So, If a row is fetched with systemName value being 'STAGING', it should throw an exception immediately.
Is there some elegant way to do this?
Set the field type to the enum.
#Column(name = "system_name", nullable = false, length = 255)
#Enumerated(EnumType.STRING)
private System systemName;
This will cause an error if you encounter a value not defined in the enum.
You also have to set EnumType.STRING explicitly, as it defaults to EnumType.ORDINAL which would correspond to the enum ordinal value instead of the name
You can write your own method in ENUM
Something like this
public static MyEnum fromValue(String value) {
for (MyEnum b : MyEnum.values()) {
if (b.value.equals(value)) {
return b;
}
}
throw new IllegalArgumentException("Unexpected value '" + value + "'");
}
Alternatively, if your Enum is part of your class, I think , Java should automatically handle and throw IllegalArgumentException if the value cannot be mapped !
So, I have a Model defined where I want to have a particular variable as ENUM.
Now I have defined it in the model like this.
#Type(
type = "array",
parameters = { #Parameter(name = ListArrayType.SQL_ARRAY_TYPE, value = "member_role") }
)
#Column(name = "access_roles", columnDefinition = "member_role[]")
#Enumerated(EnumType.STRING)
private List<ProjectMemberRole> accessRoles;
The Enum is
#Getter
#AllArgsConstructor(access = AccessLevel.PRIVATE)
public enum ProjectMemberRole {
LEAD("lead", 4),
COLLABORATOR("collaborator", 3),
PARTICIPANT("participant", 2),
VIEWER("viewer", 1);
private final String value;
private final Integer level;
#JsonCreator(mode = JsonCreator.Mode.DELEGATING)
public static ProjectMemberRole forString(String value) {
return stream(ProjectMemberRole.values())
.filter(v -> v.value.equals(value))
.findFirst()
.orElse(null);
}
#Override
#JsonValue
public String toString() {
return this.value;
}
}
Even. though I am able to create, when I fetch I get this error
"No enum constant project.model.ProjectMemberRole.collaborator; nested exception is java.lang.IllegalArgumentException: No enum constant project.model.ProjectMemberRole.collaborator"
So, it seems its getting serialized but not getting de-serialized. What should I be doing in this situation?
EDIT:
I was checking if we have a single element instead of an Array.
If we have a single value then it goes with ENUM Name COLLABORATOR but if we send it as List then it becomes collaborator
So for some reason it is saving JSON value for enum.
#Column(name = "access_roles")
#Enumerated(EnumType.STRING)
private ProjectMemberRole[] accessRoles;
#Column(name = "access_role", columnDefinition = "text")
#Enumerated(EnumType.STRING)
private ProjectMemberRole accessRole;
enums are usually expected to be upper-case during deserialization. You can enable it for Jackson using
objectMapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS);
when you configure your ObjectMapper.
I have a field named 'value' which is of type MEDIUMTEXT in the MySQL db. When I try to persist or fetch the model, it shows
Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction
Model
#Entity
#Table(name = "xyz_something")
#JsonIgnoreProperties(ignoreUnknown = true)
public class Xyz {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#NotBlank(message = "key is mandatory")
#Column(columnDefinition = "CHAR")
#Type(type = "org.hibernate.type.CharArrayType")
private char[] key;
#Column(columnDefinition = "MEDIUMTEXT")
#Type(type = "org.hibernate.type.TextType")
private String value;
// Getters and Setters
Repository
public interface XyzRepository extends JpaRepository<Xyz, Integer> {
}
Controller
#RestController()
#RequestMapping("api/v1")
public class XyzController {
private static Logger logger = LogManager.getLogger();
#Autowired
XyzRepository xyzRepository;
#PutMapping("/xyz")
public HttpStatus insertValue(#RequestHeader(value="id") int id, #NotBlank #RequestBody String value) {
return upsert(value,id);
return HttpStatus.BAD_REQUEST;
}
private HttpStatus upsert(String value, int id) {
return xyzRepository.findById(id)
.map(xyz -> {
xyz
.setKey("key")
.setValue(value);
xyzRepository.save(xyz);
return HttpStatus.CREATED;
}).orElseGet(() -> {
Xyz xyz = new Xyz();
xyz
.setId(id)
.setKey("key")
.setValue(value)
xyzRepository.save(xyz);
return HttpStatus.CREATED;
});
}
}
If I comment out the 'setValue(value)' line, it works, else I get an error mentioned above. I have tried using #Lob with columnDefinition = "MEDIUMTEXT". Also, I have tried putting length in the #Column, that doesn't work as well. What is it that I am doing wrong? Thanks in advance.
It is because you are using a reserved keyword of MySQL i.e. "key". You need to map it using #Column(name = "\"key\"") above your declaration of the field 'key'. You can refer here for more information about reserved keywords.
I like to store a object like:
#Table(value = "my_table")
public class MyTableDto {
#PrimaryKeyColumn(name = "uid", type = PrimaryKeyType.PARTITIONED)
#CassandraType(type = DataType.Name.UUID)
private UUID uid;
#Column(value = "child_ids")
private List<ChildIdDto> childIds;
}
Then I get the exception:
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: Only primitive types are allowed inside Collections for property [childIds] of type ['interface java.util.List'] in entity [de.myapplication.repository.dto.MyTableDto]
I do understand the exception, but is there another way to persist custom objects?
EDIT:
When I comment out this attribute, everything works
! Never say never, I got the solution.
To give a good example, I will list all according classes.
ParentClass.java
#Table(value = "my_table") //OPT
public class MyTableDto {
#PrimaryKeyColumn(name = "uid", type = PrimaryKeyType.PARTITIONED)
#CassandraType(type = DataType.Name.UUID)
private UUID uid;
#Column(value = "child_ids") //OPT
private List<ChildDto> childIds;
}
ChildDto.java
#UserDefinedType // THE SOLUTION
public class ChildDto {
#Column(value = "child") //OPT
#CassandraType(type = DataType.Name.TEXT) //OPT
private String groupId;
#Column(value = "description") //OPT
#CassandraType(type = Name.TEXT) //OPT
private String description;
}
The #UserDefinedType is the solution.
For more information see here.
NOTE: Each annotation with "OPT" is NOT required
I have the following Entity containing a field of Enum type:
#Entity
#Table(name = "INPUT_DATA")
public class InputDataEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name = "INPUT_DATA_SEQ", allocationSize = 1, sequenceName = "INPUT_DATA_SEQ")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "INPUT_DATA_SEQ")
private Long id;
#Column(name = "FIELD1", nullable = false)
private String field1;
#Column(name = "FIELD2", nullable = false)
#Convert(converter = Type.Converter.class)
private Type field2;
// getters and setters
}
The Enum type looks like:
public enum Type {
ENUM_ITEM_1("item1"),
// more items
ENUM_ITEM_N("itemN");
private String code;
private Type(String code) {
this.code = code;
}
public static Type fromString(String name) {
switch (name) {
case "item1":
return ENUM_ITEM_1;
// more cases
case "itemN":
return ENUM_ITEM_N;
default:
throw new IllegalArgumentException("Wrong value for Type");
}
}
#Override
public String toString() {
return code;
}
#javax.persistence.Converter
public static class Converter implements AttributeConverter<Type, String> {
#Override
public String convertToDatabaseColumn(Type attribute) {
return attribute.toString();
}
#Override
public Type convertToEntityAttribute(String s) {
return Type.fromString(s);
}
}
}
The problem is that hibernate doesn't recognize my Converter when I want to fetch data from the database.
I've also tried:
#Embedded and #Embeddable but with no luck.
#Enumerated(EnumType.STRING) but again with no luck.
My question is:
how to make hibernate to recognize my converter when converting the appropriate field?
Many thanks in advance.
I eventually ended up by implementing a StringValuedEnum interface and its relevant reflector and type class by implementing EnhancedUserType, ParameterizedType as it was described here.
This helped me to properly store into and retrieve from DB data corresponding to user defined enum types, although the questions with converters remains still open. If someday a proper answer will be given, that will be very appreciated.