Indexing enum in compass-lucene - java

I am using Compass for filtering data out from a DTO object. I mark fields with #SearchableComponent if it is a POJO object, with SearchableProperty if it is a String. That works perfectly: I get the object and String indexed.
My question is how would I annotate an ENUM data type?
Example of enums I have:
public enum FooBar {
FOO("foo"),
BAR("bar");
private final String value;
..(constructor)..
public String value() {
return value;
}
}
Where in this snippet I should put an annotation and which annotation I should put?

From version 2.1 forward this works out of the box using #SearchableProperty annotation to the field that is of the type of this enum eg.
#SearchableProperty
FooBar foobar;
Search uses enum name as the returned type of filtering. Dealing with the value thing that was on the question is to be handled after search is made with names.
See release notes of Compass 2.1.0.

Related

Hibernate Paramater Value did not match expected type with Enum

I'm using hibernate criteria to create a basic select equal expression on an Enumeration field.
The class
#Column
#Enumerated(EnumType.STRING)
private StateEnum state;
The expression
Object value = StateEnum.PENDING;
this.criteriaBuilder.equal(this.root.get("state"), value);
I'm using value as Object because in real code I have a String and need to convert this String to the field instance with reflection.
After some debug I checked that:
Hibernate QueryParameterBindingValidator has some validation, and the important here is:
else if (expectedType.isInstance(value)) {
In my case, this expression is returning false, but checking with IntelliJ tools expectedType is StateEnum:
And value is StateEnum also:
But the isInstance comparation return false
With one base test:
public class Test {
public static void main(String[] args) {
Class clazz = StateEnum.class;
Object value = StateEnum.PENDING;
if(clazz.isInstance(value)) System.out.println("isInstance");
else System.out.println("not isInstance");
}
}
Return "isInstance" because validation is true.
If I force the method return to true on hibernate validation class than hibernate return resultSet like a charm, anyone has any idea why this is happening?
Some version informations:
Hibernate-core: 5.4.15
Spring-boot: 2.3.0.RELEASE
spring-boot-starter-data-jpa: 2.3.0.RELEASE
This error means the types are different.
Unfortunately you didn't post a screenshot of debug output from value.class on
else if (expectedType.isInstance(value)) {
line
Are those enums perhaps of the same name, but from different packages?
Note, for other people with a this kind of exception - most common way to get this kind of error is defining query method with wrong type of parameter, for example a String.

Deserialize enum into object with string attribute

I'm currently trying to deserialize an enum value from a json to an object containing a string(where the enum value should end up).
Example:
Domain class
public class Person {
private UUID personId;
private Occupation occupation;
}
Occupation class:
public class Occupation {
private String occupationType;
}
The code I am running is:
PersonResponse personResponse = JsonConverter.fromJson(message.getPayload(), new TypeReference<Person>() {
});
And the JSON is:
{"personId":"719e622e-6e00-4e84-b748-739f95d7c0fa", "occupationType":"STATE_EMPLOYEE"
Basically, I want my STATE_EMPLOYEE.name() value to end up in a usable object of the Occupation class. As it is now it tries to deserialize the value STATE_EMPLOYEE into an object of the Occupation class, which obviously doesn't work.
Is there a way for me to return an object like this? I don't want to change my Person object to hold an OccupationType enum because it has a load of other stuff as well.
The error I receive is:
Can not construct instance of person.package.Occupation: no String-argument constructor/factory method to deserialize from String value ('STATE_EMPLOYEE')
It basically fails trying to put my enum value into my Occupation object containing the string. (Where I want my thing to be).
Thanks in advance!
I solved it by creating my own custom Deserializer.

Include enum type to serialize and deserialize with jackson

I have a simple enum I'd like to serialize and deserialize. The class looks like this:
public enum TipusViatge {
OCI,
NEGOCIS,
FAMILIA;
#Override
public String toString() {
return name().toUpperCase();
}
}
The thing is, I send it via a restful call and the receiving side may receive any type (so it only knows it will receive Object). So Jackson should be able to figure out the type of the argument to deserialize it.
Is it possible to do so? I was thinking that including the class name in the resulting json should allow Jackson to figure out the type, but I've been unable to do so.
I have worked over this problem for a while.
1st you could deserialize your json with Map<String, Object>. It alway works; you get standard types (your enumeration will be readed as plain string).
2nd in general case you alway know what kind of object you read. This is top-level object and you can set it to Jackson mapper: mapper.readerFor(cls).readValue(json). In case of your enumeration is a part of this cls object, then Jackson knows the type and just read value and parse to it.
3rd you actually could have multiple objects for one json string. I am talking about inheritance. And you could look at #JsonTypeInfo in Jackson documentation.
4th imagin that you read a json source and do not know what you read. In this case, you could ask Jackson to write marker at the beginning of the object. Just like you asking about class name. I think it relates to #JsonRootName. You can look on it here: Jackson JSON Deserialization with Root Element
I think that it is clean now how to work with objects in Jackson. I mean that we know how to tell Jackson what element we want to deserialize. Now we have one problem: how to serialize json -> our enumeration.
5th this is not a problem and works out of the box. Jackson uses name() method to serialize enumeration, and valueOf() to deserialize. You can look at it closer in EnumDeserializer in Jackson.
6th I do not like this behaviour, becuase it is case-sencitive. I faced with situation that when people write json string manually, the use lower-case and cannot deserialize it. Moreover, I belive, that writing enumeration constants directly to the json file is a bad practise, because If I want to refactor names of the enumeration, all existed json string should be modified as well (brrr). To solve thiese issues, I do following trick:
1. Implement EnumId interface with default implementation of parseId(String id) with using getId() to identify enumeration constants and using ignore case for compare.
1. I add id field to the enumeration
2. Add getId() - for serialization
3. Add parseId(String id) - for deserialization
4. Add new module in Jackson ObjectMapper with my customer serializer (it
should use `getId()` instead of `name()`).
if (enumId != null) {
generator.writeString(enumId.getId());
}
And tell Jackson how to deserialize this enum. Here this is dificult situation, becuase in different sources, Jackson use different deseriaization hierarchy and just adding another module to ObjectMapper with custom deserialize (just like in 4.) will not be working with all situations. To solve this problem, I found out that we could add #JsonCreator to parseId(String id) method in enumeration and Jackson will be using it in all situation.
I think that is all about this topic. I give you a code example to make it more clearly (it is better to write once, then explain twice):
public interface EnumId {
String name();
default String getId() {
return name().toLowerCase();
}
static <T extends Enum<?> & EnumId> T parseId(Class<T> cls, String id) {
T res = parseId(cls.getEnumConstants(), id, null);
if (res != null) {
return res;
}
throw new EnumConstantNotPresentException(cls, id);
}
static <T extends EnumId> T parseId(T[] values, String id, T def) {
for (T value : values) {
if (id != null ? id.equalsIgnoreCase(value.getId()) : value.getId() == null) {
return value;
}
}
return def;
}
static <T extends EnumId> T get(T value, T def) {
return value != null ? value : def;
}
}
public enum TipusViatge implements EnumId {
OCI,
NEGOCIS,
FAMILIA;
#JsonCreator
public static TipusViatge parseId(String id) {
return EnumId.parseId(TipusViatge.class, id);
}
}

JPA: Use String fields, but modify them and save as byte array

we are using JSON String in our application to store a lot of configuration data. Now we want to save this to a database as BLOB. The JSON String will be converted to a binary representation (BSON) and then we want to store this.
Entity:
#Entity
#Table(name="TBL_CONFIG")
public class ConfigEntity {
[...]
#Column(name = "CONFIG")
#JSON
private String config;
[...]
}
Global EntityListener:
public class JsonBinaryConversionListener {
#PrePersist
public void process() {
// Check #JSON fields and for every field
[...]
field.set(objectConversion.toBson(field.get()));
[...]
// The field is String, the conversion gives us a byte array
}
}
The column CONFIG is set as BLOB. Only use the #Lob annotation wont work, because we want to change the value manually.
Is there a way that we can realize this through JPA?
If you are using EclipseLink you can use a Converter,
see,
http://www.eclipse.org/eclipselink/documentation/2.4/jpa/extensions/a_converter.htm#CHDEHJEB
Otherwise you can use property access (get/set) methods to convert the data,
http://en.wikibooks.org/wiki/Java_Persistence/Basic_Attributes#Conversion
The JPA 2.1 draft has also proposed Converter support.

Best practice for storing global data in J2EE App using Hibernate

I'm looking for the best solution to store Java EE application's global data using Hibernate. It will consist of key value pairs. Example:
projectStarted = "10-11-11"
developerNumber = 3
teamLeader = "John"
As you see, all of this entries have different types.
For now I see two options:
1) Create GlobalData entity. Each field of it will be represented as unique column in the table and will contain unique setting. This way I have no problems with type casting, but I would like to avoid it in case where there will be big amount of settings.
2) Create Setting entity. Each of it will contain two fields: key(Primary key) and value and will be represented as unique record in the table. This is preferable solution, but It's seems to me that I will get a lot of type casting, because settings can be any type.
So basically, I'm looking for the way to implement second solution without getting a lot of troubles from different types. Can anybody help me?
Thanks.
Edit 1.
Yeah, thanks Christian. Just got similar idea.
What if I will have Settings entity, which will be like:
#Entity
#Table(name = "settings")
public class Setting {
#Column
private String key;
#Column
private String value;
#Column
private String converterClassFullName; //example by.lugovsky.MyConverter
//Getters, setters
}
And GlobalData class.
public class GlobalData {
private Date projectStarted;
private int developerNumber;
private String teamLeader;
Set<Setting> settings;
//Getters and setters for all, except settings.
}
So basically my idea is to convert Setting entity before persisting/updating/ etc. I can do this in my DAO, but I was wondering, if I could annotate GlobalData class with #Entity annotation as well without creating new table. This way I can set OneToMany annotation to Setting's set and Perform conversions in the internal #PrePersist etc. methods.
Will Hibernate allow me to do this?
Thanks again
You could store a Converter-Class into the db and the let it run through the given converter for a property before using the value. JSF offers Converter API:
public interface Converter{
public Object getAsObject(FacesContext fc, UIComponent component, String value) throws ConverterException;
public String getAsString(FacesContext fc, UIComponent component, Object obj) throws ConverterException;
}
If you have a schema with
name: String
value: String
converter: Class
then you could do something like this:
PropertyEntry pe = // Get from OR-Mapper
Converter c = (Converter) pe.getConverter().newInstance();
Object o = c.getAsObject(null, null, pe.getValue());
// use the object o instead of value
For even more coolness you could also define a field in the class which will not be persisted which you could use to hold the converted value within the object.

Categories

Resources