Member Menu
 
 Monthly JBoss newsletter:
 
Java Persistence with Hibernate
CaveatEmptor

Mapping Spatial Oracle type SDO_GEOMETRY to JGeometry

After a lot of looking around and tons of false starts I've finally hit upon the ability to work with SDO_GEOMETRY Oracle spatial types. I can convert them within the Hibernate EJB3 framework to JGeometry Java types by using a org.hibernate.usertypes.UserType implementation and specialized Dialect.

I've also added in some helper functionality to do JGeometry.equals comparisons.

  • Step1 - Specify the OracleSpatialDialect as the hibernate.dialect in your Hibernate configuration.
  • Step2 - If using the reverse engineering be sure to specify your table.column type as JGeometryType

Code

Example Hibernate Reverse Engineering File

<hibernate-reverse-engineering>

<table-filter match-name="TIDGET_HEADER" />
    <table-filter match-name="TIDGET_DATA" />
    <table-filter match-name="TIDGET_DEVICE" />
    <table-filter match-name="GATEWAY_DATA" />
    <table-filter match-name="GATEWAY_ID" />
    <table-filter match-name="TIDGET_TIME" />
    <table name="GATEWAY_ID">
        <column name="GATEWAY_POSITION" type="com.navsys.spatial.JGeometryType" exclude="false"/>
    </table>
</hibernate-reverse-engineering>

Custom UserType for wrapping JGeometry

package com.navsys.spatial;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Arrays;
import oracle.jdbc.driver.OracleConnection;
import oracle.spatial.geometry.JGeometry;
import oracle.sql.STRUCT;
import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType;

/**
 * This object is a UserType that represents an SDO_GEOMETRY type for use with
 * Oracle 10g databases and the Oracle Spatial Libraries
 * 
 * It represents an SDO_GEOMETRY database type by wrapping the
 * oracle.spatial.geometry.JGeometry type and implementing
 * org.hibernate.usertype.UserType.
 * 
 * This class should be used with the OracleSpacialDialect class.
 * 
 * (NOTE: I tried just extending the JGeometry instead of aggregating it, that
 * doesn't work. The static load returns a JGeometry that can't be cast to the
 * sub-class)
 * 
 * @author Joel Schuster - The NAVSYS Corporation
 * @version 1.0
 * @see com.navsys.spatial.OracleSpacialDialect
 * 
 */
public class JGeometryType implements UserType, Serializable {
    private static final long serialVersionUID = 1L;
    private JGeometry geometryInstance = null;

    /**
     * This default constructor does create an instance of 1 point at origin
     */
    public JGeometryType() {
        geometryInstance = new JGeometry( 0, 0, 0);
    }

    public JGeometryType( JGeometry geometryInstance) {
        this.geometryInstance = geometryInstance;
    }

    public JGeometryType( JGeometryType geometryType) {
        this.geometryInstance = geometryType.getJGeometry();
    }
    /* User Type Information */
    /*
     * Note that the type that is reported is OTHER (1111) not STRUCT (2002),
     * see: org.hibernate.dialect.Dialect
     */
    private static final int[] SQL_TYPES = { Types.OTHER };

    public int[] sqlTypes() {
        return SQL_TYPES;
    }

    public Class returnedClass() {
        return JGeometryType.class;
    }

    /**
     * This method gives back the equals functionality that was deprecated by
     * using the equals that's needed for the UserType
     */
    @Override
    public boolean equals( Object arg0) {
        return equals( this, arg0);
    }

    public boolean equals( Object obj1, Object obj2) throws HibernateException {
        /* check we are dealing with non-null objects of the correct type */
        if( obj1 instanceof JGeometryType && obj2 instanceof JGeometryType && obj1 != null && obj2 != null) {
            JGeometry geo1 = ( (JGeometryType) obj1).getJGeometry();
            JGeometry geo2 = ( (JGeometryType) obj2).getJGeometry();
            /* check that they are the same type */
            if( geo1.getType() != geo2.getType()) {
                return false;
            }
            /* go through the different types and check for equality */
            switch( geo1.getType()) {
                case JGeometry.GTYPE_POINT: {
                    try {
                        return geo1.getJavaPoint().equals( geo2.getJavaPoint());
                    } catch( NullPointerException npe) {
                        /*
                         * one of the points is null, this is different type
                         * than the reported
                         */
                        return false;
                    }
                }
                case JGeometry.GTYPE_MULTIPOINT: {
                    try {
                        return Arrays.equals( geo1.getJavaPoints(), geo1.getJavaPoints());
                    } catch( NullPointerException npe) {
                        /*
                         * one of the points arrays is null, this is different
                         * type than the reported
                         */
                        return false;
                    }
                }
                    /*
                     * For lack of a better way of doing comparisons right
                     * now... Feel free to implement better comparisons
                     */
                case JGeometry.GTYPE_MULTICURVE:
                case JGeometry.GTYPE_MULTIPOLYGON:
                case JGeometry.GTYPE_POLYGON:
                case JGeometry.GTYPE_CURVE: {
                    return Arrays.equals( geo1.getOrdinatesOfElements(), geo2.getOrdinatesOfElements());
                }
                default: {
                    return false;
                }
            }
        } else {
            return false;
        }
    }

    public int hashCode( Object o) throws HibernateException {
        return ( (JGeometryType) o).hashCode();
    }

    /* calls the load method */
    public Object nullSafeGet( ResultSet resultSet, String[] strings, Object o) throws HibernateException, SQLException {
        STRUCT geometry = (STRUCT) resultSet.getObject( strings[0]);
        JGeometry jg = JGeometry.load( geometry);
        return resultSet.wasNull() ? null : new JGeometryType( jg);
    }

    /* calls the store method */
    public void nullSafeSet( PreparedStatement preparedStatement, Object o, int i) throws HibernateException, SQLException {
        if( o == null) {
            preparedStatement.setNull( i, Types.OTHER);
        } else {
            if( o instanceof JGeometryType) {
                OracleConnection oc = (OracleConnection) preparedStatement.getConnection();
                STRUCT struct = JGeometry.store( (JGeometry) ( (JGeometryType) o).getJGeometry(), oc);
                preparedStatement.setObject( i, struct);
            }
        }
    }

    /* uses the 'copy' constructor */
    public Object deepCopy( Object o) throws HibernateException {
        if( o == null)
            return null;
        if( o instanceof JGeometryType) {
            return new JGeometryType( ( (JGeometryType) o).getJGeometry());
        } else {
            return null;
        }
    }

    public boolean isMutable() {
        return false;
    }

    public Serializable disassemble( Object o) throws HibernateException {
        return (Serializable) deepCopy( o);
    }

    public Object assemble( Serializable serializable, Object o) throws HibernateException {
        return deepCopy( serializable);
    }

    public Object replace( Object o, Object o1, Object o2) throws HibernateException {
        return (JGeometryType) o;
    }

    /* accessor */
    public JGeometry getJGeometry() {
        return geometryInstance;
    }

    /*
     * Helpers so you don't have to work directly with JGeometry if you don't
     * want Add to these as needed
     */
    /* JGeometry Constructors */
    public JGeometryType( double minX, double minY, double maxX, double maxY, int srid) {
        geometryInstance = new JGeometry( minX, minY, maxX, maxY, srid);
    }

    public JGeometryType( double x, double y, double z, int srid) {
        geometryInstance = new JGeometry( x, y, z, srid);
    }

    public JGeometryType( double x, double y, int srid) {
        geometryInstance = new JGeometry( x, y, srid);
    }

    public JGeometryType( int gtype, int srid, double x, double y, double z, int[] elemInfo, double[] ordinates) {
        geometryInstance = new JGeometry( gtype, srid, x, y, z, elemInfo, ordinates);
    }

    public JGeometryType( int gtype, int srid, int[] elemInfo, double[] ordinates) {
        geometryInstance = new JGeometry( gtype, srid, elemInfo, ordinates);
    }

    static public JGeometryType createCircle( double x1, double y1, double x2, double y2, double x3, double y3, int srid) {
        return new JGeometryType( JGeometry.createCircle( x1, y1, x2, y2, x3, y3, srid));
    }

    static public JGeometryType createCircle( double x, double y, double radius, int srid) {
        return new JGeometryType( JGeometry.createCircle( x, y, radius, srid));
    }

    static public JGeometryType createLinearLineString( double[] coords, int dim, int srid) {
        return new JGeometryType( JGeometry.createLinearLineString( coords, dim, srid));
    }

    static public JGeometryType createLinearMultiLineString( java.lang.Object[] coords, int dim, int srid) {
        return new JGeometryType( JGeometry.createLinearMultiLineString( coords, dim, srid));
    }

    static public JGeometryType createLinearPolygon( double[] coords, int dim, int srid) {
        return new JGeometryType( JGeometry.createLinearPolygon( coords, dim, srid));
    }

    static public JGeometryType createLinearPolygon( Object[] coords, int dim, int srid) {
        return new JGeometryType( JGeometry.createLinearPolygon( coords, dim, srid));
    }

    static public JGeometryType createMultiPoint( java.lang.Object[] coords, int dim, int srid) {
        return new JGeometryType( JGeometry.createMultiPoint( coords, dim, srid));
    }

    static public JGeometryType createPoint( double[] coord, int dim, int srid) {
        return new JGeometryType( JGeometry.createPoint( coord, dim, srid));
    }

    /* not really overrides, but helpers */
    static public double[] computeArc( double x1, double y1, double x2, double y2, double x3, double y3) {
        return JGeometry.computeArc( x1, y1, x2, y2, x3, y3);
    }

    public java.awt.Shape createShape() {
        return geometryInstance.createShape();
    }

    public int getDimensions() {
        return geometryInstance.getDimensions();
    }

    public int[] getElemInfo() {
        return geometryInstance.getElemInfo();
    }

    public double[] getFirstPoint() {
        return geometryInstance.getFirstPoint();
    }

    public java.awt.geom.Point2D getJavaPoint() {
        return geometryInstance.getJavaPoint();
    }

    public java.awt.geom.Point2D[] getJavaPoints() {
        return geometryInstance.getJavaPoints();
    }

    public java.awt.geom.Point2D getLabelPoint() {
        return geometryInstance.getLabelPoint();
    }

    public double[] getLastPoint() {
        return geometryInstance.getLastPoint();
    }

    public double[] getMBR() {
        return geometryInstance.getMBR();
    }

    public int getNumPoints() {
        return geometryInstance.getNumPoints();
    }

    public double[] getOrdinatesArray() {
        return geometryInstance.getOrdinatesArray();
    }

    public Object[] getOrdinatesOfElements() {
        return geometryInstance.getOrdinatesOfElements();
    }

    public double[] getPoint() {
        return geometryInstance.getPoint();
    }

    public long getSize() {
        return geometryInstance.getSize();
    }

    public int getSRID() {
        return geometryInstance.getSRID();
    }

    public int getType() {
        return geometryInstance.getType();
    }

    public boolean hasCircularArcs() {
        return geometryInstance.hasCircularArcs();
    }

    public boolean isCircle() {
        return geometryInstance.isCircle();
    }

    public boolean isGeodeticMBR() {
        return geometryInstance.isGeodeticMBR();
    }

    public boolean isLRSGeometry() {
        return geometryInstance.isLRSGeometry();
    }

    public boolean isMultiPoint() {
        return geometryInstance.isMultiPoint();
    }

    public boolean isPoint() {
        return geometryInstance.isPoint();
    }

    public boolean isRectangle() {
        return geometryInstance.isRectangle();
    }

    public static double[] linearizeArc( double x1, double y1, double x2, double y2, double x3, double y3) {
        return JGeometry.linearizeArc( x1, y1, x2, y2, x3, y3);
    }

    public static double[] linearizeArc( double x1, double y1, double x2, double y2, double x3, double y3, double tolerance) {
        return JGeometry.linearizeArc( x1, y1, x2, y2, x3, y3, tolerance);
    }

    public static double[] linearizeArc( double x1, double y1, double x2, double y2, double x3, double y3, int numPoints) {
        return JGeometry.linearizeArc( x1, y1, x2, y2, x3, y3, numPoints);
    }

    public void setSRID( int srid) {
        geometryInstance.setSRID( srid);
    }

    public void setType( int gt) {
        geometryInstance.setType( gt);
    }
}

Custom Dialect for specifying SDO_GEOMETRY

package com.navsys.spatial;

import java.sql.Types;
import org.hibernate.dialect.Oracle9Dialect;

/**
 * This class extends the Oracle9Dialect for Hibernate by adding the
 * sdo_geometry type as an sql.OTHER type.
 * 
 * @author Joel Schuster - The NAVSYS Corporation
 * @version 1.0
 * @see com.navsys.spatial.OracleSpacialDialect
 * 
 */
public class OracleSpatialDialect extends Oracle9Dialect {
    public OracleSpatialDialect() {
        super();
        registerColumnType( Types.OTHER, "sdo_geometry");
    }
}


Questions?

mailto:guurk (at) plasmapowered (dot) com

or

mailto:joels (at) navsys (dot) com

Enjoy.

© Copyright 2006, Red Hat Middleware, LLC. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc. [Privacy Policy]