querydsl-jpa dynamic query - java

Let's say I have a generated Entity like this:
public class QCandidate extends EntityPathBase<Candidate> {
public final com.avisto.candisearch.persistance.model.enums.QAvailabilityEnum availability;
public final DatePath<java.util.Date> birthDate = createDate("birthDate", java.util.Date.class);
public final NumberPath<Long> cvId= createNumber("cvId", Long.class);
public final StringPath city = createString("city");
}
My input values are the fields names ("availability","birthDate","cvId"...) and a string value that I should use to perform a 'like' with all the fields.
I want to build a query starting from the field names that:
casts Dates and Numbers to strings and lowercases them
if the field is an EntityPathBase (like availability) extracts the id and then again casts to lowercased string
Something like:
lower(cast(C.cvId as varchar(255))) like 'value'
for each field.
I can do this usign querydsl-sql module, but I want to achieve it using only the jpa module.
I'm not interested in the mechanism of creating the FULL 'where' clause (I know I have to use the BooleanBuilder, or at least, this is what I do in the sql version).
What I want to know is how to create the individual 'where' conditions basing on the field type.
I'm trying to use a PathBuilder but it seems that to use methods like "getString or getBoolean" you already have to know the type of the field that you are trying to extract. In my case, since I start just from the field name, I can't use these methods and I don't know how to identify the type of each field starting from the field name, so I'm stuck.

May be a bit ugly, but workable suggestion.
Note, that the number of field types that PathBuilder accepts is quite limited.
You definitely can find the field class from field name (using reflection or by maintaining a member map updated with each field).
Just implement handling for each specific type.
This can be ugly bunch of if..else or, for more elegant solution, create Map of type handlers [class->handler], each handler implements interface method to handle specific type.
Pseudocode:
//building query
for each field
Class fieldClass = findFieldClas(.., field) //use reflection or map
PathHandler handler = handlers.get(fieldClass)
handler.process( ...)
//type handler interface
public interface Handler{
public xx process(? extends DataPathBase);
}
//specific type handler implementation
public class BooleanHandler implements Handler{
public xx process(? extends DataPathBase path){
BooleanPath bPath = (BooleanPath)path;
...
}
//intitialize handlers map singleton or a factory in advance
handlers.put(BooleanPath.class, new BooleanHandler());
...
Note this is a generic solution if you have many classes. If you have only one specific class, you can just create a permanent map of fieldName->Handler and avoid lookup for the field class.
Again, this is by no means a pretty solution, but should work.

Related

General LocalDate JPA criteria building for unknown entity type

I'm attempting to build a super generalized JPA query builder that takes some general filter and generically builds criteria for a JPA query. In other words, I want it to be used for date filtering on any sort of entity.
For example, I want a method that takes some generic entity information and creates a predicate for a LocalDate. To describe this, it's probably best to show what I have so far:
public class DatePredicateBuilder
{
private final String dateAttribute;
public DatePredicateBuilder(String attributeName)
{
this.dateAttribute = attributeName;
}
#Override
public <ENTITY> Predicate getEndPredicate(
CriteriaBuilder criteriaBuilder,
final Root<ENTITY> data,
final EntityType<ENTITY> entityType,
final LocalDate endDate)
{
return criteriaBuilder.ge(
data.get(entityType.getSingularAttribute(dateAttribute)),
criteriaBuilder.literal(endDate)));
}
}
You can see what the goal is here, I think. I want to be able to build some DatePredicateBuilder class for any attribute on an entity, and have a method that builds the corresponding Predicate for me.
This won't compile, I think, because the generic type of the first argument (unknown) doesn't match that of the second (LocalDate). I can make a tweak that corrects the CriteriaBuilder.ge method call: data.<LocalDate>get(...), but now the getSingularAttribute call fails, I think, because it doesn't know the attribute is LocalDate. This is, again, because it's so generalized that we only know the attribute name.
I don't mind casting where I need to since I'm enforcing the attribute should be a LocalDate (and should fail if it isn't), but I'm not sure what to cast to fix this. In other words, this is only meant to work for LocalDate so we can make that assumption accordingly.
I'll continue hacking it to see if I can get it to at least compile, but any ideas would be appreciated.
If you use
entityType.getSingularAttribute(attributeName, LocalDate.class),
then it should both compile properly, and notify the entity type itself what you expect the attribute to be, so your type requirement is also validated by JPA framework.
Your full code then:
#Override
public <ENTITY> Predicate getEndPredicate(
CriteriaBuilder criteriaBuilder,
final Root<ENTITY> data,
final EntityType<ENTITY> entityType,
final LocalDate endDate)
{
return criteriaBuilder.greaterThanOrEqualTo(
data.get(entityType.getSingularAttribute(dateAttribute, LocalDate.class)),
criteriaBuilder.literal(endDate)));
}
Edit:
Based on comment by OP below, also the criteria builder method to use is not ge (as it is intended for numeric fields), but the greaterThanOrEqualTo.

Unkown Class error trying to implement dependency injection

I'm using generics to get my code reusable and to utilize dependency injection.
I have two Interfaces: DataParserImplementation and ObjectImplementation. I have classes that implement each: SalesRepbyId implements DataParserImpl (it parses the data into objects and puts those objects into collections). SalesRep implements Objectimpl (It is the object for a specific dataset).
I'm trying to get it so that I can select which kind of Objectimpl I use in my SalesRepbyId class so I can remove the coupling.
I know there is something called reflection that I've been told is the method I need to use. I also have heard about a "Factory Pattern" and a "Properties file" that allows me to do what I want to do. A lot of this is very confusing so please explain it like I'm five.
Here is the code with where it stops working:
EDIT: Revisions based on comments: I want to specify the type of DataObject (D) my class uses by passing it through the constructor via a common interface and using generic types. When I try and use it instead of a concrete implementing class, I get the error. I can't find anything about this error.
public class SalesRepbyId<D extends ObjectImplementation> implements DataParserImplementation<Map<String,D>> {
private FileParserImplementation<ArrayList<String[]>> FileParser;
private D dataObject;
public SalesRepbyId(FileParserImplementation<ArrayList<String[]>> FileParser,D d){
this.FileParser = FileParser;
this.dataObject = d;
}
#Override
public Map<String, D> Parse() {
try{
//reads the file and returns an array of string arrays
ArrayList<String[]> Salesrep_contactlist = FileParser.ReadFile;
//here it still says "Unknown Class." that's the problem
Map<String, dataObject> SalesrepByIdMap = new HashMap<>();
//I want to be able to put in any class that implements
//dataObject into this class and have it run the same way.
Summary of what I did
I Implemented the Factory Design pattern and created a properties file which allowed me to reflect in the class I wanted instead of trying to use a generic DataObject (or D) type.
Details of Solution
Reflecting the class using the properties file "config.properties" and then casting it to type Objectimplementation allowed me to use any class that implemented that interface (and was implemented in the Factory and set in the properties file). I then refactored all instances of D to type ObjectImplementation since the parent interface is the layer of abstraction needed here rather than a generic concrete class.
Why it didn't work the way I tried it in the question
the reason the generic D type doesn't work with reflection is because reflection uses a concrete classtype determined at runtime and the generic D type is specified before runtime. Thus I was trying to reflect in the classtype and its methods/instances without properly using reflection and the code was telling me that the classtype was unknown at the time I needed it.
Code example to compare to the Question code
Example of the working code:
public class SalesRepbyId implements
DataParserImplementation<Map<String,ObjectImplementation>> {
private FileParserImplementation<ArrayList<String[]>> FileParser;
//the Factory class that creates instances of the reflected class I wanted
private ObjectFactory Factory = new ObjectFactory();
public Map<String, ObjectImplementation> Parse() {
//the proeprties object which then loads properties from a file and reflects the classtype I want
Properties prop = new Properties();
//loading in the classtype and casting it to the subclass of ObjectImplementation that it actually is
prop.load(SalesRepbyId.class.getResourceAsStream("config.properties"));
Class<? extends ObjectImplementation> Classtouse = Class.forName(prop.getProperty("ObjectImplementation")).asSubclass(ObjectImplementation.class);
//construct instances of 'Classtouse' and parse the data into these dynamically typed objects
//return the map that holds these objects
}

How can I add my own EnumValueMapperSupport

I'm trying to persist some enums in Hibernate and it looks like my two options for built in support are to use the name of the enum, which I would rather not do because it's string based instead of int based, or the ordinal of the enum, which I would rather not do because if I add one of the enum values at the top of the class later on, I break everything down the line.
Instead, I have an interface called Identifiable that has public int getId() as part of its contract. This way, the enums I want to persist can implement Identifable and I can know that they'll define their own id.
But when I try to extend EnumValueMapperSupport so I can utilize this functionality, I'm greeted with errors from the compiler because the EnumValueMapper interface and the EnumValueMapperSupport class are not static, and thus are expected to be locked into a given EnumType object.
How can I extend this functionality in Hibernate, short of rewriting a bunch of Hibernate code and submitting a patch. If I can't, is there another way to somehow store an enum based on something other than the ordinal or name, but instead on your own code?
In a related thought, has anyone personally been down this road and decided "let's see how bad the name mapping is" and just went with name mapping because it wasn't that much worse performance? Like, is it possible I'm prematurely optimizing here?
I'm working against Hibernate version 5.0.2-final.
At least for Hibernate 4.3.5 the EnumValueMapper is static - although private.
But you can extend EnumValueMapperSupport in an extension of EnumType:
public class ExampleEnumType extends EnumType {
public class ExampleMapper extends EnumValueMapperSupport {
...
}
}
To create an instance of this mapper you need an instance of your EnumType:
ExampleEnumType type = new ExampleEnumType();
ExampleMapper mapper = type.new ExampleMapper();
Or you create it inside your type:
public class ExampleEnumType extends EnumType {
public class ExampleMapper extends EnumValueMapperSupport {
...
}
public ExampleMapper createMapper() {
return new ExampleMapper();
}
}

How to get the name of a property as string?

I have a class Car that has a Field named trunk. How can I retrieve that name only with the property that is assigned to it and without any fixed String.
Something working like this fiction would be great:
System.out.println(new Car().getTrunk().getField().getName());
Output:
trunk
I don't want to use a fixed String to retrieve the Field and it's name because that would not refactor well. If I decide to rename from trunk to boot I want this to be handled completely by my IDE's refactoring tool.
UPDATE Car class:
public class Car{
String trunk;
// getters + setters
}
BACKGROUND:
I want to use Primefaces' Dynamic Columns for a CRUD-UI for several entities which uses a columnTemplate containig the names of the Fields/properties to be evaluated by Expression Language.
Consider introducing a enum holding all the properties (without values), for example
enum CarProperty {
TRUNK, HOOD, WHATEVER;
}
and storing them in Car as EnumMap:
class Car {
private Map<CarProperty, String> propsToValues = new EnumMap<>(...);
public String getValue(CarProperty property) { ... }
}
property name could be accessed by
((CarProperty) anyPropery).toString()
And obviously it is easy to refactor
Is not a good idea access to the property name.
With reflection you are breaking OOP principles.
getTrunk() not allways need to access a trunk property
You can have a
private Trunk trunk;//remember to change the TRUNK_FIELD
public static final TRUNK_FIELD = "trunk"
but... try to avoid this solution.

jsf Converter: How to find out the class type of the actual passed value String?

I'm trying to bind custom class objects together with MyFaces UI-components like checkboxes/combos, etc. but without giving the explicit type like int/float/boolean/String/List<String> by creating the getters/setters in the backing bean.
So in my case it will usually look like this:
<h:selectOneListbox id="mylist" value="#{bean.myListValueHolder.data}"
converter="myValueConverter">
...
</h:selectOneListbox>
Where:
bean is backing bean
myListValueHolder is of type MyValueHolder<T>
data is of type <T>
I'm trying to use a custom Converter for it and this class signature:
public class MyValueHolder<T extends Comparable<T>> extends Node<T> {
....
MyValueHolder has this methods:
public T getData() {
...
public void setData(Object data) {
...
public void setData(T data) {
...
My Converter:
public Object getAsObject(FacesContext fc, UIComponent c, String value) {
...
The problem: how can I find out the type <T> passed as value at the runtime? It can be a boolean, int, float, double, String, or List<String> in my case.
But I kinda dislike to write
A lot of "try to cast to a type and if it succeeds, its your type" but without knowing for sure, whether its a int or a float/double.
Giving the type as a parameter or as second parameter as I define my h:selectOneListbox
???
How can I achieve this?
EDIT: OK, List would not work. But the question is, is it any good to bind values from JSF UIComponents in my view to some kind of a value wrapper to avoid write to many simple "dummy" getters/setters in my backing beans?
So I could use "one" bean which will have the MyValueHolder accessible by Key (property name which is a String) like "bean.get('property_name')" or over "#{bean.myListValueHolder.data}" or similar syntax?
EDIT2: To answer BalusC question and to explain the Why.
As for now, I'm not sure whether this approach is a good one.
My data comes as XML, I parse it to save it as key, value pairs. My UI-elements have also a particular state(s) (EnumSet from a State-Enum {hidden, visible, disabled, enabled, hover, focus, active, ...} which is dependent from specific values stored in ValueHolders. The dependency of required State(s) is described in a Rule class, which has an EnumSet of State and a matches method, which returns true if the defined State shall be applied because the Rule matches. Then I have a ValueChangedListeners which shall apply new State(s) to the UI-Components if the Value has been changed and the Rules associated with that Value match.
At least this is how I thought, I can do this. So I can query for value and ui-state from my view and if some concrete value changes, which in turn might change the State of the UIComponent I can invoke a callback and depending on the new State add/remove a new css class to that particular component via JQuery.
Well, sounds kinda complex or pretty overkill, but I want to define my UI-Component States in Java, so I can change the State from Java too and not having the State-Rules defined in the view.
A JSF Converter is not suitable for this. You need a custom ELResolver for this. Extending BeanELResolver and overriding getType(), getValue() and setValue() wherein you grab the concrete ValueHolder implementation should do it. This way you can ultimately end up with expressions like value="#{bean.myStringValueHolder}" without the .data. This way the data coercion/conversion will then automatically be done by JSF/EL.
You can register the custom EL resolver on application's startup by Application#addELResolver():
FacesContext.getCurrentInstance().getApplication().addELResolver(new ValueHolderELResolver());
As to figuring the generic type, assuming that ValueHolder is an abstract class and you've concrete implementations like StringValueHolder extends ValueHolder<String> and so forth, then you'll be able to extract the generic type in the constructor of ValueHolder.
public abstract class ValueHolder<T extends Comparable<T>> extends Node<T> {
private Class<T> type;
private T data;
#SuppressWarnings("unchecked")
public ValueHolder() {
this.type = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
public Class<T> getType() {
return type;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public void setData(Object data) {
this.data = type.cast(data);
}
}
I only wonder how this entire approach is useful. There's some seemingly unnecessary overhead. What exactly is the functional requirement? It is just eliminating getter/setter boilerplate? Why not just using model objects (javabeans/entities/etc) so that you end up with only one model property in the backing bean? Is it being tired of typing them everytime? A bit decent IDE can autogenerate them. Even more, complete javabean classes can be autogenerated based on a list of property names and a few clicks.
You could add a instance of your converter to your bean and bind it to your input element, then you know the type. This way you could also pass the objects to convert to the converter via a constructor and perform simple lookup. Not that handsome but it would work.

Categories