I have the next couple of beans:
Address {
String name;
String number;
String zipcode;
String town;
}
MyEntity {
Address address;
String value1;
String value2;
}
I'm trying to do the next Hibernate query:
private final List<String> propertiesDistinct = Arrays.asList("address.name");
private final List<String> properties = Arrays.asList("address.number",
"address.zipcode", "address.town")
ProjectionList projectionList = Projections.projectionList();
if (propertiesDistinct != null) {
ProjectionList projectionListDistinct = Projections.projectionList();
for (String propertyDistinct : propertiesDistinct)
projectionListDistinct.add(Projections.property(propertyDistinct).as(propertyDistinct));
projectionList.add(Projections.distinct(projectionListAgrupar));
}
if (properties != null)
for (String property : properties)
projectionList.add(Projections.property(property).as(property));
criterio.setProjection(projectionList);
// MORE FILTERS ON MyEntity FIELDS
//... criterio.add(Restrinctions...);
// I want to recover the results on my bean MyEntity so I don't have to create a new one
criterio.setResultTransformer(Transformers.aliasToBean(MyEntity.class));
Problem:
Caused by: org.hibernate.PropertyNotFoundException: Could not find setter for address.name on class com.entities.MyEntity
I understand that Hibernate is looking for something like:
public String getAddressName() {} // This should be in MyEntity
Instead of:
public String getName() {} // In my Address bean
Ideas about how can I fix this without creating a new bean?
Thanks!
I wrote a ResultTransformer that can fix your problem. It's name is AliasToBeanNestedResultTransformer, check it out on github.
Code provided in Github works fine but there is change in import for new versions of hibernate. Its as follow.
org.hibernate.property.PropertyAccessor replaced byorg.hibernate.property.access.spi.PropertyAccess
and
org.hibernate.property.PropertyAccessorFactory replaced by org.hibernate.property.access.internal.PropertyAccessStrategyBasicImpl
So you'll have change the code from
PropertyAccessor accessor = PropertyAccessorFactory.getPropertyAccessor("property");
accessor.getSetter(resultClass, (String)subclassToAlias.get(subclass).get(2)).set(root, subObject, null);
to
PropertyAccess propertyAccess = PropertyAccessStrategyBasicImpl.INSTANCE.buildPropertyAccess(resultClass, (String)subclassToAlias.get(subclass).get(2));
propertyAccess.getSetter().set(root, subObject, null);
AliasToBeanNestedResultTransformer does not handle Nested Multi Level DTOs, so I rewrote one that handles n-level dtos.
Hope this helps.
public class AliasToBeanNestedMultiLevelResultTransformer extends AliasedTupleSubsetResultTransformer {
private static final long serialVersionUID = -8047276133980128266L;
public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) {
return false;
}
private boolean initialized;
private Class<?> resultClass;
private Map<String,Class<?>> clazzMap = new HashMap<>();
private Map<String,Setter> settersMap = new HashMap<>();
public AliasToBeanNestedMultiLevelResultTransformer(Class<?> resultClass) {
this.resultClass = resultClass;
}
public Object transformTuple(Object[] tuples, String[] aliases) {
Map<String,Object> nestedObjectsMap = new HashMap<>();
Object result;
try {
result = resultClass.newInstance();
if (!initialized){
initialized = true;
initialize(aliases);
}
for (int a=0;a<aliases.length;a++){
String alias = aliases[a];
Object tuple = tuples[a];
Object baseObject = result;
int index = alias.lastIndexOf(".");
if(index>0){
String basePath = alias.substring(0, index);
baseObject = nestedObjectsMap.get(basePath);
if (baseObject == null){
baseObject = clazzMap.get(basePath).newInstance();
nestedObjectsMap.put(basePath, baseObject);
}
}
settersMap.get(alias).set(baseObject, tuple,null);
}
for (Entry<String,Object> entry:nestedObjectsMap.entrySet()){
Setter setter = settersMap.get(entry.getKey());
if (entry.getKey().contains(".")){
int index = entry.getKey().lastIndexOf(".");
String basePath = entry.getKey().substring(0, index);
Object obj = nestedObjectsMap.get(basePath);
setter.set(obj, entry.getValue(), null);
}
else{
setter.set(result, entry.getValue(), null);
}
}
}catch ( InstantiationException | IllegalAccessException e) {
throw new HibernateException( "Could not instantiate resultclass: " + resultClass.getName() );
}
return result;
}
private void initialize(String[] aliases) {
PropertyAccessor propertyAccessor = new ChainedPropertyAccessor(
new PropertyAccessor[] {
PropertyAccessorFactory.getPropertyAccessor( resultClass, null ),
PropertyAccessorFactory.getPropertyAccessor( "field" )
}
);
for (int a=0;a<aliases.length;a++){
String alias = aliases[a];
Class<?> baseClass = resultClass;
if (alias.contains(".")){
String[] split = alias.split("\\.");
StringBuffer res = new StringBuffer();
for (int i=0;i<split.length;i++){
if (res.length()>0) res.append(".");
String item = split[i];
res.append(item);
String resString = res.toString();
if (i==split.length-1){
clazzMap.put(resString,baseClass);
settersMap.put(resString, propertyAccessor.getSetter(baseClass, item));
break;
}
Class<?> clazz = clazzMap.get(resString);
if (clazz==null){
clazz = propertyAccessor.getGetter(baseClass,item).getReturnType();
settersMap.put(resString, propertyAccessor.getSetter(baseClass, item));
clazzMap.put(resString,clazz);
}
baseClass = clazz;
}
}
else{
clazzMap.put(alias, resultClass);
settersMap.put(alias, propertyAccessor.getSetter(resultClass, alias));
}
}
}
}
My solution is very basic. It's not as clean as a proper result transformer but it's useful when you just need to do a quick projection for a few properties.
If you get Could not find setter for address.name on class com.entities.MyEntity
It doesn't mean Hibernate is looking for public String getAddressName() {}. Instead it looks for a setter with the impossible "setAddress.name" name.
Instead of .add(Projections.property("address.name"),"address.name")) type a proper setter name as second argument to the .add() method as follows .add(Projections.property("address.name"),"addressName"))
Then, just add a setter on your "MyEntity" root object: "setAddressName".
public void setAddressName(String addressName) {
this.address= (this.address==null) ? new Address() : address;
this.address.setName(addressName);
}
The drawback is that it "dirties" your object with extra methods.
Also posted here.
Try creating an alias like criterio.createAlias("address", "add"); and then edit your properties to be like Arrays.asList("add.number","add.zipcode", "add.town").
Hope this helps.
Related
I have used Lombok in my code to automatically generate getter and setter code. I want to add other personal annotations and use it.
For example, I want to add an #Exist method which verifies the existence of a key in a list:
#Getter #Setter
public class User {
private String name;
private List<Integer> keys;
public boolean existKeys(Integer key) {
boolean exist = keys.contains(key);
return exist;
}
}
After creating the annotation, I would do something like:
#Getter #Setter
public class User {
private String name;
#Exist
private List<Integer> keys;
}
General Considerations
If you are already using Lombok, you can add custom Lombok transformation annotation and handler.
Define Exists annotation with #Target(FIELD) and #Retention(SOURCE)
Create a handler
#ProviderFor(JavacAnnotationHandler.class)
public class HandleExists extends JavacAnnotationHandler<Exists>{ ...`
to process your annotation. Handler class package must start with the lombok. prefix. If you need to support Eclipse, etc. in addition to javac, you'll need to write more handlers extending appropriate framework classes.
In the handler override/implement the handle() method to generate the required code through AST manipulation.
You can take as a sample the #Getter implementation:
Annotation:
Getter.java
Handler:
HandleGetter.java
You can also look into sources of other annotations and handlers to see how to generate particular code.
You'll need to add dependencies on lombok, JDK tools.jar.
Some resources:
The lombok-pg project with a source for a bunch of custom lombok annotations, in particular FluentSetter.java, HandleFluentSetter.java / FluentSetterHandler.java
An overview of a custom transformation
Simple annotation example with explanations.
Note, there are some points to consider here
This is a bunch of non-trivial code to write and maintain. If you plan to use annotation 5-6 times it is just not worth it.
You may need to change your annotation processor implementation with lombok upgrades.
The hole in compiler that lombok relies on also may be closed (then the whole Lombok project will change dramatically or cease to exist; in this case you'll have a more serious problem anyway if you use Lombok extensively, even if just for #Getter).
A more complex alternative without Lombok is to use standard annotation processing for code generation but, AFAIK, you can't change original classes and must generate/use classes that extend them (unless you'll exploit the same back-door as Lombok or resort to a code manipulation like CGLib or ASM).
Lombok Example
Below is some working code to create custom Lombok annotation that I've called #Contains.
It is javac implementation only, no Eclipse, etc. I guess it will be not hard to create a similar handler for Eclipse or other IDE.
It will generate fieldNameContains() member method which is delegated to the fieldName.contains().
Note, the code is just quick and dirty (but working) sample. For production grade annotation, you will need to handle many boundary conditions, check correct types, handle Lombok configuration and so on, as it can be observed in lombok or lombok-pg library sources.
Sample usage
SomeEnity.java
#Getter
#Setter
public class SomeEntity {
#NonNull
#Contains
private Collection<String> fieldOne = new ArrayList<>();
#NonNull
#Contains
private Collection<String> fieldTwo = new ArrayList<>();
}
SomeEntityTest.java
public class SomeEntityTest {
#Test
public void test() {
SomeEntity entity = new SomeEntity();
Collection<String> test1 = Arrays.asList(new String[] { "1", "2" });
entity.setFieldOne(test1);
assertSame(test1, entity.getFieldOne());
Collection<String> test2 = new HashSet<String>(Arrays.asList(new String[] { "3", "4" }));
entity.setFieldTwo(test2);
assertSame(test2, entity.getFieldTwo());
assertTrue(entity.fieldOneContains("1"));
assertTrue(entity.fieldOneContains("2"));
assertFalse(entity.fieldOneContains("3"));
assertFalse(entity.fieldOneContains("4"));
assertFalse(entity.fieldTwoContains("1"));
assertFalse(entity.fieldTwoContains("2"));
assertTrue(entity.fieldTwoContains("3"));
assertTrue(entity.fieldTwoContains("4"));
try {
entity.setFieldOne(null);
fail("exception expected");
} catch (Exception ex) {
}
try {
entity.setFieldTwo(null);
fail("exception expected");
} catch (Exception ex) {
}
}
}
Annotation Implementaiton
Contains.java
#Target({ElementType.FIELD})
#Retention(RetentionPolicy.SOURCE)
public #interface Contains {
Class<?>[] types() default {};
Class<?>[] excludes() default {};
}
HandleContains.java
#ProviderFor(JavacAnnotationHandler.class)
#HandlerPriority(65536)
#ResolutionResetNeeded
public class HandleContains extends JavacAnnotationHandler<Contains> {
#Override
public void handle(AnnotationValues<Contains> annotation, JCAnnotation ast, JavacNode annotationNode) {
try {
JavacNode node = annotationNode.up();
if (node.getKind() != Kind.FIELD) {
annotationNode.addError("#Contains is allowed only on fields");
return;
}
Name delegateName = annotationNode.toName(node.getName());
JavacResolution reso = new JavacResolution(annotationNode.getContext());
JCTree member = node.get();
if (member.type == null) {
reso.resolveClassMember(node);
}
Type delegateType = member.type;
if (delegateType instanceof ClassType) {
ClassType ct = (ClassType) delegateType;
//TODO validate that this field is a collection type
// if(!Collection)
// annotationNode.addError("#Contains can only be used on collections");
final String methodName = "contains";
MethodSig methodSig = getMethodBinding(methodName, ct, annotationNode.getTypesUtil());
if (methodSig == null) throw new Exception("no method " + methodName + " in " + ct.tsym.name);
JCMethodDecl methodDecl = createDelegateMethod(methodSig, annotationNode, delegateName);
injectMethod(node.up(), methodDecl);
} else {
annotationNode.addError("#Contains can only use concrete class types");
return;
}
} catch (Exception ex) {
//ex.printStackTrace();
annotationNode.addError("#Contains unexpected error: " + ex.getMessage());
}
}
public JCMethodDecl createDelegateMethod(MethodSig sig, JavacNode annotation, Name delegateName) throws TypeNotConvertibleException {
JavacTreeMaker maker = annotation.getTreeMaker();
com.sun.tools.javac.util.List<JCAnnotation> annotations;
if (sig.isDeprecated) {
annotations = com.sun.tools.javac.util.List.of(maker.Annotation(genJavaLangTypeRef(annotation, "Deprecated"), com.sun.tools.javac.util.List.<JCExpression>nil()));
} else {
annotations = com.sun.tools.javac.util.List.nil();
}
JCModifiers mods = maker.Modifiers(PUBLIC, annotations);
JCExpression returnType = JavacResolution.typeToJCTree((Type) sig.type.getReturnType(), annotation.getAst(), true);
boolean useReturn = sig.type.getReturnType().getKind() != TypeKind.VOID;
ListBuffer<JCVariableDecl> params = sig.type.getParameterTypes().isEmpty() ? null : new ListBuffer<JCVariableDecl>();
ListBuffer<JCExpression> args = sig.type.getParameterTypes().isEmpty() ? null : new ListBuffer<JCExpression>();
ListBuffer<JCExpression> thrown = sig.type.getThrownTypes().isEmpty() ? null : new ListBuffer<JCExpression>();
ListBuffer<JCTypeParameter> typeParams = sig.type.getTypeVariables().isEmpty() ? null : new ListBuffer<JCTypeParameter>();
ListBuffer<JCExpression> typeArgs = sig.type.getTypeVariables().isEmpty() ? null : new ListBuffer<JCExpression>();
Types types = Types.instance(annotation.getContext());
for (TypeMirror param : sig.type.getTypeVariables()) {
Name name = ((TypeVar) param).tsym.name;
ListBuffer<JCExpression> bounds = new ListBuffer<JCExpression>();
for (Type type : types.getBounds((TypeVar) param)) {
bounds.append(JavacResolution.typeToJCTree(type, annotation.getAst(), true));
}
typeParams.append(maker.TypeParameter(name, bounds.toList()));
typeArgs.append(maker.Ident(name));
}
for (TypeMirror ex : sig.type.getThrownTypes()) {
thrown.append(JavacResolution.typeToJCTree((Type) ex, annotation.getAst(), true));
}
int idx = 0;
String[] paramNames = sig.getParameterNames();
boolean varargs = sig.elem.isVarArgs();
for (TypeMirror param : sig.type.getParameterTypes()) {
long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, annotation.getContext());
JCModifiers paramMods = maker.Modifiers(flags);
Name name = annotation.toName(paramNames[idx++]);
if (varargs && idx == paramNames.length) {
paramMods.flags |= VARARGS;
}
params.append(maker.VarDef(paramMods, name, JavacResolution.typeToJCTree((Type) param, annotation.getAst(), true), null));
args.append(maker.Ident(name));
}
JCExpression accessor = maker.Select(maker.Ident(annotation.toName("this")), delegateName);
JCExpression delegateCall = maker.Apply(toList(typeArgs), maker.Select(accessor, sig.name), toList(args));
JCStatement body = useReturn ? maker.Return(delegateCall) : maker.Exec(delegateCall);
JCBlock bodyBlock = maker.Block(0, com.sun.tools.javac.util.List.of(body));
StringBuilder generatedMethodName = new StringBuilder(delegateName);
generatedMethodName.append(sig.name.toString());
generatedMethodName.setCharAt(delegateName.length(), Character.toUpperCase(generatedMethodName.charAt(delegateName.length())));
return recursiveSetGeneratedBy(maker.MethodDef(mods, annotation.toName(generatedMethodName.toString()), returnType, toList(typeParams), toList(params), toList(thrown), bodyBlock, null), annotation.get(), annotation.getContext());
}
public static <T> com.sun.tools.javac.util.List<T> toList(ListBuffer<T> collection) {
return collection == null ? com.sun.tools.javac.util.List.<T>nil() : collection.toList();
}
public static class MethodSig {
final Name name;
final ExecutableType type;
final boolean isDeprecated;
final ExecutableElement elem;
MethodSig(Name name, ExecutableType type, boolean isDeprecated, ExecutableElement elem) {
this.name = name;
this.type = type;
this.isDeprecated = isDeprecated;
this.elem = elem;
}
String[] getParameterNames() {
List<? extends VariableElement> paramList = elem.getParameters();
String[] paramNames = new String[paramList.size()];
for (int i = 0; i < paramNames.length; i++) {
paramNames[i] = paramList.get(i).getSimpleName().toString();
}
return paramNames;
}
#Override public String toString() {
return (isDeprecated ? "#Deprecated " : "") + name + " " + type;
}
}
public MethodSig getMethodBinding(String name, ClassType ct, JavacTypes types) {
MethodSig result = null;
TypeSymbol tsym = ct.asElement();
if (tsym == null) throw new IllegalArgumentException("no class");
for (Symbol member : tsym.getEnclosedElements()) {
if (member.getKind() != ElementKind.METHOD || !name.equals(member.name.toString())) {
continue;
}
if (member.isStatic()) continue;
if (member.isConstructor()) continue;
ExecutableElement exElem = (ExecutableElement) member;
if (!exElem.getModifiers().contains(Modifier.PUBLIC)) continue;
ExecutableType methodType = (ExecutableType) types.asMemberOf(ct, member);
boolean isDeprecated = (member.flags() & DEPRECATED) != 0;
result = new MethodSig(member.name, methodType, isDeprecated, exElem);
}
if (result == null) {
if (ct.supertype_field instanceof ClassType) {
result = getMethodBinding(name, (ClassType) ct.supertype_field, types);
}
if (result == null) {
if (ct.interfaces_field != null) {
for (Type iface : ct.interfaces_field) {
if (iface instanceof ClassType) {
result = getMethodBinding(name, (ClassType) iface, types);
if (result != null) {
break;
}
}
}
}
}
}
return result;
}
}
I didn't find anywhere whether it's possible to create pageable manually from string, let assume that we have the following service method
public <T> List<T> findAnything(final int page, final int size, final String sort) { // e.g. id,desc&username,asc
final Pageable pageable = new PageRequest(page, size, null);
return null;
}
my question is how can i instantiate an object of
org.springframework.data.domain.Sort
from a given string of format, important note that these parameters are chagned dynamically, so more likely i need a path to the spring parser, in my example im passing null instead the object
id,desc&username,asc
EDIT
A little bit more details I'm looking for a mechanism of how spring converts the 'sort' string(with the rest of default parameters) that's coming to the rest endpoint as a query param to Pageable object
You can do :
private Sort orderBy() {
return new Sort(Sort.Direction.DESC, "ID")
.and(new Sort(Sort.Direction.ASC, "username"));
}
I think this is helpful
Sort class has static nested class Order :
public static class Order{
private final Direction direction;
private final String property;
private final boolean ignoreCase;
private final NullHandling nullHandling;
}
and then you can use :
public static Sort by(List<Order> orders)
where you create your Order from String like simply splitting.
For that purpose I've written something similar to what spring has, i'd be happy if spring exposes SortHandlerMethodArgumentResolver.parseParameterIntoSort for usage outside the package but so far it's not
private Sort parseMultipleSortQueries(final String query) {
final String[] queries = query.split("&");
return parseSortQuery(queries, ",");
}
private Sort parseSortQuery(final String[] query, String delimiter) {
final List<Sort.Order> orders = new ArrayList<>();
for (String q : query) {
if (q == null) {
continue;
}
final String[] parts = q.split(delimiter);
final Sort.Direction direction = parts.length == 0 ? null : Sort.Direction.fromStringOrNull(parts[parts.length - 1]);
for (int i = 0; i < parts.length; i++) {
if (i == parts.length - 1 && direction != null) {
continue;
}
final String property = parts[i];
if (!StringUtils.hasText(property)) {
continue;
}
orders.add(new Sort.Order(direction, property));
}
}
return orders.isEmpty() ? null : new Sort(orders);
}
and here is the test
#Test
public void testParseQuery() {
System.out.println(parseMultipleSortQueries("firstName,asc&lastName,desc")); //firstName: ASC,lastName: DESC
}
This question already has answers here:
Converting many 'if else' statements to a cleaner approach [duplicate]
(7 answers)
Closed 4 years ago.
I think this is a very common situation in web projects. Assume there is an entity such as:
//JAVA code
#Data
class Entity{
private String a;
private String aExt;
private String b;
private String bExt;
private String c;
private String cExt;
... something more ...
}
For some purpose, I need to get part of values from Entity according to a passed argument, like:
public ViewObject foo(Entity entity, String condition){
ViewObject vo = new ViewObject();
if("aRelated".equals(condition)){
vo.setValue1(entity.getA());
vo.setValue2(entity.getAExt());
}
else if("bRelated".equals(condition)){
vo.setValue1(entity.getB());
vo.setValue2(entity.getBExt());
}
else if(cRelated".equals(condition)){
vo.setValue1(entity.getC());
vo.setValue2(entity.getCExt());
}
... else statement if there are other values ....
return vo;
}
I know I can use switch-case statement to reduce some words in foo(), but there is no essential difference compared with if-else, especially when the Entity has many variables.
As a plain Example, foo() is only a view object builder, but my project is more complex which have many duplicated code with only different variable's name in each if-else statement.
How do I reduce the above duplicated code?
You can try creating two hash maps:
// name these properly!
HashMap<String, Function<Entity, String>> valueMap = new HashMap<>();
HashMap<String, Function<Entity, String>> extMap = new HashMap<>();
Add these KVPs:
// valueMap
"aRelated" - Entity::getA
"bRelated" - Entity::getB
"cRelated" - Entity::getC
// extMap
"aRelated" - Entity::getAExt
"bRelated" - Entity::getBExt
"cRelated" - Entity::getCExt
Now, you can do this without an if statement:
vo.setValue1(valueMap.get(condition).apply(entity));
vo.setValue2(extMap.get(condition).apply(entity));
Another option would be to use reflection:
import java.lang.reflect.Method;
import java.lang.reflext.InvocationTargetException;
...
public ViewObject foo(Entity e, String c) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
String[] methodNames = { "get" + c.substring(0,1).toUpperCase(), "get" + c.substring(0,1).toUpperCase() + "Ext" };
Method[] methods = { e.getClass().getDeclaredMethod(methodNames[0]), e.getClass().getDeclaredMethod(methodNames[1]) };
ViewObject vo = new ViewObject();
vo.setValue1((String)methods[0].invoke(e));
vo.setValue2((String)methods[1].invoke(e));
return vo;
}
Although I have to admit I personally like the map approach of the other answers more, just showing more options.
Use of a Map would do the trick:
class EntityPart {
String s;
String sExt;
}
class Entity {
Map<String,EntityPart> m = new HashMap<>();
m.add("aRelated",new EntityPart());
m.add("bRelated",new EntityPart());
....
}
public ViewObject foo(Entity entity, String condition) {
ViewObject vo = new ViewObject();
EntityPart ep = entity.m.get(condition);
vo.setValue1(ep.s);
vo.setValue2(ep.sExt);
return vo;
}
Make Entity as enum instead of class.
public enum Entity {
A("a", "aExt"), B("b", "bExt"), C("c", "cExt");
private final String name;
private final String text;
private Entity(String name, String text) {
this.name = name;
this.text = text;
}
public String getName() {
return name;
}
public String getText() {
return text;
}
public static Entity fromString(String raw) {
return LOOKUP.get(raw);
}
private static final Map<String, Entity> LOOKUP = new HashMap<>();
static {
for (Entity e : values()) {
LOOKUP.put(e.getName(), e);
}
}
}
And modify your foo method as
public ViewObject foo(String condition){
/*
* pass condition as "a", "b", "c" only not "aRelated", "bRelated", "cRelated"
*
*/
ViewObject vo = new ViewObject();
Entity e = Entity.fromString(condition);
if(null != e) {
vo.setValue1(e.getName());
vo.setValue2(e.getText());
}
return vo;
}
I need to execute a procedure on my sql server database that will return me some fields and I wish to transform this fields directly in a List of my DTO Object that will be returned, but i'm new on spring boot and can't get it to work. I tried to do a Converter class but didnt understand much of how it works e probally did it wrong, here is my code on a way i wish it work:
public interface IMyDtoRepository extends JpaRepository<SomeEntity, Long> {
#Query(value = "EXECUTE MyProcedure :param1, :param2, :param3, :param4, :param5)")
public List<MyDtoObject> execMyProcedure(#Param(value = "param1") Integer param1,
#Param(value = "param2") String param2,
#Param(value = "param3") String param3,
#Param(value = "param4") String param4,
#Param(value = "param5") Integer param5);
}
The DtoObject
public class MyDtoObject{
// My Declared Fields...
public MyDtoObject() {
}
public MyDtoObject(/* My Fields */) {
// Setting fields
}
public MyDtoObject(Object[] objects) {
// Setting fields
}
// Getters n Setters...
I omitted the information that i didn't think it was necessary but i can give more explanation if need it
to map the result on your DtoObject with spring-data-jpa your can use : #SqlResultSetMapping
javadoc here
I have a similar method that I use in my DAL. It uses reflection and generics to convert a datatable to whatever type you pass in. Just pass in the datatable you get as a result of your procedure and you're good to go.
public List<T> ConvertDataToTypeList<T>(System.Data.DataTable DataTable) where T : class, new()
{
try
{
System.Type t_Object_Type = typeof(T);
ICollection<PropertyInfo> p_Properties;
lock (Properties_Dictionary)
{
if (!Properties_Dictionary.TryGetValue(t_Object_Type, out p_Properties))
{
p_Properties = t_Object_Type.GetProperties().Where(property => property.CanWrite).ToList();
Properties_Dictionary.Add(t_Object_Type, p_Properties);
}
}
System.Collections.Generic.List<T> l_List = new List<T>(DataTable.Rows.Count);
foreach (var v_Row in DataTable.AsEnumerable())
{
T o_Object = new T();
foreach (var prop in p_Properties)
{
var propType = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
var safeValue = v_Row[prop.Name] == null ? null : Convert.ChangeType(v_Row[prop.Name], propType);
prop.SetValue(o_Object, safeValue, null);
}
l_List.Add(o_Object);
}
return l_List;
}
catch
{
return new List<T>();
}
}
I have created an enumeration:
public enum ROLECATEGORY {
LOW ("Low Risk", 0),
MEDIUM ("Medium Risk", 1),
public final String attrname;
public final int value;
ROLECATEGORY(String attrname, int value) {
this.attrname = attrname;
this.value = value;
}
public static ROLECATEGORY valueOf(int val){
switch(val){
case 0: return LOW;
case 1: return MEDIUM;
default: throw new IllegalArgumentException("blablabla");
}
}
public int toInt() { return value; }
}
According to the starter tutorial I've created the normal ODataProvider Class. All I'm missing is a peace of code to get the enum as FQDN type for the property instantiation:
CsdlProperty p = new CsdlProperty().setName("MYENUM").setType( ?getEnumType("MYENUM")? )
OK, I found a simple solution myself. But it's probably not the best one:
1.) I've added a new static FullQualifiedName:
public static final FullQualifiedName CET_ROLECAT = new FullQualifiedName(NAMESPACE, "RoleCategory");
2.) I've created the member getEnumType()
public CsdlEnumType getEnumType(final FullQualifiedName enmuTypeName){
if (CET_ROLECAT.equals(enmuTypeName)) {
return new CsdlEnumType()
.setName(CET_ROLECAT.getName())
.setMembers(Arrays.asList(
new CsdlEnumMember().setName("LOW").setValue("0"),
new CsdlEnumMember().setName("MEDIUM").setValue("1")
))
.setUnderlyingType(EdmPrimitiveTypeKind.Int32.getFullQualifiedName())
;
}
return null;
}
3.) I've added the FQDN from 1.) to my Entity Property:
// ...
CsdlProperty p = new CsdlProperty().setName("RoleCategory").setType(CET_ROLECAT));
//...
4.) Finally I've added the EnumType the my schema:
public List<CsdlSchema> getSchemas() throws ODataException {
CsdlSchema schema = new CsdlSchema();
// ...
List<CsdlEnumType> enumTypes = new ArrayList<CsdlEnumType>();
enumTypes.add(getEnumType(CET_ROLECAT));
schema.setEnumTypes(enumTypes);
// ...
List<CsdlSchema> schemas = new ArrayList<CsdlSchema>();
schemas.add(schema);
return schemas;
}
FYI: 'NAMESPACE' is just a public static final String member in my EdmODataProvider class.
Unfortunately it was only possible for me to add Strings in the name and value parts in 2.) at the CsdlEnumMember. Neither I'm sure what's setUnderlyningType() for.