struts.xml の struts.constant 要素
struts.constant とは
Struts2 がデフォルトで設定している要素を上書きする要素
Constants provide a simple way to customize a Struts application by defining key settings that modify framework and plugin behavior.
デフォルトの要素とその値一覧はどこでわかるか
例
<struts> <constant name="struts.ui.theme" value="simple" /> <!-- struts のテーマを使わない(A) --> . . </struts>
(A) 参考情報
Dependency Injection
「DI パターン」と これを利用した「DI コンテナ」ってのがある
「DI コンテナ」使いたいけど「DI パターン」って何?状態 > 荒いけどまとめる
ここにあることが全部ではないので、正確なことは最下記リンクへ
DI パターンとは何か
「インスタンス変数にオブジェクトを割当てること」
「或るクラスに依存するオブジェクトの作成を、他の誰かの責務にすること」
James Shore 曰く
「大げさな言い回しだ(良い意味でと付け加えておく)」
まず DI パターンが出来るまでの実情
概要
- 一般的に或るクラスは 多数の他クラスオブジェクト (以下 "dependencies")
を属性として持っている - "dependencies" は要求/仕様変更/テスト実行時に
メンテナンス、テストによってはスタブ/モックの用意が必要- ...この「メンテナンス、スタブ/モック用意」
やり易い方法がないものか?
- ...この「メンテナンス、スタブ/モック用意」
詳細(例を交え)
- 下サンプルの SmileCar について、 Wheel/Engine の "dependencies" は vendorABC である
- もし、"dependencies" が vendorABC ではなく vendorXYZ になった場合
- "dependencies" をそれぞれ vendorABCWheel(), vendorABCEngine() を変更する
- ... が、これって本当に良いやり方?
- メンテは面倒(影響範囲調査など、巨大クラスになれば尚更)
- 責任範囲という面でも SmileCar(車自身) が
"dependencies"(Wheel(タイヤ) や Engine(エンジン)) の「生成」にまで
責務を持つべき?
- ... が、これって本当に良いやり方?
(before)
(Whee/Engine がそれぞれ 抽象クラスを具象化したクラスであったり
interface を implements している前提をかなりすっ飛ばしているのは意図的)
public class SmileCar { private DatabaseUtil db; private Wheel wheel = new vendorABCWheel(); // 仕様変更でメンテが必要になりそう private Engine engine = new vendorABCEngine(); // 同上 public Car() {} public showWheelNameAndEngineName() { try { // テストの時はどこの DB 見にいく? // getConnection() 内で 本番の時は... テストの時は... // って 分岐してないですよね? db.getConnection(); System.out.println(wheel.getWheelName()); System.out.println(engine.getEngineName()); } catch(Exception e) { e.printStacTrace(); } } }
(after)
public class SmileCar { private DatabaseUtil db; private Wheel wheel = new vendorXYZheel(); // vendorABC > vendorXYZ private Engine engine = new vendorXYZEngine(); // 同上 public Car() { db = new DatabaseBase(); } public showWheelNameAndEngineName() { try { // テストの時はどこの DB 見にいく? // getConnection() 内で 本番の時は... テストの時は... // って 分岐してないですよね? db.getConnection(); System.out.println(wheel.getWheelName()); System.out.println(engine.getEngineName()); } catch(Exception e) { e.printStacTrace(); } } }
DI パターンの登場
やることは単純に、冒頭通り
「インスタンス変数にオブジェクトを割当てること」
上の SmileCar (仕様変更後) を例にする
インスタンス変数にオブジェクトを割当てる
まずは Wheel/Engine オブジェクトをインスタンス変数に割当てる
これで、Wheel/Engine の仕様変更があっても「SmileCar クラス自身」は手をつけなくていい
public class SmileCar { private DatabaseUtil db; private Wheel wheel; private Engine engine; public Car(Wheel wheel, Engine engine) { db = new DatabaseBase(); this.wheel = wheel; this.engine = engine; } public showWheelNameAndEngineName() { try { db.getConnection(); System.out.println(wheel.getWheelName()); System.out.println(engine.getEngineName()); } catch(Exception e) { e.printStacTrace(); } } }
次に db オブジェクトをインスタンス変数に割当てる
クラス設計がいけてないので「え?」となるが、あくまで一例
public class SmileCar { private DatabaseUtil db; private Wheel wheel; private Engine engine; public Car(DatabaseUtil db, Wheel wheel, Engine engine) { this.db = db; this.wheel = wheel; this.engine = engine; } public showWheelNameAndEngineName() { try { db.getConnection(); System.out.println(wheel.getWheelName()); System.out.println(engine.getEngineName()); } catch(Exception e) { e.printStacTrace(); } } }
DI 自体は終わったが、データベースモック化はどんな風に?
概略コードは以下
- SmileCar は何もしなくて良い
- 本番DB に繋ぐかモックしたものを作るかは MockDatabase ないの処理で決める
public MockDatabase extends DatabaseUtil { // モック用のフィールド、メソッド用意 } public class SmilCarTest { public void TestShowWheelNameAndEngineName() { MockDatabase mockDatabase = new MockDatabase() SmileCar smileCar = new SmileCar( mockDatabase, new vendorXYZWheel(), new venXYZEngine() ); smileCar.showWheelNameAndEngineName(); smileCar.sometest(); } } public class SmileCar { private DatabaseUtil db; private Wheel wheel; private Engine engine; public Car(DatabaseUtil db, Wheel wheel, Engine engine) { this.db = db; this.wheel = wheel; this.engine = engine; } public showWheelNameAndEngineName() { try { // (特に指定がなければ) 本番用の DB を使う // テスト用の DB(モック) を使う > SmileCarTest#TestShowWheelNameAndEngineName db.getConnection(); System.out.println(wheel.getWheelName()); System.out.println(engine.getEngineName()); } catch(Exception e) { e.printStacTrace(); } } }
「依存性の注入」について思うこと(感想レベル)
DI[Dependency Injection] が 「依存性の注入」と訳されている
少しでも英語リソースやそこにある議論を眺めたら "dependency"
が「依存」そのものじゃなくて
「オブジェクト」やこれに近しい「モノそれ自身」だと気づくはず
引用参考リンク
http://www.jamesshore.com/Blog/Dependency-Injection-Demystified.html
http://stackoverflow.com/questions/130794/what-is-dependency-injection?rq=1
転載(他):Why Use Stored Procedure
- Pro
- added layer of security that can be placed on the database from
- the data functionality making it easier to manage, document, and maintain
- improved performance
- multiple client applications to have consistent database routines
- Con
- to many calls to sql-server
- hard to test
One of the most beneficial reasons to use stored procedures is the added layer of security that can be placed on the database from the calling application. If the user account created for the application or web site is configured with EXECUTE permissions only then the underlying tables cannot be accessed directly by the user account. This helps prevent hacking directly into the database tables. The risk of a hacker using the user account to run a stored procedure that has been written by you is far safer than having the user account have full insert, update and delete authority on the tables directly. Another advantage to using stored procedures, especially in medium to large scale web sites or applications, is the data functionality is separated from the application making it easier to manage, document, and maintain. For example, if an application updates the customer table in ten different places, there can be a single stored procedure and a standard procedure call from the application for this functionality. If a change needs to be made to the way a customer record is managed, then the SQL statements only need to be changed in one place, in the database layer. In most cases, the application is not affected unless the procedure call requires modification. Changing the procedure call is also easier, because a standard call is already in place. Managing the data in the data layer avoids having to keep track of embedded SQL calls that may be different in each place, whenever a change is required. Stored procedures provide improved performance because fewer calls need to be sent to the database. For example, if a stored procedure has four SQL statements in the code, then there only needs to be a single call to the database instead of four calls for each individual SQL statement. Of course there is always a tradeoff. There is an increased workload on the server side that needs to be taken into account. Another advantage to using stored procedures allows for multiple client applications written in any language and running on any platform to have consistent database routines. Each application uses the same procedures and simply has to embed a standard procedure call for the language in the calling program.
自己結合はいつ使うか
自テーブル各レコードの特性を
単純な射影や選択では比較できない場合に使う
ここでは CASE, 相関サブクエリ, 内部/外部結合などに言及しないとする
(Web 記事や書籍を眺めてみたのだが、なかなか扱いがよくわからん構文
勉強しきれてないが、これ使うのであれば
OLAP 関数使ったりテーブル設計見直したほうが、良いのでは)
例
テーブル論理要件
- 会社員テーブル(employees) が以下の属性を持っている
- 会社員ID(employeeid)
- 上司ID(bossid)
- 会社員名
- 会社員テーブルは以下の特性を持つ
- その会社員が誰を上司とするかを判断する為に
- 上司ID が 会社員ID を参照
- ただし、上司を持たない会社員は bossid が 0 である
- bossid = 0 のデータは内部で事前に用意していおく
- その会社員が誰を上司とするかを判断する為に
テーブル物理要件
CREATE TABLE employees ( employeeid INTEGER NOT NULL bossid INTEGER NOT NULL DEFAULT 0, /* 0 means "I don't have boss" */ name VARCHAR(64) NOT NULL, PRIMARY KEY (employeeid), FOREIGN KEY bossid REFERENCES(employees), CONSTRAINT employees_identifier_validation CHECK((employeeid >= 0) AND (bossid >= 0)) );
SELECT * FROM employees;
employeeid | bossid | name ------------+--------+--------------------- 0 | 0 | you have not a boss 1 | 0 | Sakano 2 | 1 | Yoneda 3 | 1 | Umino 4 | 3 | Saejima 5 | 4 | Kobayashi
要望
各社員の名前, その社員の上司名
を表示する SQL を書いて欲しい
内部でダミーの社員ってのを用意しているらしいけど
そのデータは表示しないように
実行 SQL
SELECT EMP_MAIN.name AS "When employee is...", EMP_SUB.name AS "Then Boss is..." FROM employees AS EMP_MAIN, employees AS EMP_SUB WHERE EMP_MAIN.employeeid <> 0 AND EMP_MAIN.bossid = EMP_SUB.employeeid;
When employee is... | Then Boss is... ---------------------+--------------------- Sakano | you have not a boss Yoneda | Sakano Umino | Sakano Saejima | Umino Kobayashi | Saejima
蛇足
SELECT EMP_MAIN.bossid, EMP_MAIN.name AS "When employee is...", EMP_SUB.employeeid, EMP_SUB.name AS "Then Boss is..." FROM employees AS EMP_MAIN, employees AS EMP_SUB
EMP_MAIN.bossid | When employee is... | EMP_SUB.employeeid | Then Boss is... --------+---------------------+------------+--------------------- 0 | you have not a boss | 0 | you have not a boss 0 | you have not a boss | 1 | Sakano 0 | you have not a boss | 2 | Yoneda 0 | you have not a boss | 3 | Umino 0 | you have not a boss | 4 | Saejima 0 | you have not a boss | 5 | Kobayashi 0 | Sakano | 0 | you have not a boss 0 | Sakano | 1 | Sakano 0 | Sakano | 2 | Yoneda 0 | Sakano | 3 | Umino 0 | Sakano | 4 | Saejima 0 | Sakano | 5 | Kobayashi 1 | Yoneda | 0 | you have not a boss 1 | Yoneda | 1 | Sakano 1 | Yoneda | 2 | Yoneda 1 | Yoneda | 3 | Umino 1 | Yoneda | 4 | Saejima 1 | Yoneda | 5 | Kobayashi 1 | Umino | 0 | you have not a boss 1 | Umino | 1 | Sakano 1 | Umino | 2 | Yoneda 1 | Umino | 3 | Umino 1 | Umino | 4 | Saejima 1 | Umino | 5 | Kobayashi 3 | Saejima | 0 | you have not a boss 3 | Saejima | 1 | Sakano 3 | Saejima | 2 | Yoneda 3 | Saejima | 3 | Umino 3 | Saejima | 4 | Saejima 3 | Saejima | 5 | Kobayashi 4 | Kobayashi | 0 | you have not a boss 4 | Kobayashi | 1 | Sakano 4 | Kobayashi | 2 | Yoneda 4 | Kobayashi | 3 | Umino 4 | Kobayashi | 4 | Saejima 4 | Kobayashi | 5 | Kobayashi (36 rows)
WHERE EMP_MAIN.employeeid <> 0 AND EMP_MAIN.bossid = EMP_SUB.employeeid;
When employee is... | Then Boss is... ---------------------+--------------------- Sakano | you have not a boss Yoneda | Sakano Umino | Sakano Saejima | Umino Kobayashi | Saejima
相関サブクエリはいつ(一番)使われるか
(一番)使われるのは「レコードの存在チェック」
Web 記事を色々眺めて、私なりの答え
(業務システムの SQL を見ていると「このオペレーションは、いつ、何のために使うのか」
を見失うので一回整理したい)
例
- 利用者が存在し、各属性を持つ
- 利用者 ID
- 年齢(0 以上 100 以下)
- 名前(255 文字以内の可変長)
CREATE TABLE users ( userid INTEGER NOT NULL, age INTEGER NOT NULL CHECK (age BETWEEN 0 AND 100), name VARCHAR(255) NOT NULL, PRIMARY KEY(userid) );
userid | age | name --------+-----+----------- 1 | 26 | Akayama 2 | 30 | Urano 3 | 25 | Kitano 4 | 30 | Mashita 5 | 33 | Okajima 6 | 26 | Iseno 7 | 22 | Kashima 8 | 28 | Takahashi
集計関数を使わない場合
同年齢のユーザー一覧を表示する
SELECT * FROM users AS USR WHERE EXISTS ( SELECT 1 FROM users AS USRSUB WHERE USR.userid <> USRSUB.userid AND USR.age = USRSUB.age );
userid | age | name --------+-----+--------- 6 | 26 | Iseno 1 | 26 | Akayama 4 | 30 | Mashita 2 | 30 | Urano
集計関数を使う場合
総ユーザーにおける平均年齢以上の
ユーザー一覧を表示する
SELECT AVG(age) FROM users;
avg ---- 27.5
SELECT * FROM users AS USR WHERE age >= ( SELECT AVG(USRSUB.age) FROM users AS USRSUB );
userid | age | name --------+-----+----------- 2 | 30 | Urano 4 | 30 | Mashita 5 | 33 | Okajima 8 | 28 | Takahashi
色々眺めた記事のうち一つ