2015年12月15日火曜日

psql上でOracleのSQL*Plusのeditみたいなことをする

psql上では、\eで前回実行したSQL文をエディタ上で編集できるようになる。
エディターは、EDITOR環境変数に設定されたパスのものが開かれる。

2015年12月14日月曜日

MySQLは ON UPDATE CURRENT_TIMESTAMPで更新日時を自動的に更新できる

MySQLのタイムスタンプ型では、DEFAULT句に追加でon 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)

2015年12月8日火曜日

DBMS_XPLAN.DISPLAY_CURSORを使って最近実行したSQL文のアクセスプランを取得

マニュアルによると、「DISPLAY_CURSORファンクションはカーソル・キャッシュにロードされている任意のカーソルの実行計画を表示します」とあるので、
実行されてからあまり時間がたっていない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パッケージおよびタイプ・リファレンス」を見るとわかる。


2015年12月7日月曜日

HibernateのImplicitNamingStrategyを使って識別子に任意のプレフィックス等を付加する

Hibernateでテーブル名やカラム名などに一律任意のサフィックスやプレフィックスを追加するには、ImplicitNamingStrategyを実装したクラスを作成し設定を追加しするだけでよい。
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))

2015年12月6日日曜日

HibernateのPhysicalNamingStrategyを使って識別子に任意のプレフィックス等を付加する

Hibernateでテーブル名やカラム名などに一律任意のサフィックスやプレフィックスを追加するには、PhysicalNamingStrategyを実装したクラスを作成し設定を追加しするだけでよい。

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))

2015年12月5日土曜日

Hibernate5のhibernate.auto_quote_keywordでキーワードを識別子に使えるように

hibernate.auto_quote_keywordプロパティの値をtrueに設定すると、キーワードを識別子に使った場合、自動的にクォートで囲ってくれるようになる。
これで、キーワードとかぶるエンティティを作ったとしても、@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を書く必要が出てきた時にはまりそうだなぁと思ったり。