psql上では、\eで前回実行したSQL文をエディタ上で編集できるようになる。
エディターは、EDITOR環境変数に設定されたパスのものが開かれる。
2015年12月15日火曜日
2015年12月14日月曜日
MySQLは ON UPDATE CURRENT_TIMESTAMPで更新日時を自動的に更新できる
MySQLのタイムスタンプ型では、DEFAULT句に追加でon updateをつけることで更新時にも日時を自動設定できるらしいので試してみた。
試した時に使ったバージョンはMySQL Communityの5.7.10です。
update時には、カラムの値が変更されている場合のみ、対象カラムのtaimustampが変更されている。
なお、insertやupdate時に任意の値を直接設定することもできる。
試した時に使ったバージョンはMySQL Communityの5.7.10です。
DDLとテーブル定義
テーブル定義を見ると、on updateを指定したカラムはExtra列で確認することができる。create table test_table ( id int AUTO_INCREMENT, name VARCHAR(100) NOT NULL, last_modified TIMESTAMP NOT NULL DEFAULT current_timestamp ON UPDATE current_timestamp, PRIMARY KEY (id) ) mysql> desc test_table; +---------------+--------------+------+-----+-------------------+-----------------------------+ | Field | Type | Null | Key | Default | Extra | +---------------+--------------+------+-----+-------------------+-----------------------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | varchar(100) | NO | | NULL | | | last_modified | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP | +---------------+--------------+------+-----+-------------------+-----------------------------+
結果
insertやupdate時に対象のカラムを指定しないと、自動的にcurrent_timestampの値が入ることがわかる。update時には、カラムの値が変更されている場合のみ、対象カラムのtaimustampが変更されている。
なお、insertやupdate時に任意の値を直接設定することもできる。
-- INSERT mysql> insert into test_table (name) values ('hoge'); Query OK, 1 row affected (0.07 sec) -- 自動的にtimestampが設定される。 mysql> select * from test_table; +----+------+---------------------+ | id | name | last_modified | +----+------+---------------------+ | 1 | hoge | 2015-12-14 12:24:09 | +----+------+---------------------+ 1 row in set (0.00 sec) -- UPDATE mysql> update test_table set name = 'fuga'; Query OK, 1 row affected (0.05 sec) Rows matched: 1 Changed: 1 Warnings: 0 -- timestampの値が更新される。 mysql> select * from test_table; +----+------+---------------------+ | id | name | last_modified | +----+------+---------------------+ | 1 | fuga | 2015-12-14 12:24:32 | +----+------+---------------------+ 1 row in set (0.03 sec) -- 値が変更されないUPDATE mysql> update test_table set name = 'fuga'; Query OK, 0 rows affected (0.00 sec) Rows matched: 1 Changed: 0 Warnings: 0 -- timestampは更新されない mysql> select * from test_table; +----+------+---------------------+ | id | name | last_modified | +----+------+---------------------+ | 1 | fuga | 2015-12-14 12:24:32 | +----+------+---------------------+ 1 row in set (0.00 sec)
ラベル:
MySql
2015年12月8日火曜日
DBMS_XPLAN.DISPLAY_CURSORを使って最近実行したSQL文のアクセスプランを取得
マニュアルによると、「DISPLAY_CURSORファンクションはカーソル・キャッシュにロードされている任意のカーソルの実行計画を表示します」とあるので、
実行されてからあまり時間がたっていないSQLであれば簡単にアクセスプランが見れる。
DISPLAY_CURSORは、デフォルト動作ではそのセッションの最後のカーソルのアクセスプランを確認することができる。
また、SQL_IDを指定することで任意のSQL文のアクセスプランも簡単に確認することができる。
DISPLAY_CURSORの3番めの引数のformatを指定すると、実行計画の詳細レベルを変更することができる。
詳細は、「PL/SQLパッケージおよびタイプ・リファレンス」を見るとわかる。
実行されてからあまり時間がたっていないSQLであれば簡単にアクセスプランが見れる。
DISPLAY_CURSORは、デフォルト動作ではそのセッションの最後のカーソルのアクセスプランを確認することができる。
また、SQL_IDを指定することで任意のSQL文のアクセスプランも簡単に確認することができる。
実行したSQL
select /* hoge */ 1 from dual;
v$sqlからSQLIDを取得
SQL> select sql_id, child_number from v$sql where sql_text like '%hoge%' and sql_text not like '%v$sql%'; SQL_ID CHILD_NUMBER ------------- ------------ 42mk8byafr2yx 0
DISPLAY_CURSORを使ってアクセスプランの表示
SQL> select * from table(DBMS_XPLAN.DISPLAY_CURSOR('42mk8byafr2yx', 0)); PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------ SQL_ID 42mk8byafr2yx, child number 0 ------------------------------------- select /* hoge */ 1 from dual Plan hash value: 1388734953 ----------------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU)| Time | ----------------------------------------------------------------- | 0 | SELECT STATEMENT | | | 2 (100)| | | 1 | FAST DUAL | | 1 | 2 (0)| 00:00:01 | -----------------------------------------------------------------
DISPLAY_CURSORの3番めの引数のformatを指定すると、実行計画の詳細レベルを変更することができる。
詳細は、「PL/SQLパッケージおよびタイプ・リファレンス」を見るとわかる。
ラベル:
Oracle
2015年12月7日月曜日
HibernateのImplicitNamingStrategyを使って識別子に任意のプレフィックス等を付加する
Hibernateでテーブル名やカラム名などに一律任意のサフィックスやプレフィックスを追加するには、ImplicitNamingStrategyを実装したクラスを作成し設定を追加しするだけでよい。
PhysicalNamingStrategyを使った識別子の変更とは異なり、明示的に識別子を指定した場合は、このクラスは呼び出されない。また、ImplicitNamingStrategyのほうが、命名ルールを細かく設定できる。
PhysicalNamingStrategyとくらべてかなり多くのメソッドが定義されているけど、テーブル名であればdeterminePrimaryTableNameをオーバライドしてあげるとよい。
サンプル実装では、テーブル名の後ろに「_TBL」を付加している。
PhysicalNamingStrategyを使った識別子の変更とは異なり、明示的に識別子を指定した場合は、このクラスは呼び出されない。また、ImplicitNamingStrategyのほうが、命名ルールを細かく設定できる。
ImplicitNamingStrategyの実装クラス
JPAの場合はデフォルト実装のImplicitNamingStrategyJpaCompliantImplを継承する。PhysicalNamingStrategyとくらべてかなり多くのメソッドが定義されているけど、テーブル名であればdeterminePrimaryTableNameをオーバライドしてあげるとよい。
サンプル実装では、テーブル名の後ろに「_TBL」を付加している。
import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.naming.ImplicitEntityNameSource; import org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl; public class HogeImplicitNamingStrategy extends ImplicitNamingStrategyJpaCompliantImpl { @Override public Identifier determinePrimaryTableName(ImplicitEntityNameSource source) { Identifier tableName = super.determinePrimaryTableName(source); return Identifier.toIdentifier(tableName.getText() + "_TBL"); } }
persistence.xmlにImplicitNamingStrategy実装クラスを設定
hibernate.implicit_naming_strategyというプロパティに、作成したクラスのFQCNを設定する。<properties> <property name="hibernate.implicit_naming_strategy" value="sample.HogeImplicitNamingStrategy" /> </properties>
結果
テーブル名の末尾に「_TBL」が設定されているのが確認できる。usersエンティティは、@Tableアノテーションが設定されているので、「_TBL」が付加されない。Hibernate: create table Item_TBL (id bigint not null, primary key (id)) Hibernate: create table users (id bigint not null, name varchar(255), primary key (id))
ラベル:
Hibernate
2015年12月6日日曜日
HibernateのPhysicalNamingStrategyを使って識別子に任意のプレフィックス等を付加する
Hibernateでテーブル名やカラム名などに一律任意のサフィックスやプレフィックスを追加するには、PhysicalNamingStrategyを実装したクラスを作成し設定を追加しするだけでよい。
この例では、テブル名の先頭に「hoge_」を付加している。
PhysicalNamingStrategyの実装クラス
PhysicalNamingStrategyのデフォルト実装のPhysicalNamingStrategyStandardImplを継承すると必要なメソッドだけ実装すればいいのでよい。この例では、テブル名の先頭に「hoge_」を付加している。
import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; public class HogeNameStrategy extends PhysicalNamingStrategyStandardImpl { @Override public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) { return Identifier.toIdentifier("hoge_" + name.getText(), name.isQuoted()); } }
persistence.xmlへPhysicalNamingStrategy実装クラスを設定
hibernate.physical_naming_strategyというプロパティに、作成したクラスのFQCNを設定する。<properties> <property name="hibernate.physical_naming_strategy" value="sample.HogeNameStrategy" /> </properties>
結果
一律プレフィックスが追加されているのがわかるHibernate: create table HOGE_Item (id bigint not null, primary key (id)) Hibernate: create table "HOGE_user" (id bigint not null, name varchar(255), primary key (id))
ラベル:
Hibernate
2015年12月5日土曜日
Hibernate5のhibernate.auto_quote_keywordでキーワードを識別子に使えるように
hibernate.auto_quote_keywordプロパティの値をtrueに設定すると、キーワードを識別子に使った場合、自動的にクォートで囲ってくれるようになる。
これで、キーワードとかぶるエンティティを作ったとしても、@Tableアノテーションでテーブル名を明示的に指定しなくても良くなる。
設定値にtrueを指定すると、自動的にダブルクォートで囲われるようになる。
この設定値をfalseにした場合は、こんな結果になる。
JPA(Hibernate)経由でSQL実行しているだけだといいけど、自分でSQLを書く必要が出てきた時にはまりそうだなぁと思ったり。
これで、キーワードとかぶるエンティティを作ったとしても、@Tableアノテーションでテーブル名を明示的に指定しなくても良くなる。
Entityクラス
Oracleでは識別子に指定できないUserがテーブル名となるエンティティクラスを定義する。@Entity public class User { @Id public Long id; public String name; }
hibernate.auto_quote_keywordの指定
persistence.xmlのproperties部にhibernate.auto_quote_keywordの設定を追加する。設定値にtrueを指定すると、自動的にダブルクォートで囲われるようになる。
<properties> <property name="hibernate.auto_quote_keyword" value="true" /> </properties>
実行結果
ログ上で確認するとこんな感じにクォートで囲われているのがわかる。Hibernate: create table "User" (id number(19,0) not null, name varchar2(255 char), primary key (id))
この設定値をfalseにした場合は、こんな結果になる。
Hibernate: create table User (id number(19,0) not null, name varchar2(255 char), primary key (id)) ERROR: HHH000389: Unsuccessful: create table User (id number(19,0) not null, name varchar2(255 char), primary key (id)) ERROR: ORA-00903: 表名が無効です。
JPA(Hibernate)経由でSQL実行しているだけだといいけど、自分でSQLを書く必要が出てきた時にはまりそうだなぁと思ったり。
ラベル:
Hibernate,
Hibernate5,
JPA
2015年11月6日金曜日
[Oracle]DDL_LOCK_TIMEOUTでDDLのロックタイムアウトの待機時間を設定する
Oracle11gからは、DMLでロックされているレコードがあるテーブルに対するDDL文のロックタイムアウトの待機時間が設定できる。
設定がない場合(デフォルトの動作は)、nowaitなのでロックされているレコードがあるテーブルに対するDDLは即タイムアウトする。
発生するエラーは、こんな感じ。
設定値の変更は、DDL_LOCK_TIMEOUTパラメータで行う。例えば、10秒を設定したい場合には、以下の様なalter sessionを実行する。
設定がない場合(デフォルトの動作は)、nowaitなのでロックされているレコードがあるテーブルに対するDDLは即タイムアウトする。
発生するエラーは、こんな感じ。
ERROR at line 1: ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired
設定値の変更は、DDL_LOCK_TIMEOUTパラメータで行う。例えば、10秒を設定したい場合には、以下の様なalter sessionを実行する。
alter session set DDL_LOCK_TIMEOUT=10;
ラベル:
Oracle
2015年11月1日日曜日
[Oracle]パスワードの大文字小文字の区別の設定
初期化パラメータのsec_case_sensitive_logonで大文字、小文字を区別するか設定する事ができる。
この値が、trueの場合は大文字と小文字が区別されるようになる。
この値が、trueの場合は大文字と小文字が区別されるようになる。
設定値の確認
show parametersで確認できる。SQL> sho parameters sec_case_sensitive_logon NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ sec_case_sensitive_logon boolean FALSE
設定値の変更
alter systemを使用して変更する。alter system set sec_case_sensitive_logon = false
注意点
Oracle12cのセキュリティ・ガイドによると、この機能は非推奨で下位互換のためだけに残されているようです。
ラベル:
Oracle
2015年10月19日月曜日
Oracleのバーチャルインデックス
バーチャルインデックスを使うと、ディスクスペースや作成時間、他のアプリケーション(セッション)を
気にせずにインデックス追加時のアクセスプランなどを確認することができる。
_use_nosegment_indexesパラメータの値をtrueに変更することで、現在のセッションのみバーチャルインデックスが有効となる。
バーチャルインデックスを有効に変更した後にためしてみると、インデックスが参照されるようになる。
気にせずにインデックス追加時のアクセスプランなどを確認することができる。
テーブル定義
SQL> desc person 名前 NULL? 型 ----------------------------------------- -------- ---------------------------- P_ID NOT NULL NUMBER NAME VARCHAR2(100)
バーチャルインデックスの作成
create indexのオプションにnosegmentを指定することでバーチャルインデックスになるcreate index person_name_index on person(name) nosegment;
バーチャルインデックスを使ってみる
通常は、バーチャルインデックスは使用されない。1* select * from person where name = 'a' 実行計画 ---------------------------------------------------------- Plan hash value: 1493655343 ---------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 65 | 3 (0)| 00:00:01 | |* 1 | TABLE ACCESS FULL| PERSON | 1 | 65 | 3 (0)| 00:00:01 | ---------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("NAME"='a')
_use_nosegment_indexesパラメータの値をtrueに変更することで、現在のセッションのみバーチャルインデックスが有効となる。
SQL> alter session set "_use_nosegment_indexes"=true; セッションが変更されました。
バーチャルインデックスを有効に変更した後にためしてみると、インデックスが参照されるようになる。
1* select * from person where name = 'a' 実行計画 ---------------------------------------------------------- Plan hash value: 966497106 --------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 65 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID BATCHED| PERSON | 1 | 65 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | PERSON_NAME_INDEX | 1 | | 1 (0)| 00:00:01 | ---------------------------------------------------------------------------------------------------------
ラベル:
Oracle
2015年9月25日金曜日
border-radiusで角の丸いボックスを配置する
CSS3のborder-radiusで角の丸いボックスを簡単に配置できる。
下のように、4つの値を指定した場合は、左上から時計回りに指定した値が適用される。
表示例
下のように、4つの値を指定した場合は、左上から時計回りに指定した値が適用される。
#box { border-radius: 40px 160px 80px 120px; }
表示例
ラベル:
css3
2015年9月18日金曜日
Angularでテンプレートを切り替えたタイミングで、Material Design Liteのコンポーネントを更新する
AngularJSとMaterial Design Liteを組み合わせて使った場合で、Angularのrouteを使ってtemplateを切り替えた場合、
切り替えて出力されたmdlコンポーネントの初期化処理が実行されない問題が発生した。
この問題を回避するには、templateを切り替えたタイミングでcomponentHandler.upgradeDom();を呼び出してあげれば良い。
参考サイト
切り替えて出力されたmdlコンポーネントの初期化処理が実行されない問題が発生した。
この問題を回避するには、templateを切り替えたタイミングでcomponentHandler.upgradeDom();を呼び出してあげれば良い。
参考サイト
例
angular.module('app', ['ngRoute', 'ngResource']) .run(function ($rootScope, $location, $timeout) { $rootScope.$on('$viewContentLoaded', function () { $timeout(function () { componentHandler.upgradeAllRegistered(); }); }) })
2015年9月16日水曜日
CSS3でフェードインとフェードアウト
CSS3のtransition-durationを使うと、jQueryなどを使わずに簡単に要素のフェードインやフェードアウトを行うことができる。
removeでは、要素を徐々に透明にしつつ高さを0にする。
showでは、徐々に要素の高さと透明の設定も元にもどしている。
JavaScriptでは、ボタンのクリックイベントで対象要素のクラスを切り替えてフェードイン、フェードアウトを実行している。
例
transition-durationに2sを設定しているので、2秒かけて要素の状態が徐々に変化していく。removeでは、要素を徐々に透明にしつつ高さを0にする。
showでは、徐々に要素の高さと透明の設定も元にもどしている。
div.remove { opacity: 0; height: 0; transition-duration: 2s; } div.show { opacity: 1; height: 200px; transition-duration: 2s; }フェードイン、フェードアウト対象の要素の定義とJavaScriptの内容。
JavaScriptでは、ボタンのクリックイベントで対象要素のクラスを切り替えてフェードイン、フェードアウトを実行している。
<div id="target"> 要素 </div> <button type="button" id="remove">削除</button> <button type="button" id="add">表示</button> <script> var removeBtn = document.getElementById('remove'); var addBtn = document.getElementById('add'); var div = document.getElementById('target'); removeBtn.addEventListener("click", function() { div.className = 'remove'; }); addBtn.addEventListener("click", function() { div.style.display = ''; div.className = 'show'; }); </script>
実行例
要素
ラベル:
css3
2015年9月14日月曜日
[CSS3]box-sizingプロパティでボックス幅の計算方法を指定する
CSS3のbox-sizingプロパティを使うと、ボックスの幅や高さの計算方法を指定することができるようになる。
twoは、paddingとborderがボックスサイズに含まれるので、幅と高さをその分大きく設定している。
指定できる値
- content-box
- paddingやborderはbox幅や高さに含まれない
- border-box
- paddingとborderはbox幅や高さに含まれる
例
この例の場合、oneとtwoのボックスサイズは同じ大きさとなる。twoは、paddingとborderがボックスサイズに含まれるので、幅と高さをその分大きく設定している。
<!-- スタイル定義 --> <style> #one { box-sizing: content-box; width: 200px; height: 200px; border: 10px solid black; } #two { box-sizing: border-box; width: 220px; height: 220px; border: 10px solid black; } </style> <!-- html --> <div id="one"></div> <div id="two"></div>
ラベル:
css3
2015年8月29日土曜日
Maven pluginでプロジェクトのビルドアウトプットに依存する方法
Guide to Maven Classloadingによると、Maven pluginはPlugin classloaderでクラスがロードされます。
プラグインのdependenciesに設定されたライブラリはロードされるけど、プラグインが設定されたプロジェクトのビルドアウトプットやdependenciesに設定されたライブラリを見ることはできない。
どうしても、プラグインからプロジェクトのビルドアウトプットを参照できないとダメなときは、Mojoでカスタムクラスローダを使ってロードする必要がある。例えば、Mojoから使うライブラリがClassオブジェクトを要求する場合なんかは、自分でカスタムクラスローダ使って頑張る必要がある。
Stackoverflowいい解決案があるので、これを参考に対応するのがよい。→Maven plugin can't load class
自分は、最も+1された回答を参考にしてみました。
やった内容は、大きくしたの2点になります。
getRuntimeClasspathElementsが返す値は、MojoアノテーションのrequiresDependencyResolution属性に指定した値によって変わります。
属性を指定しない場合は、target/classesだけが取得されます。ResolutionScope.RUNTIMEを設定すると、dependenciesに設定したライブラリも取得されます。
プラグインのdependenciesに設定されたライブラリはロードされるけど、プラグインが設定されたプロジェクトのビルドアウトプットやdependenciesに設定されたライブラリを見ることはできない。
どうしても、プラグインからプロジェクトのビルドアウトプットを参照できないとダメなときは、Mojoでカスタムクラスローダを使ってロードする必要がある。例えば、Mojoから使うライブラリがClassオブジェクトを要求する場合なんかは、自分でカスタムクラスローダ使って頑張る必要がある。
Stackoverflowいい解決案があるので、これを参考に対応するのがよい。→Maven plugin can't load class
自分は、最も+1された回答を参考にしてみました。
やった内容は、大きくしたの2点になります。
MavenProjectをMojoにインジェクトする
Mojoのフィールドで、MavenProjectを受け取ります。@Parameter(defaultValue = "${project}", required = true, readonly = true) private MavenProject project;
カスタムクラスローダを使ってクラスをロードする
MavenProjectのgetRuntimeClasspathElementsを使って、クラスパスに設定されているパスを取得して、カスタムクラスローダを作ります。getRuntimeClasspathElementsが返す値は、MojoアノテーションのrequiresDependencyResolution属性に指定した値によって変わります。
属性を指定しない場合は、target/classesだけが取得されます。ResolutionScope.RUNTIMEを設定すると、dependenciesに設定したライブラリも取得されます。
private URLClassLoader createClassLoader() { ListruntimeClasspathElements; try { runtimeClasspathElements = project.getRuntimeClasspathElements(); } catch (DependencyResolutionRequiredException e) { throw new RuntimeException(e); } final URL[] urls = runtimeClasspathElements.stream() .peek(s -> System.out.println("classpath:" + s)) .map(s -> { try { return new File(s).toURI().toURL(); } catch (MalformedURLException e) { throw new RuntimeException(e); } }).toArray(URL[]::new); return new URLClassLoader(urls, Thread.currentThread().getContextClassLoader()); }
ラベル:
Maven
[CSS3]attribute selector
attribute selectorは属性値を使って要素を選択できるセレクター。
使い方は、簡単で[]内にattribute selectorを定義する。
使い方は、簡単で[]内にattribute selectorを定義する。
[attribute selector] { }
例
/* attributeという属性を持つ要素を選択 */ [attribute] { } /* class="hogs"を持つ要素を選択 class="hogs fuga"はマッチしないので選択されない */ [class="hogs"] { } /* class属性の値がhogeから始まる要素を選択 */ [class^="hoge"] { } /* class属性の値がfugaで終わる要素を選択 */ [class$="fuga"] { } /* class属性の値の中で、スペースで区切られた値「hoge」がある要素を選択 class="hogs fuga"やclass="hoge"が選択される */ [class~="hoge"] { }
ラベル:
css3
2015年8月6日木曜日
PostgreSQLのpsqlでテーブル定義を確認する
psqlメタコマンドの\d テーブル名で確認することができる。
実行例
=> \d hoge テーブル "public.hoge" 列 | 型 | 修飾語 ------+--------+-------- id | bigint | name | text |
ラベル:
PostgreSQL
2015年7月27日月曜日
eclipselinkでbatch insert
eclipselinkでは、jdbc.batch-writingとjdbc.batch-writing.sizeの2つのプロパティを指定することでbatch insertが使用されます。
jdbc.batch-writingには、基本的にjdbcを指定すればよく、jdbc.batch-writing.sizeにはバッチサイズを指定する。
例えば、以下のように設定するとバッチサイズは100となります。
結果を見ると、batch insertのほうが早くなっているのがわかります。
batch insertあり
batch insertなし
jdbc.batch-writingには、基本的にjdbcを指定すればよく、jdbc.batch-writing.sizeにはバッチサイズを指定する。
例えば、以下のように設定するとバッチサイズは100となります。
<properties> <property name="javax.persistence.schema-generation.database.action" value="drop-and-create" /> <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver"/> <property name="eclipselink.jdbc.batch-writing" value="JDBC" /> <property name="eclipselink.jdbc.batch-writing.size" value="100" /> </properties>
結果を見ると、batch insertのほうが早くなっているのがわかります。
batch insertあり
---------- 1回目: 625ms ---------- ---------- 2回目: 319ms ---------- ---------- 3回目: 274ms ----------
batch insertなし
---------- 1回目: 1630ms ---------- ---------- 2回目: 1342ms ---------- ---------- 3回目: 1146ms ----------
ラベル:
eclipselink,
JPA
2015年7月22日水曜日
hibernateでbatch-insert
hibernate.jdbc.batch_sizeに任意の数字を指定すると、その値が自動的にJDBCのバッチサイズになります。
例えば、以下の設定を行うとバッチサイズは100となります。
試しに以下のコードでbatch insertの効果があるのか確認してみます。
結果をみると、batch insertの効果で5倍ぐらい早くなっているのがわかります。
batch insertあり
例えば、以下の設定を行うとバッチサイズは100となります。
<properties> <property name="javax.persistence.schema-generation.database.action" value="drop-and-create" /> <property name="javax.persistence.jdbc.driver" value="oracle.jdbc.OracleDriver" /> <property name="javax.persistence.jdbc.url" value="url"/> <property name="javax.persistence.jdbc.user" value="user"/> <property name="javax.persistence.jdbc.password" value="password"/> <property name="hibernate.jdbc.batch_size" value="100" /> </properties>
試しに以下のコードでbatch insertの効果があるのか確認してみます。
private static void insert(EntityManager em) { final EntityTransaction transaction = em.getTransaction(); transaction.begin(); for (int i = 1; i <= 1000; i++) { final UserEntity entity = new UserEntity(); entity.setName("name_" + i); em.persist(entity); } transaction.commit(); }
結果をみると、batch insertの効果で5倍ぐらい早くなっているのがわかります。
batch insertあり
---------- 1回目: 766ms ---------- ---------- 2回目: 390ms ---------- ---------- 3回目: 317ms ----------batch insertなし
---------- 1回目: 2369ms ---------- ---------- 2回目: 2347ms ---------- ---------- 3回目: 1589ms ----------
2015年7月19日日曜日
PostgreSQLのsleepファンクション
pg_sleepを使うことでデータベースサーバ側でsleep処理ができる。
pg_sleepの引数には、sleepしたい秒数を指定する。
pg_sleepの引数には、sleepしたい秒数を指定する。
-- 5秒sleep select pg_sleep(5); pg_sleep ---------- (1 行) 時間: 5016.963 ms -- 3秒sleep select pg_sleep(3); pg_sleep ---------- (1 行) 時間: 3001.497 ms
ラベル:
PostgreSQL
2015年7月5日日曜日
IS DISTINCT FROM演算子
IS DISTINCT FROM(IS NOT DISTINCT FROM)演算子の使い方。PostgreSQLでは使うことができる。Oracleは12c時点では使うことができない。
この演算子を使うと、一方がnullの可能性がある場合の条件をcol1 = :col1 or col1 is nullのようにnullを考慮する必要がなくなる。
条件を指定すると、そのレコードのみ取得できる。
この演算子を使うと、一方がnullの可能性がある場合の条件をcol1 = :col1 or col1 is nullのようにnullを考慮する必要がなくなる。
使い方
IS NOT DISTINCT FROMは、以下の条件と同じ動きとなる。(expo IS NOT NULL AND exp2 IS NOT NULL AND exp1 = exp2) OR (exp1 IS NULL AND exp2 IS NULL)
例
検索対象のデータ
データの内容(@は、nullを示しています)select * from test; col ----- 1 2 3 @ (4 行)
IS NOT DISTINCT FROM
is not distinct from nullとするとnullのレコードのみ取得できる。select * from test where col is not distinct from null; col ----- @
条件を指定すると、そのレコードのみ取得できる。
select * from test where col is not distinct from 3; col ----- 3 (1 行)
ラベル:
PostgreSQL
2015年6月28日日曜日
[Oracle12c]SQL*Loaderのエクスプレス・モード
Oracle12CからはSQL*Loaderのエクスプレス・モードを使用してコントロールファイルを作らずにデータをロードすることができるようです。
この機能は、表の列がすべて文字列、数値または日付型で、入力ファイルにデリミタ付きの文字列データが含まれている場合(例えばCSVファイル)に、テーブル名の指定のみでロードが実行できます。
実行時には、接続情報をテーブル名のみを与えるのみで、コントロールファイルは作成していません。
入力ファイルを指定しない場合、デフォルトでは「テーブル名.dat」となります。今回の場合は、「users.dat」が入力ファイルとなります。
テーブルにも5レコード格納されています。
今回の場合は、以下の定義でロードされています。
変更できる項目はマニュアルで参照できます。
この機能は、表の列がすべて文字列、数値または日付型で、入力ファイルにデリミタ付きの文字列データが含まれている場合(例えばCSVファイル)に、テーブル名の指定のみでロードが実行できます。
エクスプレスモードの例
テーブル定義
主キーと属性1つづつのシンプルなテーブルで試してみます。SQL> desc users; Name Null? Type ----------------------------------------- -------- ---------------------------- USER_ID NOT NULL NUMBER(10) NAME VARCHAR2(100 CHAR)
CSVファイルの内容
データが5行のCSVファイルです。1,user1 2,user2 3,user3 4,user4 5,user5
実行結果
実行結果の標準出力内容です。5行のロードが正常に終わっていることがわかります。実行時には、接続情報をテーブル名のみを与えるのみで、コントロールファイルは作成していません。
入力ファイルを指定しない場合、デフォルトでは「テーブル名.dat」となります。今回の場合は、「users.dat」が入力ファイルとなります。
$ sqlldr hoge/hoge@orcl TABLE=users SQL*Loader: Release 12.1.0.2.0 - Production on Sat Jun 27 10:49:48 2015 Copyright (c) 1982, 2014, Oracle and/or its affiliates. All rights reserved. Express Mode Load, Table: USERS Path used: External Table, DEGREE_OF_PARALLELISM=AUTO Table USERS: 5 Rows successfully loaded. Check the log files: users.log users_%p.log_xt for more information about the load.
テーブルにも5レコード格納されています。
SQL> r 1* select * from users USER_ID NAME ---------- ------------------------- 1 user1 2 user2 3 user3 4 user4 5 user5
どんな定義でロードされるの?
実行時の定義(コントロールファイルの内容)は、実行ログから確認できます。今回の場合は、以下の定義でロードされています。
OPTIONS(EXTERNAL_TABLE=EXECUTE, TRIM=LRTRIM) LOAD DATA INFILE 'users' APPEND INTO TABLE USERS FIELDS TERMINATED BY "," ( USER_ID, NAME CHAR(400) )
デフォルト値の上書き方法
デフォルト値はコマンドラインパラメータで上書きします。例えば、フィールドの区切り文字をデフォルトのカンマ(,)から縦棒(|)に変更したい場合には以下のようにします。sqlldr hoge/hoge@orcl TABLE=users TERMINATED_BY='|'
変更できる項目はマニュアルで参照できます。
ラベル:
Oracle,
Oracle12,
SQL*Loader
2015年6月24日水曜日
SQL*Loaderでインプットファイルの論理行番号をテーブルに格納する
SQL*Loaderでデータロード時に、ファイルの論理レコード番号をテーブルに格納したい場合には、RECNUMパラメータを使用する。
RECNUMを使用するとスキップしたレコードや不良レコード、破棄レコードも1レコードとしてカウントされる。
スキップしたヘッダー行や不正なレコードの空行もレコード数としてカウントアップされているのが分かる。
RECNUMを使用するとスキップしたレコードや不良レコード、破棄レコードも1レコードとしてカウントされる。
ロード先のテーブル定義
名前 NULL? 型
--------- -------- ----------------------------
REC_NUM NOT NULL NUMBER
NAME VARCHAR2(100)
コントロールファイルの内容
論理行番号を格納したいカラムにRECNUMパラメータを使用する。OPTIONS (SKIP=1)
LOAD DATA
CHARACTERSET 'AL32UTF8'
INFILE 'input.csv' "str '\n'"
BADFILE 'input.bad'
DISCARDFILE 'input.dsc'
TRUNCATE
INTO TABLE HOGE
FIELDS TERMINATED BY ','
(
REC_NUM RECNUM,
NAME OPTIONALLY ENCLOSED BY '"'
)
CSVファイルの内容
タイトル行あり、不正なレコード(空行)ありのファイル。name
あ
い
う
え
お
実行結果の確認
以下のコマンドで、上のCSVファイルを取り込みます。sqlldr 接続先情報 control=input.ctl
スキップしたヘッダー行や不正なレコードの空行もレコード数としてカウントアップされているのが分かる。
1* select * from hoge
REC_NUM NAME
---------- ------------------------------
2 あ
3 い
5 う
6 え
7 お
ラベル:
Oracle,
SQL*Loader
2015年6月12日金曜日
[PostgreSQL]単一のINSERT文で複数レコードを登録
INSERT文のVALUES区に登録対象のレコード情報をカンマ(,)区切りで複数列挙することで一括登録ができる
SQL例
使い方は簡単で、INSERTのVALUE句に設定する。INSERT INTO users (id, name) VALUES (1, 'hoge'), (2, 'fuga')
ラベル:
PostgreSQL
2015年5月17日日曜日
[JPA2.1]DDL generation
JPA2.1で標準化されたDDL Generationを試してみました。
この設定でアプリケーションを実行すると、カレントディレクトリ配下にdorp用とcreate用のDDLが作成されます。
また、データベースに対してもdropでDBクリーニング後にCREATEでデータベースがセットアップされます。
データベースセットアップ後には、javax.persistence.sql-load-script-sourceで指定したSQLスクリプトが実行されます。
javax.persistence.schema-generation.database.action
データベース上へのアクションを指定します。drop-and-createを指定しているので、Entityを元に既存オブジェクトの削除をしてから新しくオブジェクトが作られます。
その他にも、dropやcreateなどを選択できます。
javax.persistence.schema-generation.scripts.action
オブジェクト作成用のSQLスクリプトファイルの生成方法を指定します。drop-and-createを指定しているので、dropとcreate用の2つのSQLファイルが出力されます。
create文は、javax.persistence.schema-generation.scripts.create-targetで設定したファイルに出力されます。
drop文は、javax.persistence.schema-generation.scripts.drop-targetで設定したファイルに出力されます。
javax.persistence.sql-load-script-source
データベースセットアップ後に実行するSQLスクリプトを設定します。
初期データのセットアップなどを行うINSERT文などを定義します。
Entity
Entityクラスの実装package sample; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "user") public class UserEntity { private Long id; private String name; @Id @GeneratedValue() public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
persistence.xml
DDL Generationを使うための設定が入ったpersistence.xmlの例<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.1"> <persistence-unit name="pu" transaction-type="RESOURCE_LOCAL"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <class>sample.UserEntity</class> <properties> <property name="javax.persistence.schema-generation.database.action" value="drop-and-create" /> <property name="javax.persistence.schema-generation.scripts.action" value="drop-and-create" /> <property name="javax.persistence.schema-generation.scripts.create-target" value="create.sql" /> <property name="javax.persistence.schema-generation.scripts.drop-target" value="drop.sql" /> <property name="javax.persistence.sql-load-script-source" value="file:./load-data.sql" /> <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" /> <property name="javax.persistence.jdbc.url" value="jdbc:h2:file:/tmp/test-db" /> <property name="javax.persistence.jdbc.user" value="sa" /> <property name="eclipselink.logging.level" value="fine" /> </properties> </persistence-unit> </persistence>
この設定でアプリケーションを実行すると、カレントディレクトリ配下にdorp用とcreate用のDDLが作成されます。
また、データベースに対してもdropでDBクリーニング後にCREATEでデータベースがセットアップされます。
データベースセットアップ後には、javax.persistence.sql-load-script-sourceで指定したSQLスクリプトが実行されます。
javax.persistence.schema-generation.database.action
データベース上へのアクションを指定します。drop-and-createを指定しているので、Entityを元に既存オブジェクトの削除をしてから新しくオブジェクトが作られます。
その他にも、dropやcreateなどを選択できます。
javax.persistence.schema-generation.scripts.action
オブジェクト作成用のSQLスクリプトファイルの生成方法を指定します。drop-and-createを指定しているので、dropとcreate用の2つのSQLファイルが出力されます。
create文は、javax.persistence.schema-generation.scripts.create-targetで設定したファイルに出力されます。
drop文は、javax.persistence.schema-generation.scripts.drop-targetで設定したファイルに出力されます。
javax.persistence.sql-load-script-source
データベースセットアップ後に実行するSQLスクリプトを設定します。
初期データのセットアップなどを行うINSERT文などを定義します。
2015年5月9日土曜日
[Oracle12C]拡張データ型でVARCHAR2やRAWの最大長をより大きい値にする
Oracle12cからの新機能の拡張データ型を使用すると、VARCHAR2、NVARCHAR2、RAWの最大サイズが32,767バイトまで指定可能となります。
※以前のバージョンでは、文字列(VARCHAR2、NVARCHAR2)は4000、RAWは2000が最大サイズだったかと思います。
この初期化パラメータには、以下の2種類の設定値があります。設定値を拡張データ型(EXTENDED)に変更するとSTANDARDに戻すことはできないので注意が必要です。
rdbms/admin/utl32k.sqlを実行し、ノーマルモードでデータベースを再起動します。
作成したテーブルの定義を確認すると、指定した桁数のVARCAR2列が定義されている事がわかります。
実行結果は、以下のようになります。ドキュメントには、4000バイトを超えるデータは内部的にはCLOBのテクノロジを使用して格納するとあったけど、テーブル作った時のデータタイプなどが取得できています。
型マッピングもVARCHAR2と同じですね。(CLOBになってなくてよかったです。)
※以前のバージョンでは、文字列(VARCHAR2、NVARCHAR2)は4000、RAWは2000が最大サイズだったかと思います。
拡張データ型の使用方法
拡張データ型は初期化パラメータのMAX_STRING_SIZEにより設定します。この初期化パラメータには、以下の2種類の設定値があります。設定値を拡張データ型(EXTENDED)に変更するとSTANDARDに戻すことはできないので注意が必要です。
- EXTENDED
- 上限が32,767バイトになります
- STANDARD
- Oracle12cより前のバージョンと同じ仕様となります。デフォルトはこの値となります。
現在値の確認
SQL> show parameters max_string_size NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ max_string_size string STANDARD
UPGRADEモードでデータベースを再起動
非CDBの場合には、以下の手順でデータベースを再起動します。SQL> shutdown SQL> startup UPGRADE
拡張データ型の有効化
altert systemで拡張データ型を使用できるようにします。SQL> alter system set MAX_STRING_SIZE = EXTENDED; System altered. SQL> show parameters max_string_size NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ max_string_size string EXTENDED
rdbms/admin/utl32k.sqlを実行し、ノーマルモードでデータベースを再起動します。
テーブルを作ってみる
以下のように、VARCHAR2の4000バイトを突破したカラムを定義できるようになります。CREATE TABLE TEST_TABLE ( EXTENDED_VARCHAR VARCHAR2(32767) NOT NULL )
作成したテーブルの定義を確認すると、指定した桁数のVARCAR2列が定義されている事がわかります。
SQL> desc test_table; Name Null? Type ----------------------------------------- -------- ---------------------------- EXTENDED_VARCHAR NOT NULL VARCHAR2(32767)
データを登録してみる
以下のように4000バイトを超えるデータを挿入、参照できていることがわかります。SQL> insert into test_table values (rpad('1', 32767, '1')); 1 row created. SQL> select length(extended_varchar) from test_table; LENGTH(EXTENDED_VARCHAR) ------------------------ 32767
JDBCで扱ってみる
メータデータからカラム定義を取得
以下のコードでメタデータの情報を出力してみます。final ResultSet rs = metaData.getColumns(null, null, "TEST_TABLE", null); while (rs.next()) { System.out.println("rs.getString(\"column_name\") = " + rs.getString("column_name")); System.out.println("rs.getInt(\"data_type\") = " + rs.getInt("data_type")); System.out.println("Types.VARCHAR = " + Types.VARCHAR); System.out.println("rs.getString(\"type_name\") = " + rs.getString("type_name")); System.out.println("rs.getInt(\"column_size\") = " + rs.getInt("column_size")); }
実行結果は、以下のようになります。ドキュメントには、4000バイトを超えるデータは内部的にはCLOBのテクノロジを使用して格納するとあったけど、テーブル作った時のデータタイプなどが取得できています。
table.getString("column_name") = EXTENDED_VARCHAR table.getInt("data_type") = 12 Types.VARCHAR = 12 table.getString("type_name") = VARCHAR2 table.getInt("column_size") = 32767
データを参照してみる
JDBC経由での参照は特に何かを意識する必要はなさそうです。型マッピングもVARCHAR2と同じですね。(CLOBになってなくてよかったです。)
final Statement statement = connection.createStatement(); final ResultSet rs = statement.executeQuery("select * from TEST_TABLE"); if (rs.next()) { final Object object = rs.getObject(1); System.out.println("String? " + (object instanceof String)); final String str = String.class.cast(object); System.out.println("str.length() = " + str.length()); } //------------------- // 実行結果 //------------------- String? true str.length() = 32767
2015年4月18日土曜日
[PostgreSQL]ドメインを定義する
ドメイン定義を使用することで、よく利用する列定義をテーブルごとにするのではなくドメインの定義に集約することができる。
ドメインの定義方法
create domainを使用してドメインを作成する。例
-- not null制約のついた、varchar(5)のドメインを作成 CREATE DOMAIN test_domain VARCHAR(5) NOT NULL; -- ドメインの定義内容の確認 ドメイン一覧 スキーマ | 名前 | 型 | 修飾語 | チェック -------------+-------------+----------------------+----------+---------- test_schema | test_domain | character varying(5) | not null |
ドメイン型のカラムの作成
-- テーブルの作成 CREATE TABLE TEST_TABLE ( ID SERIAL NOT NULL, NAME TEST_DOMAIN, -- 型にドメインを指定したカラム PRIMARY KEY (ID) ) -- テーブルの定義の確認 -- ドメインに設定しているnot null制約は確認できない テーブル "test_schema.test_table" 列 | 型 | 修飾語 ------+-------------+--------------------------------------------------------- id | integer | not null default nextval('test_table_id_seq'::regclass) name | test_domain | インデックス: "test_table_pkey" PRIMARY KEY, btree (id)
データをインサートしてみる
-- 有効なデータの場合正しくinsertできる insert into test_table (name) values ('12345'); INSERT 0 1 -- 桁数オーバの場合エラーになる insert into test_table (name) values ('123456'); ERROR: value too long for type character varying(5) -- 制約違反の場合エラーになる insert into test_table (name) values (null); ERROR: domain test_domain does not allow null values
ラベル:
PostgreSQL
2015年4月9日木曜日
[PostgreSQL]JDBCの接続URLでカレントスキーマを指定する
JDBCで接続先のURLを指定するときに、接続パラメータでカレントのスキーマを指定する。
URLのフォーマット
カレントスキーマの接続パラメータは、currentSchemaなので、以下のようにURLの最後に指定する。jdbc:postgresql://localhost/test?currentSchema
実行例
// 以下のコードの場合、カレントスキーマはtest_schemaとなる dataSource.setUrl("jdbc:postgresql://localhost/test?currentSchema=test_schema"); try (Connection connection = dataSource.getConnection()) { final String schema = connection.getSchema(); System.out.println("schema = " + schema); // -> schema = test_schema }
ラベル:
JDBC,
PostgreSQL
2015年4月2日木曜日
[jdbc]カレントのスキーマ名を取得する
現在の接続のカレントスキーマ名を得る方法。
Connectionインタフェースにカレントスキーマ名を取得するメソッド(getSchema)があるのでそれを使えばよい。
※getSchemaは、Java7から追加された機能。
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"
ラベル:
Spring
2015年3月18日水曜日
JPA2.1のConverterを使ってEntityの属性の型変換
JPA2.1では、Entityの属性にConverterが設定できて、DBへの保存時や取得時に型変換を行うことができる。
AttributeConverterの総称型には、Entityの属性の型とDBへ挿入する型を指定する。
このコードの例だと、Sex列挙型の値をStringに変換してDBに登録します。
このアノテーションのconverter属性に、先ほど作成したConverterクラスを指定する。
使用方法
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
ラベル:
JPA2.1
2015年3月10日火曜日
PostgreSQLでシーケンスを使う
PostgreSQLでのシーケンス作成やシーケンスの操作関数の纏め。
一時的な(今のセッションだけに関連づいた)シーケンスを作成する。
シーケンス作成時に、temp(temporary)を指定すると今のセッションに関連づいた一時的なシーケンスが作られる。(セッションが終了すると、自動的に破棄される。)
既に同名のシーケンスが存在していた場合、一時的なシーケンスが優先されて既にあったシーケンスは見えなくなる。
currvalで最後にnextvalされた値を取得できる。
setvalで現在の値を変更できる。この例だと、値を999に変更している。
シーケンスの作成
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)
ラベル:
PostgreSQL
2015年3月8日日曜日
JPA2.1でストアドプロシージャ実行-REF CURSOR編
JPA2.1でストアドプロシージャ実行の続編。
ストアドプロシージャで開いたカーソル(REF CURSOR)をJPA経由で受け取るサンプル。
第2引数には、REF CURSORを受け取るEntityクラスを指定します。(Entity以外でもいいのか?)
executeでストアドプロシージャを実行して、getOutputParameterValueを呼び出してREF CURSORの結果を受け取ります。
ここで受け取ったEntityは管理対象となるので、状態を変更した場合は永続化層に反映される。
ストアドプロシージャで開いたカーソル(REF CURSOR)をJPA経由で受け取るサンプル。
実行対象のプロシージャ
パラメータは1つで、アウトパラメータでカーソルを返す。CREATE OR REPLACE PROCEDURE REF_CURSOR_TEST(cur out SYS_REFCURSOR) AS BEGIN OPEN CUR FOR SELECT * FROM USERS; END;
実行例
EntityManagerのcreateStoredProcedureQueryにストアドプロシージャ名を指定して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(); Listusers = (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
ラベル:
JPA2.1
2015年2月28日土曜日
[Oracle12C]with句でストアドプロシージャの定義
Oracle12Cでは、with句内でストアドプロシージャが定義できるようになりました。
使い方は、with句内にストアドプロシージャを定義して、それをクエリで使います。
この例では、パラメータの値を2倍するファンクションを定義して、それをselect句で使用しています。
実行結果です。ファンクションに渡した値が2され取得されているのがわかります。
使い方は、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
ラベル:
Oracle12
2015年2月25日水曜日
JPA2.1でストアドプロシージャ実行
JPA2.1のストアドプロシージャの実行機能の使い方サンプル。
StoredProcedureQueryのregisterStoredProcedureParameterを使用して、パラメータ情報を設定します。(パラメータの位置、イン or アウトパラメータや型情報を設定します。)
入力パラメータの場合には、setParameterを呼び出してストアドプロシージャに引き渡す値を設定します。
実行準備が整ったら、executeでストアドプロシージャを実行します。アウトパラメータはgetOutputParameterValueを使用して取得します。
CallableStatementと使い方は近いので特に迷うこともなさそうな気がします。
上の例で示したregisterStoredProcedureParameterの呼び出し部が、NamedStoredProcedureQueryのparameters属性に設定されているイメージになります。
生成した、Queryに対してパラメータ情報を設定して、executeを呼び出しアウトパラメータがある場合には結果を取得します。
上の例と比べると、パラメータの情報の設定がEntity側で定義されているのでなくなっています。
実行対象のプロシージャ
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
実行例
EntityManagerのcreateStoredProcedureQueryにストアドプロシージャ名を指定してStoredProcedureQueryを生成します。StoredProcedureQueryのregisterStoredProcedureParameterを使用して、パラメータ情報を設定します。(パラメータの位置、イン 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の呼び出し部が、NamedStoredProcedureQueryのparameters属性に設定されているイメージになります。
@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));
ラベル:
JPA2.1
2015年1月31日土曜日
[JPA]中間テーブルを作らずに直接結合させる
例えば1対多の依存があるエンティティをJPAで使用した場合、デフォルトでは中間テーブルを使った結合となる。
中間テーブルのテーブル名やカラム名を変更したい場合には、OneToManyとセットでJoinTableアノテーションを定義して任意の値に変更します。
どうしても過去の資産などを使わないとダメな場合なので中間テーブルを使えない場合には、OneToManyとセットでJoinColumnを定義してあげる。JoinColumnのname属性には、子供側テーブルの外部キーとなる項目名を定義します。
Entityを次のように変更すると、Userテーブルの子テーブルのTelテーブルにはUserテーブルを参照するための外部キー(user_id)が作成される。
テーブル定義は次のようになります。
Entityの関連定義
@OneToMany(cascade = {CascadeType.ALL}) public List<Tel> getTels() { return tels; }
テーブル定義
上のEntityの関連定義だと下のようなテーブル構造となります。中間テーブルのテーブル名やカラム名を変更したい場合には、OneToManyとセットでJoinTableアノテーションを定義して任意の値に変更します。
どうしても過去の資産などを使わないとダメな場合なので中間テーブルを使えない場合には、OneToManyとセットでJoinColumnを定義してあげる。JoinColumnのname属性には、子供側テーブルの外部キーとなる項目名を定義します。
Entityを次のように変更すると、Userテーブルの子テーブルのTelテーブルにはUserテーブルを参照するための外部キー(user_id)が作成される。
@OneToMany(cascade = {CascadeType.ALL}) @JoinColumn(name = "user_id") public List<Tel> getTels() { return tels; }
テーブル定義は次のようになります。
2015年1月25日日曜日
[JPA]ElementCollectionとCollectionTableアノテーション
JPAのEntityクラスに基本型のCollectionやListの属性を定義する場合は、ElementCollectionアノテーションを使用する。
ElementCollectionをつけることで、基本型(Entityじゃないクラス)をもつCollectionやListの属性を別のテーブルに保存してくれるようになる。
CollectionTableアノテーションを使うと、保存先のテーブルの名前などをカスタマイズできるようになる。
上のEntity定義の場合、CollectionTableアノテーションを使用していないのでデフォルトの定義でテーブルが作られます。
テーブル名は、親テーブルのテーブル名とその属性の名前(user_mail)となり、カラム名は属性名(mail)となります。
テーブル名を変更したい場合には、CollectionTableアノテーションのname属性を使用します。
カラム名を変更したい場合には、Columnアノテーションを使用します。
ElementCollectionをつけることで、基本型(Entityじゃないクラス)をもつCollectionやListの属性を別のテーブルに保存してくれるようになる。
CollectionTableアノテーションを使うと、保存先のテーブルの名前などをカスタマイズできるようになる。
例
Entityクラスの定義です。ソースコードが長くなると見づらくなるので、JPAのアノテーションはpublicフィールドに設定してアクセッサは定義していません。@Entity public class User { @Id @GeneratedValue public Long id; public String name; @ElementCollection public Setmail; }
上のEntity定義の場合、CollectionTableアノテーションを使用していないのでデフォルトの定義でテーブルが作られます。
テーブル名は、親テーブルのテーブル名とその属性の名前(user_mail)となり、カラム名は属性名(mail)となります。
テーブル名を変更したい場合には、CollectionTableアノテーションのname属性を使用します。
カラム名を変更したい場合には、Columnアノテーションを使用します。
2015年1月20日火曜日
[JDBC]DatabaseMetaDataから識別子の格納方法を判断する
JDBCのDatabaseMetaDataから、データベースの識別子(テーブル名やカラム名)がどのように格納されているか判断する方法。
DatabaseMetaDataを使ってカラム一覧や主キー一覧などを取得するときには、格納方法を判断して識別子を大文字や小文字に変換してから使用する必要がある。
引用符付きの識別子は、基本使わない(使っとこともない)ので、引用符なしの識別子が大文字、小文字どちらで格納されているかを判断するだけのサンプルコード
DatabaseMetaDataを使ってカラム一覧や主キー一覧などを取得するときには、格納方法を判断して識別子を大文字や小文字に変換してから使用する必要がある。
引用符付きの識別子は、基本使わない(使っとこともない)ので、引用符なしの識別子が大文字、小文字どちらで格納されているかを判断するだけのサンプルコード
// storesMixedCaseIdentifiersで引用符なしの識別子の格納方法を判断する。 // falseが返される場合は、大文字、小文字のどちらかで格納されている。 System.out.println("大文字小文字混在の引用符なし識別子を大文字小文字混在で格納する → " + metaData.storesMixedCaseIdentifiers()); // storesLowerCaseIdentifiersで小文字で格納されているかを判断する。 // trueが返される場合は、小文字で格納されている。 System.out.println("引用符なし識別子を小文字で格納する → " + metaData.storesLowerCaseIdentifiers()); // storesUpperCaseIdentifiersで大文字で格納されているかを判断する。 // trueが返される場合は、大文字で格納されている。 System.out.println("引用符なし識別子を大文字で格納する → " + metaData.storesUpperCaseIdentifiers());
実行結果
Oracleの場合
大文字で格納されていることが分かる大文字小文字混在の引用符なし識別子を大文字小文字混在で格納する → false 引用符なし識別子を小文字で格納する → false 引用符なし識別子を大文字で格納する → true
PostgreSQLの場合
小文字で格納されていることが分かる大文字小文字混在の引用符なし識別子を大文字小文字混在で格納する → false 引用符なし識別子を小文字で格納する → true 引用符なし識別子を大文字で格納する → false
2015年1月8日木曜日
PostgreSQLはStatement.RETURN_GENERATED_KEYSで全カラムの値が返される
PostgresSQLは、Statement生成時に、Statement.RETURN_GENERATED_KEYSを指定するとStatement#getGeneratedKeysでそのテーブルの全カラムの情報が取得される。
これは、Statement.RETURN_GENERATED_KEYSでStatementを生成すると、実行されるINSERT文に「RETURNING *」が付加されるため。
INSERT対象のカラムはNAMEのみで、ID列は自動採番カラム(SERIAL)となっています。
また、INSERT時に列挙していないカラムのデータもとれているのがわかります。
これが理由で、getGeneratedKeysが全カラムの情報を戻して来ています。
例えば、ID列のみを指定した場合実行されるSQL文は以下のようになります。
これは、Statement.RETURN_GENERATED_KEYSでStatementを生成すると、実行されるINSERT文に「RETURNING *」が付加されるため。
例
ID、NAME、BIRTHDAYの3つのカラムを持つテーブルにINSERT処理を行っています。INSERT対象のカラムはNAMEのみで、ID列は自動採番カラム(SERIAL)となっています。
PreparedStatement statement = connection.prepareStatement("insert into USER_INFO (name) VALUES (?)", Statement.RETURN_GENERATED_KEYS); statement.setString(1, "なまえ"); statement.executeUpdate(); ResultSet generatedKeys = statement.getGeneratedKeys(); if (generatedKeys.next()) { System.out.println(MessageFormat.format("カラム数 = [{0}]", statement.getMetaData().getColumnCount())); System.out.println("generatedKeys.getDate(3) = " + generatedKeys.getDate(3)); } connection.commit();
実行結果
実行すると、GeneratedKeysで取得したResultSetのカラム数は3となっていることがわかります。また、INSERT時に列挙していないカラムのデータもとれているのがわかります。
カラム数 = [3] generatedKeys.getDate(3) = null
実際に実行されたSQL文
ログを見てみると、実行されたINSERT文にRETURNING *が付加されているのがわかります。これが理由で、getGeneratedKeysが全カラムの情報を戻して来ています。
2015-01-08 12:48:11 JST LOG: duration: 0.000 ms parse: insert into USER_INFO (name) VALUES ($1) RETURNING * 2015-01-08 12:48:11 JST LOG: duration: 0.000 ms bind : insert into USER_INFO (name) VALUES ($1) RETURNING * 2015-01-08 12:48:11 JST DETAIL: parameters: $1 = 'なまえ' 2015-01-08 12:48:11 JST LOG: duration: 1.000 ms execute : insert into USER_INFO (name) VALUES ($1) RETURNING * 2015-01-08 12:48:11 JST DETAIL: parameters: $1 = 'なまえ' 2015-01-08 12:48:11 JST LOG: duration: 0.000 ms parse : insert into USER_INFO (name) VALUES ($1) RETURNING *
カラムインデックスやカラム名指定の場合は?
Statement生成時に自動生成カラムのインデックスや名前を指定した場合は、指定したカラムの情報のみ取得することが出来ます。例えば、ID列のみを指定した場合実行されるSQL文は以下のようになります。
2015-01-08 12:55:28 JST LOG: duration: 34.000 ms parse: insert into USER_INFO (name) VALUES ($1) RETURNING "id" 2015-01-08 12:55:28 JST LOG: duration: 0.000 ms bind : insert into USER_INFO (name) VALUES ($1) RETURNING "id" 2015-01-08 12:55:28 JST DETAIL: parameters: $1 = 'なまえ' 2015-01-08 12:55:28 JST LOG: duration: 0.000 ms execute : insert into USER_INFO (name) VALUES ($1) RETURNING "id" 2015-01-08 12:55:28 JST DETAIL: parameters: $1 = 'なまえ' 2015-01-08 12:55:28 JST LOG: duration: 0.000 ms parse : insert into USER_INFO (name) VALUES ($1) RETURNING "id"
ラベル:
PostgreSQL
2015年1月7日水曜日
OracleでJDBC経由でレコード追加後にROWIDを取得する
OracleのJDBCドライバで、Statement生成時にStatement.RETURN_GENERATED_KEYSフラグを指定している場合、自動生成キーは追加されたレコードのROWIDが返されます。
何が返されるかわからなかったので、試してみたらROWIDが取得出来たのですが、OracleのドキュメントにROWIDが取得されることが明記されていました。
以下が、ドキュメントの内容となります。
例えば、「AAAOKLAAEAAACQTAAA」のような値が出力されます。
何が返されるかわからなかったので、試してみたらROWIDが取得出来たのですが、OracleのドキュメントにROWIDが取得されることが明記されていました。
以下が、ドキュメントの内容となります。
java.sql.Statement キー列を明示的に指定しないと、Oracle JDBCドライバでは取り出す列を特定できません。特定できるのは、列名または列索引の配列が使用されている場合です。 ただし、int型のStatement.RETURN_GENERATED_KEYSフラグが使用されている場合、Oracle JDBCドライバではこれらの列を特定できません。int型フラグを使用して自動生成キーが戻るように指定した場合は、ROWID擬似列がキーとして戻ります。 このROWIDは、ResultSetオブジェクトからフェッチして、他の列を取り出すために使用できます。
例
PreparedStatement statement = connection.prepareStatement("INSERT INTO HOGE VALUES (?, ?)", Statement.RETURN_GENERATED_KEYS); statement.setLong(1, 1); statement.setString(2, "name"); statement.executeUpdate(); ResultSet generatedKeys = statement.getGeneratedKeys(); if (generatedKeys.next()) { ROWID rowid = (ROWID) generatedKeys.getRowId(1); System.out.println(rowid.stringValue()); }上のコードを実行した場合、標準出力にはROWIDを文字列表現に変換した値が出力されます。
例えば、「AAAOKLAAEAAACQTAAA」のような値が出力されます。
登録:
投稿 (Atom)