DB2 Express-C 10.1.2がリリースされました。
12月 2012のアーカイブ
DB2 Express-C 10.1.2がリリースされました。 Leave a comment
EclipseLinkのLoggingを利用したJPQLの発行SQLの確認 Leave a comment
1.発行SQLのログ出力方法
デバッグおよびテストを行う場合などで、JPQLの変換後のSQLの情報が必要なる場合があります。persistence.xmlに2行追加するだけです。
persistence.xmlに10行目と、11行目を追加します。10行目は、JPQLの発行したSQL文をログ出力するために、「eclipselink.logging.level.sql」の項目を「FINE」に設定します。また11行目は、ホスト変数をログ出力するかどうかの設定です。
図 1. persistence.xmlファイル例
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="testPU" transaction-type="JTA"> <jta-data-source>jdbc/db2</jta-data-source> <exclude-unlisted-classes>false</exclude-unlisted-classes> <properties> <property name="eclipselink.logging.level.sql" value="FINE"/> <property name="eclipselink.logging.parameters" value="true"/> </properties> </persistence-unit> </persistence>
図 2. 実行JPQL
Query query = em.createQuery("SELECT p FROM Product p WHERE p.id = :id and p.productName = :productName"); query.setParameter("id", 10); query.setParameter("productName", "パソコン"); query.getResultList();
図 3. 出力ログ
詳細レベル (低): SELECT ID, CREATE_PROGRAM_ID, CREATE_TIMESTAMP, CREATE_USER_ID, MAKER, PRODUCT_NAME, PRODUCT_SPECIFICATION_CONTENT, PRODUCT_SPECIFICATION_DIV, RELEASE_DATE, UPDATE_PROGRAM_ID, UPDATE_TIMESTAMP, UPDATE_USER_ID FROM PRODUCT WHERE ((ID = ?) AND (PRODUCT_NAME = ?)) bind => [10, パソコン]
2.総評
プログラム上でホスト変数のログ出力を行わずに、EclipseLinkのLogging設定を行うだけで、ホスト変数の確認ができるのがとても便利です。使用するフレームワークによりますが、ホスト変数のログ出力を、コーディングしなければならないこともあるので、とても便利です。
お手数ですが、この記事が、あなたのお役に立った場合は、ぜひ、「いいね」ボタンを押してくれるとうれしいです。自分自身のモチベーションの維持と、このブログを読んでくださる、他の読者にも参考になると思います。
3.参照
EclipseLink/Examples/JPA/Logging Log Level Configuration
©中條勝徳 and okulejp.com, 2012.
DB2のLBACみたいなことをpureXMLを使用して実現する Leave a comment
1.説明
DB2のLBACは便利ですが、それと同じようなことを、pureXMLで実現する方法を説明します。
ロールの構造は、下記のような組織構造をとるものとします。説明のため単純化してありますが、すきなだけ数だけ階層化できます。商品情報テーブルのROLE列には、そのデータを表示できるロールが設定されています。商品情報のロールに、東京支店の社員が設定されていた場合は、そのデータを見ることができるのは、東京支店の社員、東京支店長、社長です。商品情報のロールに、千葉支店の社員が設定されていた場合は、そのデータを見ることができるのは、千葉支店の社員、千葉支店長、社長です。XML形式の会社組織のデータは、SECURITY_ROLE_TREEテーブルのCONTENT列に格納されています。
図 1. 会社組織図
図 2. XML形式の会社組織
<roles> <role id="k1" name="社長"> <role id="k1_1" name="東京支店長"> <role id="k1_1_1" name="社員"></role> </role> <role id="k1_2" name="千葉支店長"> <role id="k1_2_1" name="社員"> </role> </role> </role> </roles>
図 3. 商品情報(PRODUCT_INFO)テーブルの全部のデータ
2.行に対するアクセス制御
11行目の$MYROLEは、6行目のMYROLEを示します。11行名の$CONTENT変数は、SECURITY_ROLE_TREEテーブルのCONTENT列を示します。13行目の$ROLE変数は、PRODUCT_INFOテーブルのROLE列を示します。このように、pureXMLの便利なところは、DB2により列名と同じ名前の変数が、自動的に作成されことです。6行目のMYROLEは、WEBアプリケーションのプログラムで直下書きになります。(「’k_1_1’」の部分が該当。ログイン時に、ログインユーザのロールを、セッション情報に保持するなどすれば、プログラムで取得することは、容易なはずです。)
「descendant-or-self」軸の利用が肝です。13行のif文で、自分自身のロールが、商品情報のロールと同じか、または、子孫方向に商品情報のロールがあれば、1の値を持つシーケンスを返し、そうでなければ、空シーケンスを返します。またXMLEXISTS関数は、与えられた式が空シーケンスであればFALSEを、それ以外はTRUEを返します。
図 4. 行に対して、ロールのルールを適用する場合。(東京支店の社員の場合。)
SELECT PI.ID, PI.NAME, PI.PRICE, PI.ROLE FROM PRODUCT_INFO PI, SECURITY_ROLE_TREE SRT, ( SELECT 'k1_1_1' AS MYROLE FROM SYSIBM.SYSDUMMY1 ) SD WHERE XMLEXISTS(' let $myRolePosition := $CONTENT//*[@id=$MYROLE] return if($myRolePosition/descendant-or-self::*[@id = $ROLE]) then (1) else () ')
図 5. 結果(千葉支店の社員のデータが表示されません。)
図 6. 行に対して、ロールのルールを適用する場合。(社長の場合。)
SELECT PI.ID, PI.NAME, PI.PRICE, PI.ROLE FROM PRODUCT_INFO PI, SECURITY_ROLE_TREE SRT, ( SELECT 'k1' AS MYROLE FROM SYSIBM.SYSDUMMY1 ) SD WHERE XMLEXISTS(' let $myRolePosition := $CONTENT//*[@id=$MYROLE] return if($myRolePosition/descendant-or-self::*[@id = $ROLE]) then (1) else () ')
図 7. 結果(すべてのデータが表示されます。)
3.列に対するアクセス制御
図 8. 列に対して、ロールのルールを適用する場合で、「*****」を返す場合。(東京支店の社員の場合。)
SELECT PI.ID, PI.NAME, XMLCAST( XMLQUERY(' let $myRolePosition := $CONTENT//*[@id=$MYROLE] return if($myRolePosition/descendant-or-self::*[@id = $ROLE]) then $PRICE else "*****" ') AS VARCHAR(9)), PI.ROLE FROM PRODUCT_INFO PI, SECURITY_ROLE_TREE SRT, ( SELECT 'k1_1_1' AS MYROLE FROM SYSIBM.SYSDUMMY1 ) SD
図 9. 結果(千葉支店の社員のデータがマスキングされています。)
図 10. 列に対して、ロールのルールを適用する場合で、NULLを返す場合。(東京支店の社員の場合。)
SELECT PI.ID, PI.NAME, XMLCAST( XMLQUERY(' let $myRolePosition := $CONTENT//*[@id=$MYROLE] return if($myRolePosition/descendant-or-self::*[@id = $ROLE]) then $PRICE else () ') AS VARCHAR(9)), PI.ROLE FROM PRODUCT_INFO PI, SECURITY_ROLE_TREE SRT, ( SELECT 'k1_1_1' AS MYROLE FROM SYSIBM.SYSDUMMY1 ) SD
図 11. 結果(千葉支店の社員のデータがNULLになっています。)
図 12. 列に対して、ロールのルールを適用する場合で、NULLを返す場合。(社長の場合。)
SELECT PI.ID, PI.NAME, XMLCAST( XMLQUERY(' let $myRolePosition := $CONTENT//*[@id=$MYROLE] return if($myRolePosition/descendant-or-self::*[@id = $ROLE]) then $PRICE else () ') AS VARCHAR(9)), PI.ROLE FROM PRODUCT_INFO PI, SECURITY_ROLE_TREE SRT, ( SELECT 'k1' AS MYROLE FROM SYSIBM.SYSDUMMY1 ) SD
図 13. 結果(マスキングされていません。)
4.総評
複雑な組織の階層構造に対応したアクセス制御機能が、DB2 pureXMLを使用することで、いとも簡単に実現できてしまう。この記事を読んだ、読者のみなさんの中で、これをヒントにして、もっといいアイディアが浮かぶかもしれません。そう思わせるほど、DB2 pureXMLには、大きな可能性を秘めていると思います。この方法は、WEBアプリケーションからのアプローチです。いいかえれば、ROLEの定義をデータベースで行うのか、WEBアプリケーションで行うかの違いです。
5.付録
ここで使用した、テーブル定義とデータです。
CREATE TABLE PRODUCT_INFO ( ID INT NOT NULL, NAME VARCHAR(50), PRICE INT NOT NULL, ROLE VARCHAR(10) NOT NULL ); ALTER TABLE PRODUCT_INFO ADD CONSTRAINT PK_ID PRIMARY KEY(ID); INSERT INTO PRODUCT_INFO (ID, NAME, PRICE, ROLE) VALUES (1, 'ブルーレイ', 120000, 'k1_1_1'); INSERT INTO PRODUCT_INFO (ID, NAME, PRICE, ROLE) VALUES (2, 'ビデオカメラ', 60000, 'k1_2_1'); CREATE TABLE SECURITY_ROLE_TREE ( CONTENT XML NOT NULL ); INSERT INTO SECURITY_ROLE_TREE (CONTENT) VALUES ( ' <roles> <role id="k1" name="社長"> <role id="k1_1" name="東京支店長"> <role id="k1_1_1" name="社員"></role> </role> <role id="k1_2" name="千葉支店長"> <role id="k1_2_1" name="社員"> </role> </role> </role> </roles> ');
©中條勝徳 and okulejp.com, 2012.