GeoLatte / geolatte-geom

A geometry model that conforms to the OGC Simple Features for SQL specification.
Other
132 stars 63 forks source link

Oracle - persist SDO point with JPA gives java.lang.ArrayStoreException (with fix(?)) #87

Closed FDN73 closed 4 years ago

FDN73 commented 4 years ago

Hello, given this example entity:

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import org.geolatte.geom.G3D;
import org.geolatte.geom.Point;
import org.geolatte.geom.builder.DSL;
import org.geolatte.geom.crs.CoordinateReferenceSystem;
import org.geolatte.geom.crs.CoordinateReferenceSystems;
import org.geolatte.geom.crs.LinearUnit;
import org.hibernate.annotations.GenericGenerator;

/**
 *
 * @author fdn
 */
@Entity
@Table(name="TEST_TABLE")
public class TestEntity
{
    @Id
    @GeneratedValue(generator="Identity12c")
    @GenericGenerator(name="Identity12c", strategy="it.jh3.server.jpa.idgenerators.Ora12IdentityGenerator")
    private Long id;
    private Point position;

    public Point getPosition()
    {
        return position;
    }

    public void setPosition(double latitude, double longitude)
    {
        CoordinateReferenceSystem<G3D> wgs84z = CoordinateReferenceSystems.addVerticalSystem(CoordinateReferenceSystems.WGS84, G3D.class,
            LinearUnit.METER);
        G3D g3d = DSL.g(longitude,latitude,0.0);
        this.position = DSL.point(wgs84z, g3d);
    }

}

When persisted, it will give this error:

java.lang.ArrayStoreException: java.lang.Double

I traced the error to the createStruct method of the OracleJDBCTypeFactory class, in particular this section:

if (geom.getPoint() != null) {
            final Object pointStructDescriptor = createStructDescriptor( SDOGeometry.getPointTypeName(), oracleConnection );
            final Object[] pointAttributes = createDatumArray(3);
            SDOPoint sdoPnt = geom.getPoint();
            pointAttributes[0] = sdoPnt.x;
            pointAttributes[1] = sdoPnt.y;
            pointAttributes[2] = sdoPnt.z;
            attributes[2] = createStruct(pointStructDescriptor, oracleConnection, pointAttributes);
        }

I reproduced the issue with this simple code (copying the required methods from the mentioned class):

Class datumClass = findClass( "oracle.sql.Datum" );
        final Object[] pointAttributes = createDatumArray(datumClass,3);
        pointAttributes[0] = 49.0;
        pointAttributes[1] = 15.0;
        pointAttributes[2] = 0.0;

In this case, a code that would work is:

import oracle.sql.BINARY_DOUBLE;
...
Class datumClass = findClass( "oracle.sql.Datum" );
        final Object[] pointAttributes = createDatumArray(datumClass,3);
        pointAttributes[0] = new BINARY_DOUBLE(49.0);
        pointAttributes[1] = new BINARY_DOUBLE(15.0);
        pointAttributes[2] = new BINARY_DOUBLE(0.0);

Any thoughts?

FDN73 commented 4 years ago

I forgot to specify that the error is triggered enabling SDO POINT usage: System.setProperty(Features.USE_SDO_POINT, "true");

FDN73 commented 4 years ago

Here a complete unit test (taken from a Spring Boot application, but the ConnectionFinder can be overridden to explicitly connect to Oracle):

    @Test
    public void testUseSDOPoint() throws SQLException
    {
        CoordinateReferenceSystem<G3D> wgs84z = CoordinateReferenceSystems.addVerticalSystem(CoordinateReferenceSystems.WGS84, G3D.class,
            LinearUnit.METER);
        org.geolatte.geom.Point<G3D> point = DSL.point(wgs84z, DSL.g(49.0,15.0,0.0d));
        System.setProperty(Features.USE_SDO_POINT, "true");

        SDOGeometry sdoGeometry = Encoders.encode(point);

        Struct struct = new OracleJDBCTypeFactory(new ConnectionFinder()
        {
            @Override
            public Connection find(Connection conn)
            {
                try
                {
                    return dataSource.getConnection();
                }
                catch (SQLException ex)
                {
                    System.error(ex.toString());
                }
                return null;
            }
        }).createStruct(sdoGeometry, ds.getConnection());//**Here will fail **
        org.geolatte.geom.Geometry decoded = Decoders.decode(struct);

        assertEquals(point, decoded);
    }
FDN73 commented 4 years ago

Ok, I have updated the geolatte version (the one shipped with my version of Hibernate Spatial is 1.3) and by looking at the sources, this is already fixed. Closing.