S2JDBCを改造(order byを柔軟に設定できるようにする)

参考

通常のorder by 指定

例えばここで使ったようなテーブルがあるとして、
postgresqlだと

create table employee (
  id SERIAL NOT NULL, 
  name TEXT NOT NULL
);

id順に取得したい場合はSQL

SELECT * FROM employee ORDER BY id;

のようになります。
で、S2JDBCでこれをやろうとすると、

List<Employee> employees = jdbcManager.from(Employee.class).orderBy("id").getResultList();

のように書けばいいんですが、この時実際に発行されるSQL

select T1_.ID as C1_, T1_.NAME as C2_ from EMPLOYEE T1_ order by C1_

のようになります。
※ 例えばlog4Jを使っている場合、log4j.logger.org.seasar.extension.jdbc=DEBUGといった感じで設定されていれば表示されます。

何がしたいか

特定のデータを優先的に一覧の先頭に持って来たいという場合があるとします。
例えば、自分のIDを持っているデータ。
この時、下記のようにすればID=2のデータを先頭に持ってくることが可能です。

SELECT * FROM employee ORDER BY id <> 2, id;

※ すべてのRDBMSで実行可能かどうかは調べてないです。。。
※ 「<>」を使ったのは、bool型の場合false->trueの順番でソートされるためです

これを単純にS2JDBCでやろうとすると、

List<Employee> employees = jdbcManager.from(Employee.class).orderBy("id <> 2, id").getResultList();

のようになりますが、これで実行するとエラーになります。
どうやら実際に発行されるSQLのorder by句でカラムのエイリアスを使った式がダメなようです。(C1_ <> 2の部分)

select T1_.ID as C1_, T1_.NAME as C2_ from EMPLOYEE T1_ order by C1_ <> 2, C1_

で、これはPostgreSQLの話で、例えばMySQLだとこのSQLを正しく実行できます。

create table employee (
  id INTEGER auto_increment PRIMARY KEY, 
  name VARCHAR(128) NOT NULL
);
insert into employee (name) values ('hogeo'),('hogetaro');
select T1_.ID as C1_, T1_.NAME as C2_ from EMPLOYEE T1_ order by C1_ <> 2, C1_;

解決策

上の話からすると、MySQL使えばいいってことなんですが、
PostgreSQLから離れられないってこともありますのでちょっと手を加えてみました。
このあたりの制御をしているのが、org.seasar.extension.jdbc.query.AutoSelectImplクラスなんで直接ソースコードを変えてもいいんですが今回はこのクラスを拡張する形でやってみました。
どこを変えたいかというと、prepareOrderBy()メソッドです。

    protected void prepareOrderBy() {
        if (StringUtil.isEmpty(orderBy)) {
            return;
        }
        orderByClause.addSql(convertCriteria(orderBy, true));
    }

ここで、convertCriteriaメソッドを呼んでいますが、これは第1引数の文字列内で指定されたプロパティ名をT1_.idだとかC1_だとかに置換してくれるメソッドです。第2引数がtrueの場合はC1_のような形(カラムのエイリアス名)に、falseの場合はT1_.idのような形(テーブルのエイリアス名)に置換します。
なので、今回の目的としてはtrueではなくfalseを指定できればよいということです。

package kamegu.common.jdbc;

import org.seasar.extension.jdbc.manager.JdbcManagerImplementor;
import org.seasar.extension.jdbc.query.AutoSelectImpl;
import org.seasar.framework.util.StringUtil;

public class MyAutoSelectImpl<T> extends AutoSelectImpl<T> {

	public MyAutoSelectImpl(JdbcManagerImplementor jdbcManager, Class<T> baseClass) {
		super(jdbcManager, baseClass);
	}

	@Override
	protected void prepareOrderBy() {
        if (StringUtil.isEmpty(orderBy)) {
            return;
        }
        orderByClause.addSql(convertCriteria(orderBy, false)); //true -> false
	}
}

AutoSelect自体はSeasar2コンポーネントとして登録されているわけではないため、MyAutoSelectImplに置き換えるためにはそれを呼び出しているJdbcManager(コンポーネント登録されています)から作り直す必要がありました。

package kamegu.common.jdbc;

import org.seasar.extension.jdbc.AutoSelect;
import org.seasar.extension.jdbc.manager.JdbcManagerImpl;

public class MyJdbcManagerImpl extends JdbcManagerImpl {

	public <T> AutoSelect<T> from(Class<T> baseClass) {
        return new MyAutoSelectImpl<T>(this, baseClass).maxRows(maxRows)
                .fetchSize(fetchSize).queryTimeout(queryTimeout);
    }
}

必要なクラスは作成したので、後は設定です。
s2jdbc.diconを1行書き換えます。

    <component name="jdbcManager" class="kamegu.common.jdbc.MyJdbcManagerImpl">

log4Jを使っている場合は、デバッグ情報としてSQLを表示されるためにlog4j.propertiesに1行追加します。

log4j.logger.kamegu.common.jdbc=DEBUG

これで実行すると発行されるSQLは下記のようになり、正しく取得できました。

select T1_.ID as C1_, T1_.NAME as C2_ from EMPLOYEE T1_ order by T1_.ID <> 2, T1_.ID

[CodeIQ] 平成変換問題解きました(CodeIQ×はてな エンジニア夏祭り2013)

問題

オリジナルはこれ
http://antimon2.hatenablog.jp/entry/2013/08/07/231634

実際の問題のページは見れなくなってるようです。

僕の解答はここ
https://gist.github.com/kamegu/6929169

解き方

解説を見てもやっぱり基本は総当りのようです。
ただし、それだとあまりにもパターン数が増えすぎてしまうので工夫が必要です。

工夫

僕のやった工夫は

  • 桁数制限

名前のとおりです。桁数が大きくなりすぎると計算が大変になるのでそれらはあきらめます(速度優先)。ただし、あまり厳しくしすぎないこと。

  • 似たものを切り捨てる

例えば
A->B->C->Z
A->B->D->Z
A->B->E->F->Z
とあったとして下の2つは切り捨てました。最短経路という意味では上の2つは最短経路となり得ますが、全部列挙する必要もないため、そうしてます。

  • 双方向

これは実際にはやってませんが検討はしました。

さらに

今回

[2]014->[4]014->[16014]->(256)4(4)(81)(9)[6]->1(64)[2]93(36)->(1849)(36)->(4)(36)->26

という答えを見つけましたが、よく見るとこれには無駄な箇所が含まれていました。
256448196から184936に移るところで
(256)4(4)(81)(9)[6]->1(64)[2]93(36)->184936
こうしてるところを
(256)44(81)(9)[6]->1(64)493(36)->184936
こう出来ます

この辺も上の「似たものを切り捨てる」のところでステップ数だけでなく「平方根の回数+2乗の回数」も比較して切捨てていれば解決できたのかなと思います。

面白い問題でした。

追記

解説記事が出てました
https://codeiq.jp/magazine/2013/10/404/
最短7ステップ到達者に名前(kamegu)が載ってました

JSFでGETパラメータを使うときのメモ

f:viewParamとf:viewActionを使う。これらをf:metadataタグで囲む。
f:viewActionはJSF2.2で追加されたっぽい。それまではf:event type="preRenderView"を使っていた。
http://jdevelopment.nl/jsf-22/#758
http://www.oracle.com/technetwork/articles/java/jsf22-1377252.html

ただ、ここでかなりはまることに。はまったのは2つ

1個目

まず、動かない。
これはどうやらバグらしいのだが、NetBeansで作業しているとfタグを使おうとすると自動で
xmlns:f="http://xmlns.jcp.org/jsf/core"
を追加してくれるんですが、どうやら
xmlns:f="http://java.sun.com/jsf/core"
にしないとダメらしい。
https://java.net/jira/browse/JAVASERVERFACES-2868
java.sun.comは古いnamespaceで新しくxmlns.jcp.orgになったみたいなんですが、動かないなんて。。。
JSF2.2.1で修正されたようなのでそれを使えば大丈夫のはず(未確認)。

2個目

f:errormessagesタグを使ってると

The metadata component needs to be nested within a f:metadata tag. Suggestion: enclose the necessary components within 

みたいなエラーが画面に表示される。
これもバグっぽい
https://java.net/jira/browse/JAVASERVERFACES-2803
どうやら無視して問題ないようです。


JSFではあまりGETパラメータを使ってページ遷移させることがないのかなと思ってしまう。

デブサミ関西 2013いってきました

昨日9/20に神戸で行われたイベントに参加してきました。
こういうイベントは初参加。
http://event.shoeisha.jp/devsumi/20130920/


出席したのは

  • 「開発 / デザインのプロなら知っておくべき  互いの能力を最大に生かす方法」 江川 崇/矢野 りん
  • 「チケット駆動でプロジェクトチームを加速せよ」 鈴木雄介
  • JavaScript Security beyond HTML5」 はせがわようすけ
  • 「Employable Code~採用されないコードを書いていませんか?」 大成弘子
  • JavaScript時代のJavaJava x Webを取り巻く熱いムード」 山本裕介

の5セッション

今回はなるべく自分の関連するようなセッションを選んで出席するようにしました。

とにかく刺激を受けました。


以下メモ

開発 / デザインのプロなら知っておくべき  互いの能力を最大に生かす方法

ちゃんとした意味でデザイナーの人と仕事したことないので今後のために。
「お互いを理解しあう」とかっていうのは異なる職種間ではもちろん、エンジニア間でも必要だなと感じました。
「そうきたか」
#kansumiC3

チケット駆動でプロジェクトチームを加速せよ

WBS管理とチケット駆動を対比させてのお話。
チケット駆動のデメリットもちゃんと説明してました。
ガントチャートは完成された形。BTSのガントチャートはあくまでビューア。
#kansumiB4

JavaScript Security beyond HTML5

http://www.slideshare.net/hasegawayosuke/devsumi-kansaihasegawa
URLとして入力された文字列を信用しない、ってのが大事。
凝った対策(正規表現とか)は大変。エスケープさせるのが重要。
#kansumiB5

Employable Code~採用されないコードを書いていませんか?

CodeIQの中の人
#kansumiB6

JavaScript時代のJavaJava x Webを取り巻く熱いムード

クライアントサイドはpushStateかっこいい。
Javaださくない。
今はRDBがボトルネックだから動的言語のほうがオシャレになってるが、NoSQLや検索エンジンがもっと使われだすと動的言語がボトルネックになり静的言語がおしゃれになる。
#kansumiB7

JSFってどうやって使うのがいいんだろうか

最近、
http://hoshi.air-nifty.com/diary/2012/06/java-ee6java-ee.html
みたいな記事を読んだりしてJavaEEに興味が出てきて
Java EE 7 & GlassFish について語ろうにも参加したりしました。


で、JavaEEではフロントエンドの画面作成(MVC)はJSFのようです。
今まではこのブログでも取り上げているようなstruts2を使うことが多かったし、これからもしばらくは使うと思ってより詳しく勉強しようとしてましたが、ちょっとJSFも試してみました。


まず、struts2とJSFでは考え方が大きく違ってます。すごく簡単に言うとstruts2ではMVCでいうところのControllerがViewを呼び出しますが、JSFではViewがControllerを呼び出します。
JSFのような形態をコンポーネントベースというようです。(よく知らないのですが)VBの開発のイメージに近いようです。


JavaEE7とGlassFishの環境構築~JSFでの開発については
Java EE 7 入門 〜 NetBeansで始めるJavaEE7 First Tutorialあたりを参考にしてもらうとして、実際に簡単な画面遷移を実装しようとしたところなかなか大変でした。


どういうものを作ろうとしてたかというと、
「一覧画面」->(選択)->「詳細画面」->(POSTリクエスト)->「(リダイレクト後)実行結果」
のようなものです。
ただ作りながらモヤモヤ感が積もる一方でした。


http://dev.worksap.co.jp/Members/inoue_se/archives/55
この記事をそのモヤモヤ感の説明に引用させてもらいます。

JSFはリクエスト処理のデータバインディングを隠蔽します。データバインディングによりリクエストのパラメータ(一般にフォームのフィールド入力値)からBeanオブジェクト(JSFの用語を使うと、昔風にはBacking Bean、今風にはManaged Bean)を構築します。

ひとつ目の欠点は、JSFがURLを抽象化層の下に隠している点です。なぜならURLこそ、抽象化層が外に見せるAPIそのものであっていいからです。勘の良い人は気づくかと思いますが、この進化の系統樹の先のひとつがRESTful Webアプリ(サービス)です。宗教論争になるので、RESTfulだけが正義で、それ以外はダメだとまでは主張しません。個人的にもRESTがオンリーワンの選択肢とは思っていません。大事なのはURLをAPIとして意識することです。

もうひとつのJSFの欠点が、最近のJavaScriptのプログラミングの進化と衝突する点です。最近はクライアントサイドのJavaScriptMVCで(昔より)まっとうなGUIプログラミングモデルに進化しつつあります。JSFはこの動向と衝突します。なぜならJSFはクライアントサイドのJavaScriptのイベントを抽象化層の下に隠蔽する発想だからです。

とにかく何でもかんでもJSFの「抽象化層の下に隠蔽」されてしまって何がなんだか分からなくなります。
HTTPリクエスト/レスポンスヘッダを見ても何をやっているのか掴めないし、レスポンスボディを見てもform要素やjavascriptが何をどうするためのものなのかが掴めません。

発表でもうひとつ言わなかったのがJSFへのダメ出しです。別に政治的な意図があって言わなかったのではなく、論点をずらしたくなかったからです。ブログ記事なのではっきり書きます。JSFは嫌いです。JSF2でも同じです。JSF1に比べてJSF2は良くなっています。DIベースになって一見美しいです。デモやチュートリアルを見ていると一瞬騙されそうです。しかし、やはり嫌いです。しょせんJSFStrutsの進化の系統樹にある技術だからです。どんなに進化しても最初の出発点が違うので好きになれません。

私はここまで詳しくないし、全然調べ切れてないのでこう言うのも気が引けますが、現時点では嫌いと言わざるを得ないです。


Java EE 7 SDKの中のCode Sampleも見ましたがやっぱり良さが分からない。
これぞというサンプルプログラムがあれば教えてほしいものです。

jQueryでSVGを扱う

結論から言うと、セレクタを使ってHTML要素を取得するくらいしかjQueryは使えなさそう。
addClassとかつかってスタイルを変えれれば便利なんだけど、できなかった。

例えば次のような直線をひくとする。

<svg id="svg1">
  <line x1="100" y1="100" x2="200" y2="300" stroke="#000" class="cls1"/>
</svg>

SVG要素作成

jQueryを使って次のように書きたいけどこれだとダメっぽい。

  var $line = $('<line>');

どうすればいいかというと、

  var line = document.createElementNS("http://www.w3.org/2000/svg", "line");

SVG要素の属性設定

jQueryを使って次のように書きたい(ry

  $line.attr('x1', '100').attr('y1', '200');
  $line.addClass('cls1');

どうすればいいかというとsetAttributeメソッドを使う

  line.setAttribute('x1', '100');
  line.setAttribute('y1', '200');
  line.setAttribute('class', 'cls1);

getAttributeメソッドで属性を取得できます。
CSSクラスを追加、削除する場合は文字列操作等の処理が必要になります。

SVG要素をHTMLに追加

これはjQuery使ってもOKです

  $('#svg1').append($line);
  // もしくはjQueryオブジェクトじゃなくてもいい
  $('#svg1').append(line);

もちろんjavascriptのdomのappendChildメソッドを使ってもいいです。

  var svg = document.getElementById('svg1');
  svg.appendChild(line);

HTMLからSVG要素を取得

これはjQuery使ってもOKです

  var $line = $('line');

ただし、これらの要素を操作するためにはjQueryオブジェクトのままだとダメでDOMを操作する必要がある。

  // こんな感じにしたり
  $line[0].setAttribute('x1', '150');
  // こんな風にしたり
  $line.each(function(){
    var line = this;
    var cls = line.getAttribute('class');
    line.setAttribute('class', cls + " selected");
  });

jQuery UIのsortableでtable並び替え(helperオプションで表示の修正)

jQuery UIのsortableを使ってtable要素を並び替えるの続きです。


例えば上のリンクのデモ画面を見ると、並び替えをしようとして行をドラッグすると移動中の行の幅が小さくなってしまっています。
例えば、このデモ画面を見ると、ドラッグ中にdivとかliの場合は幅が保存されているのに対してtableの場合は幅が保存されていません。なので、ちょっと見にくいです。


sortableではドラッグ時に移動中の要素(helperと呼びます)の幅を設定してくれます。これは元の要素の幅を動的に取得してhelperにも同じ幅を指定することで実現しています。
table内を並び替えする場合もhelperとなるtrの幅を設定してくれているのですが、trにはwidthを設定しても無視されるようです。


この解決策として、helperオプションを指定します。
コードを記載します。

html
	<table id="sortable-table1">
		<thead>
			<tr>
				<th>番号</th>
				<th>項目</th>
				<th>備考</th>
			</tr>
		</thead>
		<tbody>
			<tr>
				<td>1</td><td>項目1</td><td></td>
			</tr>
			<!-- 省略 -->
		</tbody>
	</table>
css
#sortable-table1 {
	width: 50%;
	border-collapse: collapse;
}
#sortable-table1 td, #sortable-table1 th {
	border: solid 1px black;
}
javascript
$(function(){
	$('#sortable-table1 tbody').sortable({helper: helper1, cursor: "move", opacity: 0.5});
});
function helper1(e, tr) {
	var $originals = tr.children();
	var $helper = tr.clone();
	$helper.children().each(function(index) {
		$(this).width($originals.eq(index).width());
	});
	return $helper;
}

tr内のtdそれぞれに対してwidthを指定することでtableの行としての幅を保存できます。

デモ画面

このhelperというのはちょっと理解するのが難しいかもしれないです。
Sortable API(helper)