s2jdbcでPostgreSQLの幾何データ型を使う
初めて、s2jdbc-genとs2jdbcを使用してみた。
変更履歴
2009-08-26 「最低限やること」「PostgreDialectを拡張オーバーライド」を修正
postgreSQLのデータ型
PostgreSQLにはデータ型として幾何データ型がある。その幾何データ型は、S2JDBCが標準でサポートしている永続プロパティの型とは異なるため、
s2jdbc-genデフォルトで作成した場合は、String型の扱いとなる。
それを、postgreSQLのjdbcドライバで用意している型にするには?ということでちょっと模索してみた。
PostgreSQL JDBCドライバが返すメタデータのように、point、line、box、path、polygon、circleといった型をjdbcドライバが用意する型として扱いたい。
Seasar-user:11711 S2JDBC 型マッピングのカスタマイズについて
Seasar-user:11718 Re: S2JDBC 型マッピングのカスタマイズについて
といった情報があるので参考にしてみた。
- 使用している主なライブラリのバージョン
- postgresql-8.3-605.jdbc4.jar(jre1.6用)
- s2-extension-2.4.39.jar
- s2-framework-2.4.39.jar
- s2-tiger-2.4.39.jar
- s2jdbc-gen-2.4.39.jar
s2jdbcの処理
SQLの結果をエンティティにセットするorg.seasar.extension.jdbc.query.AbstractQuery<S extends Query<S>> #handleResultSet(ResultSetHandler handler, ResultSet rs)
の
ret = handler.handle(rs);
で、エンティティクラスのプロパティにセットされる。
エンティティ(カラム)に対応する型情報は、
org.seasar.extension.jdbc.dialect.PostgreDialect#getValueType(PropertyMeta propertyMeta)
により判断される。
ということで、point型を試しにやってみる。
最低限やること
- PostgreDialectを拡張
- #getValueType(PropertyMeta propertyMeta)をオーバーライド
- 自動生成で実行されるときに使われる?
- #getValueType(Class clazz, boolean lob, TemporalType temporalType) をオーバーライド
- SQLファイルに記述したときに使われる?
- 幾何データ型で検索する場合など、自動生成で使用することは まれ だと思われるのでこちらを主に使うようになるのではないかな。
- #getValueType(PropertyMeta propertyMeta)をオーバーライド
- point、line、boxといった型に対応したValueTypeを実装したクラスを作成する
- getValueTypeで、pointやline、boxそれぞれにあったValueTypeを返却する。
- エンティティでpoint型を定義
- jdbc内の型でプロパティを宣言する(自動生成されたStringから変更)
- dialectを変更する
- s2jdbcの設定を変更
PostgreDialectを拡張して、オーバーライド
@Override public ValueType getValueType(PropertyMeta propertyMeta) { final Class<?> clazz = propertyMeta.getPropertyClass(); if (propertyMeta.isLob()) { return super.getValueType(propertyMeta); } else { final ValueType valueType = getGeometryValueType(clazz); if (valueType != null) { return valueType; } } final ValueType valueType = getValueTypeInternal(clazz); if (valueType != null) { return valueType; } return super.getValueType(propertyMeta); } @Override public ValueType getValueType(Class<?> clazz, boolean lob, TemporalType temporalType) { ValueType valueType = getGeometryValueType(clazz); if (valueType != null) { return valueType; } return super.getValueType(clazz, lob, temporalType); } protected ValueType getGeometryValueType(final Class<?> clazz) { if (clazz == PGpolygon.class) { System.out.println("★polygon---★"); return PostgresValueTypes.PGPOLYGON; } else if (clazz == PGbox.class) { System.out.println("■box---■"); return PostgresValueTypes.PGBOX; } else if (clazz == PGpoint.class) { System.out.println("▲point---▲"); return PostgresValueTypes.PGPOINT; } return null; }
point型に対応したValueTypeの作成
public class PGpointType extends AbstractValueType { public PGpointType() { super(Types.OTHER); } @Override public void bindValue(PreparedStatement ps, int index, Object value) throws SQLException { if (value == null) { setNull(ps, index); } else { ps.setObject(index, value); } } @Override public void bindValue(CallableStatement cs, String parameterName, Object value) throws SQLException { throw new UnsupportedOperationException("未実装"); } @Override public Object getValue(ResultSet resultSet, int index) throws SQLException { return (PGpointType) (resultSet.getObject(index)); } @Override public Object getValue(ResultSet resultSet, String columnName) throws SQLException { return (PGpointType) (resultSet.getObject(columnName)); } @Override public Object getValue(CallableStatement cs, int index) throws SQLException { throw new UnsupportedOperationException("未実装"); } @Override public Object getValue(CallableStatement cs, String parameterName) throws SQLException { throw new UnsupportedOperationException("未実装"); } @Override public String toText(Object value) { if (value == null) { return BindVariableUtil.nullText(); } PGpointType var = (PGpointType) (value); return BindVariableUtil.toText(var); } }
PostgreSQL専用の値タイプのファクトリ
public class PostgresValueTypes { public final static ValueType PGPOINT = new PGpointType(); }
テーブルのエンティティ
import org.postgresql.geometric.PGpoint; @Entity @Generated(value = {"S2JDBC-Gen 2.4.39", "org.seasar.extension.jdbc.gen.internal.model.EntityModelFactoryImpl"}, date = "2009/08/19 12:00:00") public class TblXxxEntity implements Serializable { private static final long serialVersionUID = 1L; /** pointxyプロパティ */ // @Column(length = 2147483647, nullable = true, unique = false) // デフォルトだとString型で定義される public PGpoint pointxy; }
作成したdialectを使用するように設定
s2jdbc.diconの設定でPostgreDialectを拡張したクラスを使うように設定<components> <include path="jdbc.dicon"/> <include path="s2jdbc-internal.dicon"/> <component name="samplePostgreDialect" class="sample.jdbc.dialect.SamplePostgreDialect"> </component> <component name="jdbcManager" class="org.seasar.extension.jdbc.manager.JdbcManagerImpl"> <property name="maxRows">0</property> <property name="fetchSize">0</property> <property name="queryTimeout">0</property> <property name="dialect">samplePostgreDialect</property> </component> </components>
やってみたは良いが、、【課題】
とりあえず、やってみたが どうなんだろ。PGpointType extends AbstractValueType のように、polygonやboxもそれぞれ定義する必要がある。
ValueTypeの実装が かなり不安だ。。。
#getValue(ResultSet resultSet, int index)
#getValue(ResultSet resultSet, String columnName)
は、何とかなるが、
#bindValue()
#getValue(CallableStatement cs, int index)
#getValue(CallableStatement cs, String parameterName)
などが どういった場合に呼ばれるのか?
#toText(Object value)
については、それぞれの幾何データ型が カンマ区切りや()付きで格納されていそうなので
それを考慮する必要がありそうかな。