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( 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( 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.
Comments