0

I want to call several "setter" methods dynamically from several values passed. Each method will have String, int... type of variables to set (for example: setUserName(String userName) or setUserAge(int age)).

In my case I have a setter method "setUse_status(int use_status)" and I´m not able to configure the getDeclaredMethod method to work. It seems that when getting the method the classVariableName is set to a String (obvious as I´m passing that variable as a name [string] of the variable).

I´m figuring out how to obtain the variable type and set it in the getDeclaredMethod call.

So, I have this code:

private void  getValueFromColumn(Object paramsClass, String classVariableName, Object dataBaseValue) {

//First create the string with the method name
    String methodName = "set" + classVariableName.substring(0, 1).toUpperCase().toString() +
            classVariableName.substring(1);

    Method loadParamsMethod;


    try {
        loadParamsMethod = paramsClass.getClass().getDeclaredMethod(methodName, classVariableName.getClass());

        try {
            loadParamsMethod.invoke(paramsClass, dataBaseValue);
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {

            e.printStackTrace();
        }
    } catch (NoSuchMethodException | SecurityException e) {

        e.printStackTrace();
    }
}

And the error:

java.lang.NoSuchMethodException: com.sener.dbgui.model.params.CommonParameters.setUse_status(java.lang.String)
at java.base/java.lang.Class.getDeclaredMethod(Class.java:2432)
at com.sener.dbgui.utils.ParamsClassUtils.getValueFromColumn(ParamsClassUtils.java:72)
at com.sener.dbgui.utils.ParamsClassUtils.retrieveParamsData(ParamsClassUtils.java:107)
at com.sener.dbgui.utils.ParamsClassUtils.loadAllParameters(ParamsClassUtils.java:39)
at com.sener.dbgui.controller.ComponentController.retrieveParamsFromDB(ComponentController.java:506)
at com.sener.dbgui.controller.ComponentController.access$18(ComponentController.java:494)
at com.sener.dbgui.controller.ComponentController$4.run(ComponentController.java:294)
at java.base/java.lang.Thread.run(Thread.java:844)
LazyTurtle
  • 131
  • 1
  • 3
  • 16
  • Can you give an example for `classVariableName`? Would it be "int" or "use_status" in your example? – Malte Hartwig Feb 20 '18 at 08:59
  • Ok, I have seen this possible solution [link](https://stackoverflow.com/questions/27838243/call-a-method-with-arguments-by-a-variable-if-argument-type-is-unknown) – LazyTurtle Feb 20 '18 at 08:59
  • 2
    `classVariableName.getClass()` will return `String` - should be obvious. Dou you mean `dataBaseValue.getClass()` instead? In that case be aware that `int` isn't a class, i.e. when you pass an integer as `dataBaseValue` you'd get a wrapper class, e.g. `Integer` and `getDeclaredMethod(name, Integer.class)` won't find the method if it accepts an `int` parameter. – Thomas Feb 20 '18 at 08:59
  • Yes, it would be use_status in my example – LazyTurtle Feb 20 '18 at 08:59
  • I think the name of the parameter does not help in this situation. Follow Thomas' lead. – Malte Hartwig Feb 20 '18 at 09:00
  • That does not scale well, is quite slow * number of fields. Better use JPA (for instance eclipseLink), or with DIY generate code. If you still want to use it, consider XPath. – Joop Eggen Feb 20 '18 at 09:00
  • @Thomas There is `int.class`. – lexicore Feb 20 '18 at 09:01
  • @JoopEggen How exactly JPA or XPath would help calling setters dynamically? – lexicore Feb 20 '18 at 09:02
  • @lexicore well, you still can't pass primitives to parameters that accept objects only. You'd need to use (auto-) boxing in which case the parameter would have the type `Integer`. Besides that `int.class` is more of a means to get the`int` primitive into reflection rather than an actual class definition. – Thomas Feb 20 '18 at 09:03
  • @lexicore I can't say much about XPath but JPA would use the Java beans specification to look up properties with their setters and getters, map the properties to columns and handle calling the setters and getters transparently. There's no need to fiddle with reflection yourself. – Thomas Feb 20 '18 at 09:05
  • @lexicore JPA also provides O/R mapping, but not by the slow reflection. XPath uses reflection, and might fit, should complexer expressions be required. JPA is more mature; it would have made from the DB column `use_status` the java field `useStatus`. – Joop Eggen Feb 20 '18 at 09:10
  • 1
    But, as I have the variable name and class, is there a way I can find the variable type of the setter method? From my complete ignorance this should be simple... – LazyTurtle Feb 20 '18 at 09:13
  • @JoopEggen The OP wants "to call several "setter" methods dynamically from several values passed", this is not an ORM task. Forcing ORM or JPA on it is, let's say, rather creative. Saying that "XPath uses reflection" is also a bit strange as XPath is simply an XML query language and per se does not use any expression. – lexicore Feb 20 '18 at 09:22
  • @lexicore thanks for the patience, I might have confused XPath with some EL (expression language) library. My original comment was not intended as answer, but a hope to prevent this kind of solution: I was a poor sinner too, from this kind of bean access to field maps. – Joop Eggen Feb 20 '18 at 09:30
  • @Thomas So, if using Integer variables the code seems to work and I succesfully load database data to the specified class. Thank you Thomas for that tip. – LazyTurtle Feb 20 '18 at 09:38
  • @Joop Eggen On the other hand, is there a simple way round in accessing dinamically methods of a class and reading // updating data in a database with plain JDBC? – LazyTurtle Feb 20 '18 at 09:41
  • @user6127833 when you want to connect objects to a relational database you need some form of object relational mapping (ORM in short). Currently you're building it yourself while JPA is a standard for doing that in Java. Plain JDBC is more on the relational side so in almost all cases you _will_ need something in addition to get the mapping right. – Thomas Feb 20 '18 at 09:46
  • You could use code to generate java entities from the database, mappers from ResultSet to such an entity, and say produce a library. However JPA does that, and has for instance a nice typesafe criteria builder. I would play with an eclipseLink sample. Alternatively Spring has JdbcTemplate. There exist things like QueryDSL, JDBI, JOOQ. Pick code generation over reflective access (same effort). You might use JPA at least to create the entities, with is-getters, camel-case identifiers, annotations and such. – Joop Eggen Feb 20 '18 at 09:59

1 Answers1

1

The problem with your code is that you are using classVariableName.getClass() as parameter type when looking for the method. Since classVariableName is String, classVariableName.getClass() always gives you a string. You should use something like dataBaseValue.getClass() instead, but also consider the case with primitive/wrapper types. Like, you get Integer but your setter accepts int.

You cannot find out the type of the method parameter by name just from classVariableName, it is not that easy. What you could (theoretically) do:

  • Get all the declared methods using Class.getDeclaredMethods().
  • Search for methods with the corresponding name (there may be several).
  • For each of the methods, discover the parameter names (more on it below).
  • Select the method with the corresponding parameter name.

As for "discover the parameter names", see this answer:

Getting the name of a method parameter

It is not always possible: the code must be compiled with debug information.

Finally, why do you want to deal with parameter names anyway? Setter convention per se is string enough, there is actually no need to check parameter names, but it makes stuff much more complicated. Just search for a method with corresponding name and signature and that's it.

lexicore
  • 42,748
  • 17
  • 132
  • 221
  • Nonetheless, using this approach I will still have the primitive/wrapper types issue, won´t I? For example, having a setter for a float type variable will not work. Is this correct? – LazyTurtle Feb 20 '18 at 10:16
  • @user6127833 Yes, you have to solve primitive/wrapper type anyway. I'd check Apache Commons (probably Lang) libraries to see if they have some utility classes for this. – lexicore Feb 20 '18 at 10:27
  • Any suggestion on this? – LazyTurtle Feb 20 '18 at 12:41
  • @user6127833 None except for - just implement it! It is not so difficult. – lexicore Feb 20 '18 at 13:14
  • Sorry but i´m still new to this and I´m a bit lost when trying to find a way. Is there an easy example of something similar to what I have to do? Maybe this [link](https://stackoverflow.com/questions/13943550/how-to-convert-from-string-to-a-primitive-type-or-standard-java-wrapper-types) is an approach. – LazyTurtle Feb 20 '18 at 13:45
  • A possible and simpler solution is to change all my "int", "float" primitives the corresponding wrapper. Would this be correct? – LazyTurtle Feb 20 '18 at 14:24
  • @user6127833 You may also change all primitives to wrappers in setters and make this a convention. Otherwise you should try both primitive and wrapper class for primitive wrapper classes. – lexicore Feb 20 '18 at 14:53
  • @user6127833 To make your life easier, you may want to use [`MethodUtils`](https://commons.apache.org/proper/commons-lang/xref/org/apache/commons/lang3/reflect/MethodUtils.html) from Apache Commons Lang. It considers such corner cases (and many more). – lexicore Feb 20 '18 at 14:57
  • Thank you for your help and your patience! – LazyTurtle Feb 20 '18 at 15:00