Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPA2 Criteria API runtime cast from varchar(25) to decimal

So I've seen all threads on stack overflow on similar subjects but I've found no solution to my problem.

I'm trying to create a Criteria query and I get this SQL (1st SQL, simplified):

SELECT latitude FROM stations WHERE (ABS(latitude - 45.893227) <= 0.2)

but it gives me this error:

java.sql.SQLDataException: The resulting value is outside the range for the data type DECIMAL/NUMERIC(31,31).

That's because my latitude is of type varchar(25). So this fixes it (2nd SQL):

SELECT latitude FROM stations WHERE (ABS(CAST(latitude AS DECIMAL) - 45.893227) <= 0.2)

But now I need to do it in Criteria syntax. This is my method:

public List<Stations> searchStations(Float distance, List<Address> addresses) {
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Stations> cq = cb.createQuery(Stations.class);
    Root<Stations> stations = cq.from(Stations.class);

    //this <Float> cast actually does nothing:
    Expression<Float> Lat = stations.get("latitude");
    Expression<Float> Lon = stations.get("longitude");

    //make a list of conditions
    List<Predicate> predicates = new ArrayList<>();
    for (Address a : addresses) {
        Float aLat = Float.valueOf(a.getLatitude());
        Float aLon = Float.valueOf(a.getLongitude());


        //condition: |station.Lat - address.Lat| <= distance
        //le() = lessThan, abs() = absolute, diff() = subtraction

        Predicate condLat = cb.le(cb.abs(cb.diff(Lat, aLat)), distance);
        Predicate condLon = cb.le(cb.abs(cb.diff(Lon, aLon)), distance);

        //if I do: Lat.as(Float.class) it won't cast on runtime!

        predicates.add(condLat);
        predicates.add(condLon);
    }

    //add the array of conditions to the WHERE expression, connected with AND:
    cq.where(cb.and(predicates.toArray(new Predicate[predicates.size()])));

    TypedQuery<Stations> q = em.createQuery(cq);
    return q.getResultList();
}

With that code I'm getting the 1st SQL, and I want the 2nd one.I just can't get that runtime cast functionality. The Criteria type cast functions that I've tried don't work on runtime. How do I get around this problem?

like image 763
Denis K Avatar asked May 19 '14 00:05

Denis K


1 Answers

After some searching I found a way to force runtime casts with CriteriaBuilder function using java derby DOUBLE function:

    Expression Lat = stations.get("latitude");

    Expression LatCast = cb.function("DOUBLE", Float.class, Lat);

And I get this SQL query cast:

DOUBLE(LATITUDE)

References: http://www.javadb.net/double-function.html, http://www.objectdb.com/api/java/jpa/criteria/CriteriaBuilder/function_String_Class__Expression__

It works with most supported database functions.

like image 67
Denis K Avatar answered Oct 17 '22 20:10

Denis K