We're currently researching the best way to upgrade from Toplink 2.1-60f to EclipseLink 2.6. The project is somewhat large and most of the manual work would have to be done in parts of the code where we are using NativeQuery. Query.getResultList() result differs between the two JPA-implementations as TopLink returns a List<Vector> and EclipseLink on the other hand returns a List<Object[]>. The code is unfortunately therefore littered with List<Vector> references.

Part of the solution would be to convert the result from list array to a list of vectors. Instead of doing this in all the numerous places manually, I was thinking we could use AspectJ to intercept the getResultList() calls and convert the return values. Is this a viable solution? Has anyone implemented similar solutions? We're using Maven as our build tool.

Thanks in advance!

1

There are 1 best solutions below

0
kriegaex On

My suggestion is: Use a good IDE and refactor your code!

But because you asked for an AOP solution, here is a self-consistent AspectJ example. As I have never used JPA, I will just recreate your situation as a little abstraction.

Abstract Query base implementation with lots of dummy methods:

package de.scrum_master.persistence;

import java.util.*;
import javax.persistence.*;

public abstract class MyBaseQueryImpl implements Query {
    @Override public int executeUpdate() { return 0; }
    @Override public int getFirstResult() { return 0; }
    @Override public FlushModeType getFlushMode() { return null; }
    @Override public Map<String, Object> getHints() { return null; }
    @Override public LockModeType getLockMode() { return null; }
    @Override public int getMaxResults() { return 0; }
    @Override public Parameter<?> getParameter(String arg0) { return null; }
    @Override public Parameter<?> getParameter(int arg0) { return null; }
    @Override public <T> Parameter<T> getParameter(String arg0, Class<T> arg1) { return null; }
    @Override public <T> Parameter<T> getParameter(int arg0, Class<T> arg1) { return null; }
    @Override public <T> T getParameterValue(Parameter<T> arg0) { return null; }
    @Override public Object getParameterValue(String arg0) { return null; }
    @Override public Object getParameterValue(int arg0) { return null; }
    @Override public Set<Parameter<?>> getParameters() { return null; }
    @Override public Object getSingleResult() { return null; }
    @Override public boolean isBound(Parameter<?> arg0) { return false; }
    @Override public Query setFirstResult(int arg0) { return null; }
    @Override public Query setFlushMode(FlushModeType arg0) { return null; }
    @Override public Query setHint(String arg0, Object arg1) { return null; }
    @Override public Query setLockMode(LockModeType arg0) { return null; }
    @Override public Query setMaxResults(int arg0) { return null; }
    @Override public <T> Query setParameter(Parameter<T> arg0, T arg1) { return null; }
    @Override public Query setParameter(String arg0, Object arg1) { return null; }
    @Override public Query setParameter(int arg0, Object arg1) { return null; }
    @Override public Query setParameter(Parameter<Calendar> arg0, Calendar arg1, TemporalType arg2) { return null; }
    @Override public Query setParameter(Parameter<Date> arg0, Date arg1, TemporalType arg2) { return null; }
    @Override public Query setParameter(String arg0, Calendar arg1, TemporalType arg2) { return null; }
    @Override public Query setParameter(String arg0, Date arg1, TemporalType arg2) { return null; }
    @Override public Query setParameter(int arg0, Calendar arg1, TemporalType arg2) { return null; }
    @Override public Query setParameter(int arg0, Date arg1, TemporalType arg2) { return null; }
    @Override public <T> T unwrap(Class<T> arg0) { return null; }
 }

The only method missing is getResultList(), so now let us provide two different implementations for it, extending the abstract base implementation:

Concrete Query implementation returning List<Vector>:

This emulates your TopLink class.

package de.scrum_master.persistence;

import java.util.*;

public class VectorQuery extends MyBaseQueryImpl {
    @Override
    public List getResultList() {
        List<Vector<String>> resultList = new ArrayList<>();
        Vector<String> result = new Vector<>();
        result.add("foo"); result.add("bar");
        resultList.add(result);
        result = new Vector<>();
        result.add("one"); result.add("two");
        resultList.add(result);
        return resultList;
    }
}

Concrete Query implementation returning List<Object[]>:

This emulates your EclipseLink class.

package de.scrum_master.persistence;

import java.util.*;

public class ArrayQuery extends MyBaseQueryImpl {
    @Override
    public List getResultList() {
        List<Object[]> resultList = new ArrayList<>();
        Object[] result = new Object[] { "foo", "bar" };
        resultList.add(result);
        result = new Object[] { "one", "two" };
        resultList.add(result);
        return resultList;
    }
}

Driver application:

The application creates queries of both concrete subtypes, each time assuming that the list elements will be vectors.

package de.scrum_master.app;

import java.util.*;
import de.scrum_master.persistence.*;

public class Application {
    public static void main(String[] args) {
        List<Vector<?>> resultList;

        resultList = new VectorQuery().getResultList();
        for (Vector<?> result : resultList)
            System.out.println(result);

        resultList = new ArrayQuery().getResultList();
        for (Vector<?> result : resultList)
            System.out.println(result);
    }
}

Console log without aspect:

[foo, bar]
[one, two]
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to java.util.Vector
    at de.scrum_master.app.Application.main(Application.java:15)

Uh-oh! This is exactly your problem, right? Now what can we do about it if we absolutely refuse to refactor? We abuse AOP for patching up the legacy code. (Please don't do it, but you can if you absolutely want to.)

AspectJ query result adapter:

Disregarding usage of raw types and other ugly stuff, here is my proof of concept:

package de.scrum_master.aspect;

import java.util.*;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class QueryResultAdapter {
    @Around("call(* javax.persistence.Query.getResultList())")
    public List<Vector> transformQueryResult(ProceedingJoinPoint thisJoinPoint) throws Throwable {
        System.out.println(thisJoinPoint);
        List result = (List) thisJoinPoint.proceed();
        if (result != null && result.size() > 0 && result.get(0) instanceof Vector)
            return result;
        System.out.println("Transforming arrays to vectors");
        List<Vector> transformedResult = new ArrayList<Vector>();
        for (Object[] arrayItem : (List<Object[]>) result)
            transformedResult.add(new Vector(Arrays.asList(arrayItem)));
        return transformedResult;
    }
}

Console log with aspect:

call(List de.scrum_master.persistence.VectorQuery.getResultList())
[foo, bar]
[one, two]
call(List de.scrum_master.persistence.ArrayQuery.getResultList())
Transforming arrays to vectors
[foo, bar]
[one, two]

Et voilà - you can do ugly stuff and other things it was not invented for with AOP. ;-)