diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..1bd192a39 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +*.autofetch +*create-all.sql +*drop-all.sql +*.orig +.classpath +.project +.settings/ +target/ +logs/ +log/ +ebean-autotune.xml +ebean-profiling*.xml +/db +/mydb.db +!src/test/ddl-review/*.sql + + +# Intellij project files +*.iml +*.ipr +*.iws +.idea/ diff --git a/README.md b/README.md index 45113d597..a88d4c0b6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# ebean-postgis -Ebean support for Postgres Geometry types - org.postgis and org.geolatte.geom +# ebean-com.avaje.ebean.postgis +Ebean support for Postgres Geometry types - org.com.avaje.ebean.postgis and org.geolatte.geom diff --git a/pom.xml b/pom.xml new file mode 100644 index 000000000..4187539c0 --- /dev/null +++ b/pom.xml @@ -0,0 +1,111 @@ + + + 4.0.0 + + org.avaje.ebean + ebean-postgis + 1.1.1-SNAPSHOT + + + org.avaje + oss-parent + 1.1 + + + + 2.2.1 + 1.8 + debug=1 + + + + + + net.postgis + postgis-jdbc + [2.2.1] + + + + org.postgresql + postgresql + 9.4.1211.jre7 + provided + + + + org.geolatte + geolatte-geom + 1.0.6 + provided + + + + org.slf4j + slf4j-api + 1.7.20 + provided + + + + org.avaje.ebean + ebean + 8.5.2-SNAPSHOT + provided + + + + javax.persistence + persistence-api + 1.0 + test + + + + + + + + + + + org.avaje.composite + composite-testing + 3.1 + test + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.2 + + 1.6 + 1.6 + + + + + io.repaint.maven + tiles-maven-plugin + 2.8 + true + + + org.avaje.ebean.tile:enhancement:1.1 + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/com/avaje/ebean/postgis/RegisterFactory.java b/src/main/java/com/avaje/ebean/postgis/RegisterFactory.java new file mode 100644 index 000000000..5cf06498a --- /dev/null +++ b/src/main/java/com/avaje/ebean/postgis/RegisterFactory.java @@ -0,0 +1,32 @@ +package com.avaje.ebean.postgis; + +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebean.plugin.ExtraTypeFactory; +import com.avaje.ebean.postgis.latte.ScalarTypeGeoLatteMultiPolygon; +import com.avaje.ebean.postgis.latte.ScalarTypeGeoLattePoint; +import com.avaje.ebean.postgis.latte.ScalarTypeGeoLattePolygon; +import com.avaje.ebeaninternal.server.type.ScalarType; + +import java.util.ArrayList; +import java.util.List; + +public class RegisterFactory implements ExtraTypeFactory { + + + @Override + public List> createTypes(ServerConfig serverConfig, Object objectMapper) { + + List> list = new ArrayList>(); + list.add(new ScalarTypePgisPoint()); + list.add(new ScalarTypePgisPolygon()); + list.add(new ScalarTypePgisMultiPolygon()); + + if (serverConfig.getClassLoadConfig().isPresent("org.geolatte.geom.Geometry")) { + list.add(new ScalarTypeGeoLattePoint()); + list.add(new ScalarTypeGeoLattePolygon()); + list.add(new ScalarTypeGeoLatteMultiPolygon()); + } + + return list; + } +} diff --git a/src/main/java/com/avaje/ebean/postgis/ScalarTypePgisBase.java b/src/main/java/com/avaje/ebean/postgis/ScalarTypePgisBase.java new file mode 100644 index 000000000..1477aea7e --- /dev/null +++ b/src/main/java/com/avaje/ebean/postgis/ScalarTypePgisBase.java @@ -0,0 +1,165 @@ +package com.avaje.ebean.postgis; + +import com.avaje.ebeaninternal.server.type.CtCompoundTypeScalarList; +import com.avaje.ebeaninternal.server.type.DataBind; +import com.avaje.ebeaninternal.server.type.DataReader; +import com.avaje.ebeaninternal.server.type.ScalarType; +import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyType; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import org.postgis.Geometry; +import org.postgis.PGgeometryLW; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.SQLException; +import java.sql.Types; + +abstract class ScalarTypePgisBase implements ScalarType { + + private final int jdbcType; + + private final Class cls; + + ScalarTypePgisBase(int jdbcType, Class cls) { + this.jdbcType = jdbcType; + this.cls = cls; + } + + @Override + public void bind(DataBind bind, T value) throws SQLException { + + if (value == null) { + bind.setNull(Types.NULL); + } else { + bind.setObject(new PGgeometryLW(value)); + } + } + + + @Override + @SuppressWarnings("unchecked") + public T read(DataReader reader) throws SQLException { + + Object object = reader.getObject(); + if (object == null) { + return null; + } + PGgeometryLW gro = (PGgeometryLW)object; + Geometry pts = gro.getGeometry(); + return (T)pts; + } + + + @Override + public boolean isJdbcNative() { + return true; + } + + @Override + public int getJdbcType() { + return jdbcType; + } + + @Override + public Class getType() { + return cls; + } + + @Override + public T readData(DataInput dataInput) throws IOException { + return null; + } + + @Override + public void writeData(DataOutput dataOutput, T v) throws IOException { + + } + + @Override + public boolean isBinaryType() { + return false; + } + + @Override + public boolean isMutable() { + return false; + } + + @Override + public boolean isDirty(Object value) { + return false; + } + + @Override + public int getLength() { + return 0; + } + + @Override + public void loadIgnore(DataReader reader) { + + } + + @Override + public void accumulateScalarTypes(String propName, CtCompoundTypeScalarList list) { + + } + + @Override + public Object toJdbcType(Object value) { + return null; + } + + @Override + public T toBeanType(Object value) { + return null; + } + + @Override + public String formatValue(T value) { + return null; + } + + @Override + public String format(Object value) { + return null; + } + + @Override + public T parse(String value) { + return null; + } + + @Override + public DocPropertyType getDocType() { + return null; + } + + @Override + public boolean isDateTimeCapable() { + return false; + } + + @Override + public long asVersion(T value) { + return 0; + } + + @Override + public T convertFromMillis(long dateTime) { + return null; + } + + + @Override + public T jsonRead(JsonParser parser) throws IOException { + return null; + } + + @Override + public void jsonWrite(JsonGenerator writer, T value) throws IOException { + + } +} diff --git a/src/main/java/com/avaje/ebean/postgis/ScalarTypePgisMultiPolygon.java b/src/main/java/com/avaje/ebean/postgis/ScalarTypePgisMultiPolygon.java new file mode 100644 index 000000000..a6eb9d7c8 --- /dev/null +++ b/src/main/java/com/avaje/ebean/postgis/ScalarTypePgisMultiPolygon.java @@ -0,0 +1,12 @@ +package com.avaje.ebean.postgis; + +import com.avaje.ebean.config.dbplatform.ExtraDbTypes; +import org.postgis.MultiPolygon; + +public class ScalarTypePgisMultiPolygon extends ScalarTypePgisBase { + + public ScalarTypePgisMultiPolygon() { + super(ExtraDbTypes.POLYGON, MultiPolygon.class); + } + +} diff --git a/src/main/java/com/avaje/ebean/postgis/ScalarTypePgisPoint.java b/src/main/java/com/avaje/ebean/postgis/ScalarTypePgisPoint.java new file mode 100644 index 000000000..1294ce7bb --- /dev/null +++ b/src/main/java/com/avaje/ebean/postgis/ScalarTypePgisPoint.java @@ -0,0 +1,12 @@ +package com.avaje.ebean.postgis; + +import com.avaje.ebean.config.dbplatform.ExtraDbTypes; +import org.postgis.Point; + +public class ScalarTypePgisPoint extends ScalarTypePgisBase { + + public ScalarTypePgisPoint() { + super(ExtraDbTypes.POINT, Point.class); + } + +} diff --git a/src/main/java/com/avaje/ebean/postgis/ScalarTypePgisPolygon.java b/src/main/java/com/avaje/ebean/postgis/ScalarTypePgisPolygon.java new file mode 100644 index 000000000..07cf01329 --- /dev/null +++ b/src/main/java/com/avaje/ebean/postgis/ScalarTypePgisPolygon.java @@ -0,0 +1,11 @@ +package com.avaje.ebean.postgis; + +import com.avaje.ebean.config.dbplatform.ExtraDbTypes; +import org.postgis.Polygon; + +public class ScalarTypePgisPolygon extends ScalarTypePgisBase { + + public ScalarTypePgisPolygon() { + super(ExtraDbTypes.POLYGON, Polygon.class); + } +} diff --git a/src/main/java/com/avaje/ebean/postgis/latte/ScalarTypeGeoLatteBase.java b/src/main/java/com/avaje/ebean/postgis/latte/ScalarTypeGeoLatteBase.java new file mode 100644 index 000000000..b95f0d654 --- /dev/null +++ b/src/main/java/com/avaje/ebean/postgis/latte/ScalarTypeGeoLatteBase.java @@ -0,0 +1,168 @@ +package com.avaje.ebean.postgis.latte; + +import com.avaje.ebeaninternal.server.type.CtCompoundTypeScalarList; +import com.avaje.ebeaninternal.server.type.DataBind; +import com.avaje.ebeaninternal.server.type.DataReader; +import com.avaje.ebeaninternal.server.type.ScalarType; +import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyType; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import org.geolatte.geom.Geometry; +import org.geolatte.geom.codec.Wkt; +import org.postgis.PGgeometry; +import org.postgis.PGgeometryLW; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.sql.SQLException; +import java.sql.Types; + +abstract class ScalarTypeGeoLatteBase implements ScalarType { + + private final int jdbcType; + + private final Class cls; + + ScalarTypeGeoLatteBase(int jdbcType, Class cls) { + this.jdbcType = jdbcType; + this.cls = cls; + } + + @Override + public boolean isJdbcNative() { + return true; + } + + @Override + public int getJdbcType() { + return jdbcType; + } + + @Override + public Class getType() { + return cls; + } + + @Override + public void bind(DataBind bind, T value) throws SQLException { + + if (value == null) { + bind.setNull(Types.OTHER); + } else { + String wkt = Wkt.newEncoder(Wkt.Dialect.POSTGIS_EWKT_1).encode(value); + bind.setObject(new PGgeometryLW(wkt)); + } + } + + + @Override + @SuppressWarnings("unchecked") + public T read(DataReader reader) throws SQLException { + + Object object = reader.getObject(); + if (object == null) { + return null; + } + if (object instanceof PGgeometry) { + org.postgis.Geometry geometry = ((PGgeometry) object).getGeometry(); + return (T) Wkt.newDecoder(Wkt.Dialect.POSTGIS_EWKT_1).decode(geometry.toString()); + } + throw new IllegalStateException("Received object of type " + object.getClass().getCanonicalName()); + } + + @Override + public T readData(DataInput dataInput) throws IOException { + return null; + } + + @Override + public void writeData(DataOutput dataOutput, T v) throws IOException { + + } + + @Override + public boolean isBinaryType() { + return false; + } + + @Override + public boolean isMutable() { + return false; + } + + @Override + public boolean isDirty(Object value) { + return false; + } + + @Override + public int getLength() { + return 0; + } + + @Override + public void loadIgnore(DataReader reader) { + + } + + @Override + public void accumulateScalarTypes(String propName, CtCompoundTypeScalarList list) { + + } + + @Override + public Object toJdbcType(Object value) { + return null; + } + + @Override + public T toBeanType(Object value) { + return null; + } + + @Override + public String formatValue(T value) { + return null; + } + + @Override + public String format(Object value) { + return null; + } + + @Override + public T parse(String value) { + return null; + } + + @Override + public DocPropertyType getDocType() { + return null; + } + + @Override + public boolean isDateTimeCapable() { + return false; + } + + @Override + public long asVersion(T value) { + return 0; + } + + @Override + public T convertFromMillis(long dateTime) { + return null; + } + + @Override + public T jsonRead(JsonParser parser) throws IOException { + return null; + } + + @Override + public void jsonWrite(JsonGenerator writer, T value) throws IOException { + + } +} diff --git a/src/main/java/com/avaje/ebean/postgis/latte/ScalarTypeGeoLatteMultiPolygon.java b/src/main/java/com/avaje/ebean/postgis/latte/ScalarTypeGeoLatteMultiPolygon.java new file mode 100644 index 000000000..0d7000a2b --- /dev/null +++ b/src/main/java/com/avaje/ebean/postgis/latte/ScalarTypeGeoLatteMultiPolygon.java @@ -0,0 +1,12 @@ +package com.avaje.ebean.postgis.latte; + +import com.avaje.ebean.config.dbplatform.ExtraDbTypes; +import org.geolatte.geom.MultiPolygon; + +public class ScalarTypeGeoLatteMultiPolygon extends ScalarTypeGeoLatteBase { + + public ScalarTypeGeoLatteMultiPolygon() { + super(ExtraDbTypes.MULTIPOLYGON, MultiPolygon.class); + } + +} diff --git a/src/main/java/com/avaje/ebean/postgis/latte/ScalarTypeGeoLattePoint.java b/src/main/java/com/avaje/ebean/postgis/latte/ScalarTypeGeoLattePoint.java new file mode 100644 index 000000000..a196a6fc4 --- /dev/null +++ b/src/main/java/com/avaje/ebean/postgis/latte/ScalarTypeGeoLattePoint.java @@ -0,0 +1,11 @@ +package com.avaje.ebean.postgis.latte; + +import com.avaje.ebean.config.dbplatform.ExtraDbTypes; +import org.geolatte.geom.Point; + +public class ScalarTypeGeoLattePoint extends ScalarTypeGeoLatteBase { + + public ScalarTypeGeoLattePoint() { + super(ExtraDbTypes.POINT, Point.class); + } +} diff --git a/src/main/java/com/avaje/ebean/postgis/latte/ScalarTypeGeoLattePolygon.java b/src/main/java/com/avaje/ebean/postgis/latte/ScalarTypeGeoLattePolygon.java new file mode 100644 index 000000000..069e31b56 --- /dev/null +++ b/src/main/java/com/avaje/ebean/postgis/latte/ScalarTypeGeoLattePolygon.java @@ -0,0 +1,11 @@ +package com.avaje.ebean.postgis.latte; + +import com.avaje.ebean.config.dbplatform.ExtraDbTypes; +import org.geolatte.geom.Polygon; + +public class ScalarTypeGeoLattePolygon extends ScalarTypeGeoLatteBase { + + public ScalarTypeGeoLattePolygon() { + super(ExtraDbTypes.POLYGON, Polygon.class); + } +} diff --git a/src/main/resources/META-INF/services/com.avaje.ebean.plugin.ExtraTypeFactory b/src/main/resources/META-INF/services/com.avaje.ebean.plugin.ExtraTypeFactory new file mode 100644 index 000000000..58c0418c2 --- /dev/null +++ b/src/main/resources/META-INF/services/com.avaje.ebean.plugin.ExtraTypeFactory @@ -0,0 +1 @@ +com.avaje.ebean.postgis.RegisterFactory \ No newline at end of file diff --git a/src/test/java/org/example/domain/BaseEntity.java b/src/test/java/org/example/domain/BaseEntity.java new file mode 100644 index 000000000..410fe38f9 --- /dev/null +++ b/src/test/java/org/example/domain/BaseEntity.java @@ -0,0 +1,20 @@ +package org.example.domain; + +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; + +@MappedSuperclass +abstract class BaseEntity { + + @Id + Long id; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + +} diff --git a/src/test/java/org/example/domain/MyBean.java b/src/test/java/org/example/domain/MyBean.java new file mode 100644 index 000000000..ae8a77cd6 --- /dev/null +++ b/src/test/java/org/example/domain/MyBean.java @@ -0,0 +1,54 @@ +package org.example.domain; + + +import org.postgis.MultiPolygon; +import org.postgis.Point; +import org.postgis.Polygon; + +import javax.persistence.Entity; +import javax.persistence.Table; + +@Entity +@Table(name="mybean") +public class MyBean extends BaseEntity { + + String name; + + Point point; + + Polygon poly; + + MultiPolygon mpoly; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Point getPoint() { + return point; + } + + public void setPoint(Point point) { + this.point = point; + } + + public Polygon getPoly() { + return poly; + } + + public void setPoly(Polygon poly) { + this.poly = poly; + } + + public MultiPolygon getMpoly() { + return mpoly; + } + + public void setMpoly(MultiPolygon mpoly) { + this.mpoly = mpoly; + } +} diff --git a/src/test/java/org/example/domain/OtherBeanGeoLatte.java b/src/test/java/org/example/domain/OtherBeanGeoLatte.java new file mode 100644 index 000000000..b72f28e12 --- /dev/null +++ b/src/test/java/org/example/domain/OtherBeanGeoLatte.java @@ -0,0 +1,54 @@ +package org.example.domain; + + +import org.geolatte.geom.MultiPolygon; +import org.geolatte.geom.Point; +import org.geolatte.geom.Polygon; + +import javax.persistence.Entity; +import javax.persistence.Table; + +@Entity +@Table(name="mybean") +public class OtherBeanGeoLatte extends BaseEntity { + + String name; + + Point point; + + Polygon poly; + + MultiPolygon mpoly; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Point getPoint() { + return point; + } + + public void setPoint(Point point) { + this.point = point; + } + + public Polygon getPoly() { + return poly; + } + + public void setPoly(Polygon poly) { + this.poly = poly; + } + + public MultiPolygon getMpoly() { + return mpoly; + } + + public void setMpoly(MultiPolygon mpoly) { + this.mpoly = mpoly; + } +} diff --git a/src/test/java/org/example/domain/TestInsertQuery.java b/src/test/java/org/example/domain/TestInsertQuery.java new file mode 100644 index 000000000..63fd8561f --- /dev/null +++ b/src/test/java/org/example/domain/TestInsertQuery.java @@ -0,0 +1,40 @@ +package org.example.domain; + +import com.avaje.ebean.Ebean; +import org.postgis.Point; +import org.postgis.Polygon; +import org.testng.annotations.Test; + +import java.sql.SQLException; +import java.util.List; + +public class TestInsertQuery { + + @Test + public void insert() throws SQLException { + + + List list = Ebean.find(MyBean.class).findList(); + for (MyBean MyBean : list) { + System.out.println(MyBean.getPoint()); + } + + List list1 = Ebean.find(MyBean.class) + .where() + .raw("st_within(st_pointfromwkb(st_point(?, ?), 4674), poly)", 1.9, 1.9) + .findList(); + + System.out.println(list1); + + + Point point = new Point("SRID=4674;POINT(2.8 1.7)"); + Polygon poly = new Polygon("SRID=4674;POLYGON((2 2, 2 -2, -2 -2, -2 2, 2 2))"); + + MyBean p = new MyBean(); + p.setName("me at "+System.currentTimeMillis()); + p.setPoint(point); + p.setPoly(poly); + + Ebean.save(p); + } +} diff --git a/src/test/java/org/example/domain/TestOtherInsertQuery.java b/src/test/java/org/example/domain/TestOtherInsertQuery.java new file mode 100644 index 000000000..496132faa --- /dev/null +++ b/src/test/java/org/example/domain/TestOtherInsertQuery.java @@ -0,0 +1,41 @@ +package org.example.domain; + +import com.avaje.ebean.Ebean; +import org.geolatte.geom.Point; +import org.geolatte.geom.Polygon; +import org.geolatte.geom.codec.Wkt; +import org.testng.annotations.Test; + +import java.sql.SQLException; +import java.util.List; + +public class TestOtherInsertQuery { + + @Test + public void insert() throws SQLException { + + + List list = Ebean.find(OtherBeanGeoLatte.class).findList(); + for (OtherBeanGeoLatte OtherBeanGeoLatte : list) { + System.out.println(OtherBeanGeoLatte.getPoint()); + } + + List list1 = Ebean.find(OtherBeanGeoLatte.class) + .where() + .raw("st_within(st_pointfromwkb(st_point(?, ?), 4674), poly)", 1.9, 1.9) + .findList(); + + System.out.println(list1); + + + Point point = (Point) Wkt.fromWkt("SRID=4674;POINT (12 23)"); + Polygon poly = (Polygon) Wkt.fromWkt("SRID=4674;POLYGON((2 2, 2 -2, -2 -2, -2 2, 2 2))"); + + OtherBeanGeoLatte p = new OtherBeanGeoLatte(); + p.setName("geolatte at "+System.currentTimeMillis()); + p.setPoint(point); + p.setPoly(poly); + + Ebean.save(p); + } +} diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml new file mode 100644 index 000000000..e4ad1ed0d --- /dev/null +++ b/src/test/resources/logback-test.xml @@ -0,0 +1,39 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + log/application.log + + log/application.%d{yyyy-MM-dd}.log + 90 + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/test-ebean.properties b/src/test/resources/test-ebean.properties new file mode 100644 index 000000000..d71a5a501 --- /dev/null +++ b/src/test/resources/test-ebean.properties @@ -0,0 +1,18 @@ + +## when running tests + +ebean.ddl.generate=true +ebean.ddl.run=true +#ebean.ddl.createOnly=true + +ebean.geometrySRID=4674 + +datasource.default=db + +datasource.db.username=pgis +datasource.db.password=pgis +datasource.db.databaseUrl=jdbc:postgresql_lwgis://127.0.0.1:5432/pgis +datasource.db.databaseDriver=org.postgis.DriverWrapperLW + + +