I have a method that receives a dto in which many of its fields are used to make a dynamic query based on the non-null fields.
I am accessing each field by reflection in a lambda to add the non-null fields to the dynamic query. This works, but I don't know how to get that list to return it.
#Override
public List<AirlinePreOrderDto> getPreorders(AirlinePreOrderDto airlinePreOrderDto) {
PreOrder entity = PreOrderMapper.mapToJpaEntity(airlinePreOrderDto);
Query dynamicQuery = new Query();
ReflectionUtils.doWithLocalFields(entity.getClass(), field -> {
field.setAccessible(true);
try {
if (field.get(entity) != null) {
if (!field.getName().equals("serialVersionUID")) {
if(field.getName().equals("preorderId")) {
dynamicQuery.addCriteria(Criteria.where(field.getName()).is(field.get(entity)));
} else {
dynamicQuery.addCriteria(Criteria.where(field.getName()).is(field.get(entity)));
}
}
List<AirlinePreOrderDto> result = PreOrderMapper
.mapToDtos(mongoTemplate.find(dynamicQuery, PreOrder.class, "preorders"));
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
});
// return result list;
}
I suggest you to use an enclosing class.
class EnclosingResults {
List<AirlinePreOrderDto> results;
public void setResults(List<AirlinePreOrderDto> results) {
this.results = results;
}
public List<AirlinePreOrderDto> getResults() {
return results;
}
}
So your method will be:
#Override
public List<AirlinePreOrderDto> getPreorders(AirlinePreOrderDto airlinePreOrderDto) {
PreOrder entity = PreOrderMapper.mapToJpaEntity(airlinePreOrderDto);
Query dynamicQuery = new Query();
EnclosingResults resultEncloser = new EnclosingResults();
ReflectionUtils.doWithLocalFields(entity.getClass(), field -> {
field.setAccessible(true);
try {
if (field.get(entity) != null) {
if (!field.getName().equals("serialVersionUID")) {
if(field.getName().equals("preorderId")) {
dynamicQuery.addCriteria(Criteria.where(field.getName()).is(field.get(entity)));
} else {
dynamicQuery.addCriteria(Criteria.where(field.getName()).is(field.get(entity)));
}
}
List<AirlinePreOrderDto> result = PreOrderMapper
.mapToDtos(mongoTemplate.find(dynamicQuery, PreOrder.class, "preorders"));
resultEncloser.setResults(result);
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
});
return resultEncloser.getResults()
}
It seems that you don't want to call mongoTemplate.find or PreOrderMapper.mapToDtos inside of the lambda, as the lambda will be executed once for each local field.
But from your code I'd guesstimate that you really want to execute the query after having built the dynamicQuery object from all fields.
And since you already manipulate the object referenced by dynamicQuery inside the lambda, it's as simple as moving the code to actually execute it out of the lambda:
PreOrder entity = PreOrderMapper.mapToJpaEntity(airlinePreOrderDto);
Query dynamicQuery = new Query();
ReflectionUtils.doWithLocalFields(entity.getClass(), field -> {
field.setAccessible(true);
try {
if (field.get(entity) != null) {
if (!field.getName().equals("serialVersionUID")) {
if(field.getName().equals("preorderId")) {
dynamicQuery.addCriteria(Criteria.where(field.getName()).is(field.get(entity)));
} else {
dynamicQuery.addCriteria(Criteria.where(field.getName()).is(field.get(entity)));
}
}
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
});
List<AirlinePreOrderDto> result = PreOrderMapper
.mapToDtos(mongoTemplate.find(dynamicQuery, PreOrder.class, "preorders"));
return result;
Related
The answer to the following described issue may be as simple as that I am not using SortedSet correctly, but I wouldn't know if that is the case.
void SQLRankGuildsByPoints(final CallbackReturnIntegerStringSortedSet callback)
{
java.sql.Connection cn = null;
try {
cn = DataSource.getConnection();
if(cn != null)
{
PreparedStatement query = cn.prepareStatement("SELECT GuildName, TotalActivityPoints FROM Guilds");
ResultSet result = query.executeQuery();
SortedSet<Pair_IntString> GuildsRanking = new TreeSet(new ComparatorGuildsRanking());
while(result.next())
{
int resultInt = result.getInt("TotalActivityPoints");
String resultString = result.getString("GuildName");
GuildsRanking.add(new Pair_IntString(resultInt, resultString));
}
Bukkit.getScheduler().runTask(MainClassAccess, new Runnable() { //Callback to main thread
#Override
public void run() {
callback.onDone(GuildsRanking);
}
});
}
} catch (SQLException e) {
System.err.print(e);
} finally {
try {
cn.close();
} catch (SQLException e) {
System.err.print(e);
}
}
}
All 8 results from the Guilds table are present in "result" ResultSet.
GuildsRanking.add() isn't adding the new custom Pair_IntString object constructed with the query results, specifically for guilds "test" and "lilo" in Guilds table.
SQLRankGuildsByPoints method finishes it's execution, calling back the GuildsRanking SortedSet without 2 of the iterated results.
This behaviour is unintended and I can't find an explanation for it.
The comparator used for TreeSet:
public class ComparatorGuildsRanking implements Comparator<Pair_IntString> {
#Override
public int compare(Pair_IntString intStr1, Pair_IntString intStr2) {
return intStr2.integer.compareTo(intStr1.integer);
}
}
Custom Pair_IntString class:
public class Pair_IntString {
public Integer integer;
public String string;
Pair_IntString(Integer i, String s)
{
integer = i;
string = s;
}
}
No error messages with the skipped add iterations.
I am writing code to unmarshal XML from a file. I don't know up front which schema the XML is based on so I try to unmarshal it with several schemas in the form of different Jaxb2Marshaller instances.
The method needs to:
attempt to unmarshal the XML with each marshaller
If this succeeds, return the resulting object
If it fails, try the next marshaller
If all marshallers fail, throw an exception with the last error message
Here is the current code:
private Object getObject(final byte[] data) throws MyException {
String lastErrorMessage = "";
for (final Jaxb2Marshaller marshaller : this.marshallers) {
try {
return marshaller.unmarshal(new StreamSource(new ByteArrayInputStream(data)));
} catch (final XmlMappingException e) {
LOGGER.warn("Invalid XML", e);
lastErrorMessage = e.getMessage();
}
}
throw new MyException(lastErrorMessage);
}
I feel this method does too many things at different levels of abstraction:
iterate over marshallers
apply a marshaller
return result
catch exceptions
throw exception
But I don't see a way to simplify it. The try-catch block is needed for every marshaller (because I should catch and ignore these XmlMappingExceptions except the last one). That block either returns a result object, or the lastErrorMessage, which is needed below the iteration to throw the MyException.
The only solution I can think of is to create some contrived Result class which contains either the result object or the error message but that feels cludgy. Any other insights?
I would like methods with a granularity like these:
private Object getObject(byte[] data) throws MyException {
Result result;
for (Jaxb2Marshaller marshaller : this.marshallers) {
result = getObject(marshaller, data);
}
return handleError(result);
}
private Result getObject(Jaxb2Marshaller marshaller, byte[] data) {
try {
return Result.value(marshaller.unmarshal(new StreamSource(new ByteArrayInputStream(data))));
} catch (final XmlMappingException e) {
LOGGER.warn("Invalid XML", e);
return Result.error(e.getMessage());
}
}
private Object handleError(Result result) {
if (result.isError()) {
throw new MyException(result.errroMessage);
}
else {
return result.value;
}
}
But the additional Result class is verbose and cludgy:
private class Result {
String errorMessage;
Object value;
static Result error(String errorMessage) {
Result result = new Result();
result.errorMessage = errorMessage;
return result;
}
static Result value(Object value) {
Result result = new Result();
result.value = value;
return result;
}
boolean isError() {
return errorMessage != null;
}
}
How about this?
public class MultiUnmarshaller {
private final List<Jaxb2Marshaller> marshallers;
private Object value;
private String error;
public MultiUnmarshaller(List<Jaxb2Marshaller> marshallers) {
this.marshallers = marshallers;
}
private void init() {
error = "No marshallers available";
value = null;
}
public Object getObject(byte[] data) throws MyException {
init();
Iterator<Jaxb2Marshaller> it = marshallers.iterator();
while(it.hasNext() && errorMessage != null) {
unmarshalObject(marshaller, data);
}
return produceResult();
}
private void unmarshalObject(Jaxb2Marshaller marshaller, byte[] data) {
try {
value = marshaller.unmarshal(new StreamSource(new ByteArrayInputStream(data)));
error = null;
} catch (final XmlMappingException e) {
LOGGER.warn("Invalid XML", e);
error = e.getMessage();
}
}
private Object produceResult() {
if (error == null) {
return value;
}
else {
throw new MyException(error);
}
}
}
Let's imagine that we have to call one method from different classes by definition coming at run time. For example we receive JSON like this:
{"calculator": "MyClass1", "parameter1": 1.0, "parameter2": 2.0, ... }
MyClass1 and more classes either extend some base class or implement some interface (just to be able to enumerate them at run time). We have to create object, pass the parameters to the object and call calculate() method.
I can think of two ways to do this:
switch(calculatorString) { case "MyClass1": calculator = new MyClass1(); ...
using Java Reflection
The first way is really stupid because the code must be updated each time new calculator class is added to the project. The second way is slightly better, but the IDE is unable to catch any type errors we make while creating the objects and invoking the method.
Are there other ways to do this (possibly better)?
You could maybe use the Java Service Provider Interface:
See for example here:
https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html
https://www.baeldung.com/java-spi
It allows for different implementations/services that you implement (you still need class names in the services files) but don't have to use reflection directly.
You define the service providers in the META-INF
META-INF/services/com.baeldung.rate.spi.ExchangeRateProvider
Your services can register themselves and if you allow them to use the input json, you will be very flexible.
Reflection is the best way to create object at runtime, but its all depend on usecase.
You can also use factory design pattern for object Creation.
Using ReflectionAPI
try {
cls = Class.forName(className);
instance = cls.newInstance();
instance = BeanUtils.populateBean(properties, cls);
} catch (Exception e) {
e.printStackTrace();
}
BeanUtil Class
public static Object populateBean(Map<String, Object> propertyMap, Class<?> clazz) throws Exception {
PropertyUtilsBean bean = new PropertyUtilsBean();
Object obj = null;
try {
obj = clazz.newInstance();
for(Map.Entry<String, Object> entrySet: propertyMap.entrySet()) {
PropertyDescriptor descriptor = null;
try {
descriptor =
bean.getPropertyDescriptor(obj, entrySet.getKey());
if (descriptor == null) {
continue;
}
Method writeMethod = bean.getWriteMethod(descriptor);
writeMethod.invoke(obj, convert(descriptor.getPropertyType(), entrySet.getValue(), DATE_PATTERN));
} catch (IncompatibleConversion e) {
throw e;
} catch (Exception e) {
throw new Exception("Unable to parse");
}
}
}catch(Exception e) {
throw e;
}
return obj;
}
Convert Class
private static Object convert(Class<?> clzz, Object value, String datePattern) throws Exception {
if (clzz.isAssignableFrom(BigInteger.class)) {
if (value == null) {
return value;
}
if (value instanceof BigInteger) {
return value;
}
try {
return new BigInteger(value.toString());
} catch (Exception e) {
throw new IncompatibleConversion(e);
}
} else if (clzz.isAssignableFrom(BigDecimal.class)) {
if (value == null) {
return value;
}
if (value instanceof BigDecimal) {
return parseBigDecimal(value.toString(), DECIMAL_PRECISION);
}
try {
return parseBigDecimal(value.toString(), DECIMAL_PRECISION);
} catch (Exception e) {
throw new IncompatibleConversion(e);
}
} else if (clzz.isAssignableFrom(Integer.class)) {
if (value == null) {
return value;
}
if (value instanceof Integer) {
return value;
}
try {
return new Integer(value.toString());
} catch (Exception e) {
throw new IncompatibleConversion(e);
}
} else if (clzz.isAssignableFrom(Long.class)) {
if (value == null) {
return value;
}
if (value instanceof Long) {
return value;
}
try {
return new Long(value.toString());
} catch (Exception e) {
throw new IncompatibleConversion(e);
}
} else if (clzz.isAssignableFrom(String.class)) {
if (value == null) {
return value;
}
if (value instanceof String) {
return value;
}
try {
return value.toString();
} catch (Exception e) {
throw new IncompatibleConversion(e);
}
} else if (clzz.isAssignableFrom(Date.class)) {
if (value == null) {
return value;
}
if (value instanceof Date) {
return value;
}
if (datePattern == null) {
throw new Exception("date pattern cannot be null");
}
try {
SimpleDateFormat sdf = new SimpleDateFormat(datePattern);
return sdf.parse(value.toString());
} catch (Exception e) {
throw new IncompatibleConversion(e);
}
} else if (clzz.isAssignableFrom(Byte.class)) {
if (value == null) {
return value;
}
if (value instanceof Byte) {
return (value);
} else if (value instanceof Number) {
return new Byte(((Number) value).byteValue());
}
try {
return (new Byte(value.toString()));
} catch (Exception e) {
throw new IncompatibleConversion(e);
}
} else if (clzz.isAssignableFrom(Float.class)) {
if (value == null) {
return value;
}
if (value instanceof Float) {
return (value);
}
try {
return new Float(value.toString());
} catch (Exception e) {
throw new IncompatibleConversion(e);
}
} else if (clzz.isAssignableFrom(Double.class)) {
if (value == null) {
return value;
}
if (value instanceof Double) {
return (value);
}
try {
return new Double(value.toString());
} catch (Exception e) {
throw new IncompatibleConversion(e);
}
}
throw new Exception("Incompactible Conversion");
}
I recently went through a code review, and it was firmly suggested that I consolidate two methods into one. Both methods are identical, save for a single method call in each, and one does not require an argument.
Method #1
private void updateCache(List<CategoryObject> objectList) {
ServiceApi serviceApi = getService();
if (serviceApi != null) {
try {
serviceApi.updateResources(objectList);
} catch (BusinessException e) {
log.error(e);
}
}
}
Method #2
private void registerCache() {
ServiceApi serviceApi = getService();
if (serviceApi != null) {
try {
serviceApi.registerCategory(CATEGORY_NAME);
} catch (BusinessException e) {
log.error(e);
}
}
}
Can these even be efficiently combined?
You can pull the inner functionality out into an interface:
private interface Op {
void perform(ServiceApi serviceApi);
}
static void cache(Op op) {
ServiceApi serviceApi = getService();
if (serviceApi != null) {
try {
op.perform(serviceApi);
} catch (BusinessException e) {
log.error(e);
}
}
}
private void updateCache(List<CategoryObject> objectList) {
cache(new Op() {
#Override
public void perform(ServiceApi serviceApi) {
serviceApi.updateResources(objectList);
}
});
}
private void registerCache() {
cache(new Op() {
#Override
public void perform(ServiceApi serviceApi) {
serviceApi.registerCategory(CATEGORY_NAME);
}
});
}
In Java 8 the two methods become truly simple and elegant.
private void updateCache(List<CategoryObject> objectList) {
cache(serviceApi -> serviceApi.updateResources(objectList));
}
private void registerCache() {
cache(serviceApi -> serviceApi.registerCategory(CATEGORY_NAME));
}
You could just use one method and differentiate by the input parameter:
private void updateCache(List<CategoryObject> objectList) {
ServiceApi serviceApi = getService();
if (serviceApi != null) {
try {
if(objectList != null){
serviceApi.updateResources(objectList);
}
else{
serviceApi.registerCategory(CATEGORY_NAME);
}
} catch (BusinessException e) {
log.error(e);
}
}
}
Maybe the method name should be refactored as well then: handleCache()?
You then can call the method in 2 ways:
handleCache(objectList) --> works like method #1
handleCache(null) --> works like method #2
I have the following method to use a Predicate to filter a collection, throwing out any members where propertyName isn't in a given list of allowed values. It uses Common-BeanUtils to extract a value from the object, and that value has to be a String:
public static <T> void filterListByStringPropertyWithAllowableValues(List<T> listToFilter,
final String propertyName,
final List<String> allowedValues) {
Predicate<T> allowedValuesPredicate = new Predicate<T>() {
#Override
public boolean apply(T arg0) {
String value;
boolean result = false;
try {
value = BeanUtils.getProperty(arg0, propertyName);
System.out.println(value);
result = allowedValues.contains(value);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return result;
}
};
Iterables.filter(listToFilter, allowedValuesPredicate);
}
Unfortunately, my test fails utterly.
#Test
public void testfilterListByStringPropertyWithAllowableValues() throws Exception {
TestClass item1 = new TestClass("value1","whatever1");
TestClass item2 = new TestClass("value2","whatever2");
TestClass item3 = new TestClass("value3","whatever3");
List<TestClass> initialList = Lists.newArrayList(item1, item2, item3);
MyCollectionUtils.filterListByStringPropertyWithAllowableValues(initialList, "importantField",
Lists.newArrayList("value1","value2"), 3);
assertTrue("Collection size should be 2. Actual: " + initialList.size(), initialList.size() == 2);
}
Am I making an incredibly stupid mistake here?
Update: working code is below.
public static <T> List<T> filterListByStringPropertyWithAllowableValues(List<T> listToFilter,
final String propertyName,
final Set<String> allowedValues) {
Predicate<T> allowedValuesPredicate = new Predicate<T>() {
#Override
public boolean apply(T arg0) {
String value;
boolean result = false;
try {
value = BeanUtils.getProperty(arg0, propertyName);
System.out.println(value);
result = allowedValues.contains(value);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return result;
}
};
return Lists.newArrayList(Iterables.filter(listToFilter, allowedValuesPredicate));
}
Yes, you're doing an incredibly stupid mistake ;-)
You're not returning the filtered list. filter() doesn't modify the given list. It returns a filtered Iterable.
Also, allowedValues should be a HashSet rather than a List. That would make your filter more efficient. HashSet.contains() runs in constant time, whereas List.contains() is O(n).