2015年4月2日木曜日

[jdbc]カレントのスキーマ名を取得する

現在の接続のカレントスキーマ名を得る方法。

Connectionインタフェースにカレントスキーマ名を取得するメソッド(getSchema)があるのでそれを使えばよい。
※getSchemaは、Java7から追加された機能。

サンプルコード

        final Connection connection = dataSource.getConnection();
        final String schema = connection.getSchema();
        System.out.println("schema = " + schema);

[Spring]MockMVCとJsonPathをつかたテストでjava.lang.NoClassDefFoundError: com/jayway/jsonpath/InvalidPathExceptionが出た場合の対処


エラー内容

java.lang.NoClassDefFoundError: com/jayway/jsonpath/InvalidPathException
 at org.springframework.test.web.servlet.result.JsonPathResultMatchers.(JsonPathResultMatchers.java:44)
 at org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath

対処方法

依存ライブラリにjson-pathを追加します。

Gradleの場合には、以下のようになります。
    testCompile "com.jayway.jsonpath:json-path:2.0.0"

2015年3月18日水曜日

JPA2.1のConverterを使ってEntityの属性の型変換

JPA2.1では、Entityの属性にConverterが設定できて、DBへの保存時や取得時に型変換を行うことができる。

使用方法


Converterクラスの作成

Converterクラスは、javax.persistence.AttributeConverterインタフェースをを実装し、javax.persistence.Converterアノテーションを設定する。
AttributeConverterの総称型には、Entityの属性の型とDBへ挿入する型を指定する。

このコードの例だと、Sex列挙型の値をStringに変換してDBに登録します。
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;

import jpa.entity.Sex;

@Converter
public class SexConverter implements AttributeConverter<Sex, String> {

    @Override
    public String convertToDatabaseColumn(Sex attribute) {
        return attribute.toString();
    }

    @Override
    public Sex convertToEntityAttribute(String dbData) {
        return Sex.valueOf(dbData);
    }
}

Entityの実装

変換対象のカラムにjavax.persistence.Convertアノテーションを指定する。
このアノテーションのconverter属性に、先ほど作成したConverterクラスを指定する。
@Entity
@Table(name = "user_info")
public class User {

    private Long id;

    private Sex sex;

    @Id
    @GeneratedValue
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @Column
    @Convert(converter = SexConverter.class)
    public Sex getSex() {
        return sex;
    }

    public void setSex(Sex sex) {
        this.sex = sex;
    }
}

実行

以下のコードで、Sex列挙型の項目がDBに文字列で保存されることを確認します。
final User user = new User();
user.setSex(Sex.MALE);
em.persist(user);
DBの値は下の結果のように列挙型の文字列表記になっています。
ID           SEX
---------- --------------------
1            MALE

2015年3月10日火曜日

PostgreSQLでシーケンスを使う

PostgreSQLでのシーケンス作成やシーケンスの操作関数の纏め。

シーケンスの作成

create sequence文を使ってシーケンスを作成する。

全てデフォルトの設定でシーケンスを作成する。(初期値は1、増分値は1、キャッシュはなしなどになる)
create sequence test_seq;

一時的な(今のセッションだけに関連づいた)シーケンスを作成する。
シーケンス作成時に、temp(temporary)を指定すると今のセッションに関連づいた一時的なシーケンスが作られる。(セッションが終了すると、自動的に破棄される。)
既に同名のシーケンスが存在していた場合、一時的なシーケンスが優先されて既にあったシーケンスは見えなくなる。
create temp sequence test_seq;

シーケンスの情報を確認する

シーケンスを検索することで、今の状態が確認できる。
select * from シーケンス名


シーケンスを操作するための関数

nextvalで次の値を取得する。
select nextval('シーケンス名')

currvalで最後にnextvalされた値を取得できる。
select currval('シーケンス名')

setvalで現在の値を変更できる。この例だと、値を999に変更している。
select setval('シーケンス名', 999)


2015年3月8日日曜日

JPA2.1でストアドプロシージャ実行-REF CURSOR編

JPA2.1でストアドプロシージャ実行の続編。
ストアドプロシージャで開いたカーソル(REF CURSOR)をJPA経由で受け取るサンプル。

実行対象のプロシージャ

パラメータは1つで、アウトパラメータでカーソルを返す。
CREATE OR REPLACE PROCEDURE REF_CURSOR_TEST(cur out SYS_REFCURSOR)
AS
BEGIN
    OPEN CUR FOR SELECT *
                 FROM USERS;
END;

実行例

EntityManagercreateStoredProcedureQueryにストアドプロシージャ名を指定してStoredProcedureQueryを生成します。
第2引数には、REF CURSORを受け取るEntityクラスを指定します。(Entity以外でもいいのか?)

executeでストアドプロシージャを実行して、getOutputParameterValueを呼び出してREF CURSORの結果を受け取ります。
ここで受け取ったEntityは管理対象となるので、状態を変更した場合は永続化層に反映される。
final StoredProcedureQuery query = em.createStoredProcedureQuery("REF_CURSOR_TEST", User.class)
         .registerStoredProcedureParameter(1, void.class, ParameterMode.REF_CURSOR);
query.execute();
List users = (List) query.getOutputParameterValue(1);

System.out.println("取得件数 = " + users.size());
users.forEach(user -> {
            System.out.println("id = " + user.getId());
            System.out.println("name = " + user.getName());
            user.setName(user.getName() + ":" + user.getId());
        }
);

実行結果

ストアドプロシージャで開いたカーソルの結果が受け取れていることがわかる。
[EL Fine]: sql: 2015-03-07 23:51:33.88--ClientSession(1620989914)--Connection(108049354)--Thread(Thread[main,5,main])--BEGIN REF_CURSOR_TEST(?); END;
 bind => [=> 1]
取得件数 = 2
id = 1
name = hoge
id = 2
name = fuga

2015年2月28日土曜日

[Oracle12C]with句でストアドプロシージャの定義

Oracle12Cでは、with句内でストアドプロシージャが定義できるようになりました。

使い方は、with句内にストアドプロシージャを定義して、それをクエリで使います。
この例では、パラメータの値を2倍するファンクションを定義して、それをselect句で使用しています。
WITH
FUNCTION func(x IN PLS_INTEGER) RETURN PLS_INTEGER
IS
BEGIN
    RETURN X * 2;
END;
SELECT func(100)
FROM DUAL;

実行結果です。ファンクションに渡した値が2され取得されているのがわかります。
SQL> r
  1  WITH
  2  FUNCTION func(x IN PLS_INTEGER) RETURN PLS_INTEGER
  3  IS
  4  BEGIN
  5  RETURN X * 2;
  6  END;
  7  SELECT func(100)
  8  FROM DUAL
  9
 10*

 FUNC(100)
----------
       200

2015年2月25日水曜日

JPA2.1でストアドプロシージャ実行

JPA2.1のストアドプロシージャの実行機能の使い方サンプル。

実行対象のプロシージャ

INパラメータを2つ受け取って、その値の合計値をアウトパラメータで返却する単純なプロシージャです。
CREATE OR REPLACE FUNCTION PUBLIC.test_function(X INT, Y INT, RET OUT INT)
AS
$BODY$
BEGIN
  RET = Y + X;
END;
$BODY$
LANGUAGE plpgsql

実行例

EntityManagercreateStoredProcedureQueryにストアドプロシージャ名を指定してStoredProcedureQueryを生成します。
StoredProcedureQueryregisterStoredProcedureParameterを使用して、パラメータ情報を設定します。(パラメータの位置、イン or アウトパラメータや型情報を設定します。)
入力パラメータの場合には、setParameterを呼び出してストアドプロシージャに引き渡す値を設定します。

実行準備が整ったら、executeでストアドプロシージャを実行します。アウトパラメータはgetOutputParameterValueを使用して取得します。

CallableStatementと使い方は近いので特に迷うこともなさそうな気がします。
final StoredProcedureQuery storedProcedureQuery = em.createStoredProcedureQuery("test_function")
        .registerStoredProcedureParameter(1, Integer.class, ParameterMode.IN)
        .setParameter(1, 10)
        .registerStoredProcedureParameter(2, Integer.class, ParameterMode.IN)
        .setParameter(2, 20)
        .registerStoredProcedureParameter(3, Integer.class, ParameterMode.OUT);
storedProcedureQuery.execute();
System.out.println("result = " + storedProcedureQuery.getOutputParameterValue(3));

NamedStoredProcedureQueryを使った場合の実行例


Entity側の準備

Entityには以下のように名前付きでストアドプロシージャの情報を定義します。
上の例で示したregisterStoredProcedureParameterの呼び出し部が、NamedStoredProcedureQueryparameters属性に設定されているイメージになります。
@NamedStoredProcedureQuery(
        name = "calc",
        procedureName = "test_function",
        parameters = {
                @StoredProcedureParameter(name = "x", type = Integer.class, mode = ParameterMode.IN),
                @StoredProcedureParameter(name = "y", type = Integer.class, mode = ParameterMode.IN),
                @StoredProcedureParameter(name = "result", type = Integer.class, mode = ParameterMode.OUT)
        })

実行分

createNamedStoredProcedureQueryにEntityで定義したストアドプロシージャクエリの名前を指定して、Queryを生成します。
生成した、Queryに対してパラメータ情報を設定して、executeを呼び出しアウトパラメータがある場合には結果を取得します。
上の例と比べると、パラメータの情報の設定がEntity側で定義されているのでなくなっています。
final StoredProcedureQuery func = em.createNamedStoredProcedureQuery("calc")
        .setParameter("x",1)
        .setParameter("y",2);
func.execute();
System.out.println("func.getOutputParameterValue(\"result\") = "+func.getOutputParameterValue(3));