Cannot serialize mongo integer data to enum in object document - java

I'm trying to retrieve documents from Mongo DB. The documents received well when the Gender attribute is not included. But when it's included an error occurs.
Here is my function to retrieve the person documents from Mongo
private List<Person> getPersons()
{
// Query to get person list
// Loop through person list
for (Person person : personList) {
// Some condition checking
filteredPersonList.add(person);
}
return filteredPersonList;
}
Here is my Person class
public class Person {
#BsonProperty("firstName")
#JSONField("firstName")
public String FirstName
#BsonProperty("lastName")
#JSONField("lastName")
public String LastName
#BsonProperty("gender")
#JSONField("gender")
public Gender Gender
#BsonProperty("isMarried")
#JSONField("isMarried")
public Boolean IsMarried
}
Here is the Gender enum for the Gender property
#JSONType(serializeEnumAsJavaBean = true)
public enum Gender {
Male(0),
Female(1),
;
private final int type;
Gender (int type) {
this.type = type;
}
public int getType() {
return type;
}
}
This is the error I get
org.bson.codecs.configuration.CodecConfigurationException: Failed to decode 'Person'. Decoding 'gender' errored with: readString can only be called when CurrentBSONType is STRING, not when CurrentBSONType is INT32.A custom Codec or PojoCodec may need to be explicitly configured and registered to handle this type.
at org.bson.codecs.pojo.PojoCodecImpl.decodePropertyModel(PojoCodecImpl.java:224)
at org.bson.codecs.pojo.PojoCodecImpl.decodeProperties(PojoCodecImpl.java:197)
at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:121)
at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:125)
at org.bson.codecs.pojo.LazyPojoCodec.decode(LazyPojoCodec.java:57)
at org.bson.codecs.DecoderContext.decodeWithChildContext(DecoderContext.java:96)
at org.bson.codecs.pojo.PojoCodecImpl.decodePropertyModel(PojoCodecImpl.java:218)
... 58 more
In Mongo DB, the type of the gender property is INT32.

You have to add custom codec following your mongodb implementation.
See this
http://mongodb.github.io/mongo-java-driver/4.3/bson/codecs/

Related

Is it possible to map a JPA/Hibernate Entity field to a field in another class without #Embeddable?

I have a very simple Entity (Person.java) that I am wanting to persist via JPA/Hibernate.
The Entity contains two fields: ID and Identification String.
The ID is a simple Integer, and is no problem. The Identification String is currently a String, but for various reasons, I want to instead use a wrapper class for String (IDString), where there are various validation methods among other things.
I am wondering how I can get JPA/Hibernate to use the wrapped string (inside the custom class IDString) when persisting the Person table in the database. I know this can probably be solved by letting the IDString be #Embeddable and then embed IDString in the Person entity with #Embedded, but I am looking for another method, mostly because IDString is in an entirely different package, and I am reluctant to have to go there and change stuff.
Googling, I found https://www.baeldung.com/hibernate-custom-types, but it seems to be mostly about more complicated cases, where you want to convert one class into another type, and I do feel that there is probably a smarter way that I am simply overlooking.
Here is the entity (in theory)
#Entity(name="Person")
#Table(name="DB_TABLE_PERSON")
public class Person implements Serializable {
#Id
Integer id;
// WHAT SHOULD I PUT HERE? I WANT TO SIMPLY USE THE STRING INSIDE IDSTRING AS THE FIELD TO PERSIST
IDString idString;
// getter and setter for ID.
public void getIdString() {
return idString.getValue();
}
public void setIdString(String in) {
idString.setValue(in);
}
}
And here is the class IDString (in theory):
public class IDString {
// I really want to be a POJO
private final String the_string;
public IdString(String input) {
if (isValid(input)) {
the_string = input;
} else {
throw new SomeCoolException("Invalid format of the ID String");
}
public boolean isValid(String input) {
// bunch of code to validate the input string
}
public String getValue() {
return the_string;
}
public void setValue(String input) {
if (isValid(input)) the_string = s;
else throw new SomeCoolException("Invalid format of the ID String");
}
I know that I could place the validation if the IDString inside the Entity, but the IDString will be used elsewhere (it's a general custom class), so I don't want to do that. Is there a simple way?
#Converter(autoApply=true) // autoApply is reasonable, if not use #Converter on field
public class IDStringConverter implements AttributeConverter<IDString,String> {
#Override
public String convertToDatabaseColumn(IDString attribute) {
return attribute != null ? attribute.getValue() : null;
}
#Override
public IDString convertToEntityAttribute(String dbData) {
return dbData != null ? new IDString(dbData) : null;
}
}
With this you should not need any other modifications in your code. One limitation of the AttributeConverter is that it maps from exactly 1 Java field to exactly 1 DB column. If you wanted to map to more columns (not the case here), you would need embeddables.
You could also put a #Column annotation on the getter:
#Entity
public class Person {
private final IdString idString = new IdString();
#Column(name = "ID_STRiNG")
public IdString getIdString() {
return idString.getValue();
}
public void setIdString(String input) {
idString.setValue(input);
}
Another solution could be to convert to/from IdString using #PostLoad and #PrePersit event handlers:
#Entity
public class Person {
#Column(name = "ID_STRiNG")
private String the_string; // no getters & setters
#Transient
private final IdString idString = new IdString();
#PostLoad
public void postLoad() {
idString.setValue(the_string);
}
#PrePersist
public void prePersist() {
the_string = idString.getValue();
}
// getters & setters for idString

JPA retrieve enum with white space

I have a spring boot application and I have defined some POJO classess to specify the data model. In a table I want to store enum columns but I have an enum data type and some of the enums contains spaces.
enum DataEnum {
Workload("Workload"),
ReleaseContent("Release content"),
"VerificationProc"("Verification proc")
private String name = "";
DataEnum (final String name) {
this.name = name;
}
public String getName() {
return name;
}
public DataEnum fromString(String value) {
return DataEnum.valueOf(value);
}
#Override
public String toString() {
return name;
}
}
I have a POJO entity class as below:
#Entity
#IdClass(FieldId.class)
public class Field {
#Id
private String id;
#Id
#Enumerated(EnumType.STRING)
private DataEnum sheet;
...
}
When I try to retrieve data from the database I get:
java.lang.IllegalArgumentException: Unknown name value [Release
content] for enum class [data.util.DataEnum]
JPA cannot map your string to enum because it cannot find any enum with that name exists.
Internally, the #Enumerated use the method Enum.valueOf to convert the DB String to enum constant. The DB String required to be exactly the same to the enum constant identifier.
That is, if you want to store DataEnum.ReleaseContent, you have to store it as ReleaseContent not Release content.
In your case, if you want to store value different than your enum identifier, you can declare a custom converter:
#Converter
public class DataEnumConverter implements AttributeConverter<DataEnum, String> {
#Override
public String convertToDatabaseColumn(DataEnum enum) {
// Convert your enum to DB value
}
#Override
public DataEnum convertToEntityAttribute(String dbValue) {
// Convert String to your enum
}
}
You can see My Answer for an example how to convert String to enum
And in your entity:
#Column
#Convert(converter = DataEnumConverter.class)
private DataEnum sheet;

How to have a custom type behave as a String with QueryDSL

I am trying to use a custom type with QueryDSL, something like this:
public class Email implements Comparable<Email> {
public String name;
public String domain;
#Override
public int compareTo(Email o) {
return toString().compareTo(o.toString());
}
#Override
public String toString() {
return name + "#" + domain;
}
}
I then have a QueryDSL entity that uses this custom type:
#Entity
public class Record {
public Email email;
public String text;
}
I can then build Queries like this:
BooleanExpression withEmail(Email email)
{
QRecord Q = QRecord.record;
BooleanExpression pred = Q.email.eq(email);
return pred;
}
However, when using MongodbSerializer, the query ends up setting the Email type in a mongo DBObject, and later I get an exception from the mongo driver saying that Email cannot be serialized.
java.lang.RuntimeException: json can't serialize type : class Email
If I annotated by email field in my Record like this:
#Entity
public class Record {
#QueryType(PropertyType.STRING)
public Email email;
public String text;
}
Then it starts working - although I have to manipulate the email with a email as strings in my query constructions instead of the email objects themselves, ex. QRecord.record.email.eq(myEmail.toString()) instead of simply QRecord.record.email.eq(myEmail) like before.
My issue with this solution is that anyone using the Email object has to know that they should annotate it when embedding it inside another object.
Question: is there a way to annotate the Email class in such a way that it will always be serialized as a string entity - or is there a better way to achieve this altogether?

Object to string delimited format

I have set of objects of different types.
Ex : Employee emp, adress adr
These two classes have list of properties
public class Employee{
private Stringname;
private int age;
}
public class Adress {
private String HouseNo;
private string Street;
private string pin;
}
Each attribute is assigned with some 2 character value
Name (NA), age (AG), HouseNo(HN),Street(ST), pin(PN)
I need to construct a string with these data and delimit with a %
Output:
NA%Vidhya%AG%30%HN%80%ST%1st cross%PN%100100
Each class knows it own data best so I would let each class be responsible for generating the string. As I understand it the two char codes for each field are unique for each class and member and only used when generating the string so only the class would need them.
interface AttributeDescription {
String generateDescription();
}
public class Employee implements AttributeDescription {
//members...
public String generateDescription() {
return String.format(“NA%%%s%%AG%%%d”, name, age)
}
Then simply call this method for all objects implementing the interface.
AttributeDescription object = ...
String attr = object.generateDescription();
I don't think it can be generalized more than this given the requirements.
Update
It might be better to have a builder class for building the string to get a more unified behavior between classes. Here is an example
public class AttributeBuilder {
private builder = new StringBuilder();
public String getAttribute() {
return builder.toString();
}
public void add(String code, String value) {
if (value == null) {
return;
}
builder.append(code);
builder.append(‘%’);
builder.append(value);
builder.append(‘%’);
}
}
And then you would also have to implement add(...) methods for other data types in a similar fashion. The builder could then be used like
public String generateDescription() {
AttributeBuilder builder = new AttributeBuilder();
builder.add(“NA”, name);
builder.add(“AG”, age);
return builder.getAttribute();
}

Return an int value to Database

I am working on an application which makes it easier for employees to return lost luggage.
I'm working on a screen which registers a piece of luggage.
The status ComboBox is filled with data from the database, as well as the CustomerID combobox.
SCREEN:
http://gyazo.com/d81c7c2377c79eb2b42d6f101fb8d5f5
Now the ComboBox fetches an SQL query, and shows a firstname. But the customer has a customerID as well. For now i have a method which sets the value of the variable customerId, with:
lug.setCustomerID(String.valueOf(CustomerID.getSelectedItem()));
But then it sets its value to 'Khoa'.
How can i show a name in my combobox, but get the INT value of the customerID?
Instead of just adding strings to the ComboBox you can add an object with both ID and String.
You need to override the toString() function of your class from which you create the customer object.
public class Customer {
private String name;
private int id;
public Customer(String label, int identifier) {
name = label;
id = identifier;
}
public getId() { return id; }
#Overrider public String toString() { return name; }
}
You can when using an object out of that class
lug.setCustomerID(CustomerID.getSelectedItem().getId());
You should wrap your data in simple bean (e.g. Customer) with 2 fields: int id; String firstName and toString must return firstName.
After declare your combobox as JComboBox<Customer>.
The result will look like lug.setCustomerID(CustomerID.getSelectedItem().getId());

Categories