ウィリアムのいたずらの、まちあるき、たべあるき

ウィリアムのいたずらが、街歩き、食べ物、音楽等の個人的見解を主に書くブログです(たま~にコンピューター関係も)

Javascriptを使い、ブラウザでSQLを組み立てると、かなり柔軟に仕様変更できるが・・・

2009-09-16 12:12:59 | Weblog

 昨日の、Javascriptを利用してActionを実行する方法だけど、この応用で、Javascriptを使い、ブラウザでSQLを組み立てると、サーバー側のプログラムを変えずに、かなり柔軟に仕様変更できる。

 さらに、サーバー側のプログラムはStrutsでなくても、CGIでも、サーブレットでも、何でもよくなってくる。

 具体的に例をだそう。

 なお、これ以降の説明中に出てくるテーブルは、改訂新版 はじめてのSQLの36ページDEPT表を使っている。(が、実は、どんなテーブルでもOKなのお・・・ってことは、後でわかる)




■概要

 たとえば、Strutsを利用して検索の場合、

(1)検索画面では、strutsタグでは、hiddenで、paraという1項目だけを定義する(ここにSQLが入る)
(2)そして、他の入力項目はINPUTタグで定義し(そうすると、ActionFormで、セッターゲッターを定義しなくてよい)、ボタンを用意する。
(3)ボタンが押されたら、入力項目からデータを取ってきて、検索SQL文を作成し、paraにセットし、submit()して、サーバーへ
(4)サーバー側では、paraからSQL文を受け取り、それを実行、結果をArrayList(要素はHashMap)にいれる
   1レコード1ArrayListの要素(=1HashMap)で、
   1レコード分のHashMapの1つのキーは1項目分、値は、それに対する値
(5)そのArrayListをセッションにいれて、結果表示JSPは、そのセッションの値を
   元に表示する。

 Strutsでない場合、(3)をsubmitでなく、AJAX(REST)とかにして、(5)の返り値をXMLにするとかすれば、サーバーのフレームワーク、言語は問わないことになる。




■ソース

ってことで、まず、(1)~(3)を実行する、検索部分のJSPはこんなかんじ(index.jsp)
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
<%@ page contentType="text/html; charset=utf-8" %>
<html:html>
<HEAD>
<TITLE>検索</TITLE>
<script type="text/javascript" language="javascript">
<!--

function zikko()
{
	where = "";
	where = addwhere(where,selectForm.basho.value,"LOC","'");
	where = addwhere(where,selectForm.dname.value,"DNAME","'");
	selectForm.para.value = "SELECT * FROM DEPT";
	if ( where != "" )
	{
		selectForm.para.value += " WHERE " + where;
	}
	selectForm.submit();
}		

function addwhere(where,val,key,flg)
{
	if (val != "")
	{
		if ( where != "" )
		{
			where += " AND";
		}
		where += ( " "+ key+ "=" + flg + val + flg );
	}
	return where;
}
// -->
</script>
</HEAD>
<BODY>
<html:form action="/select">
<html:hidden property="para"/>
場所<INPUT TYPE=text NAME="basho" size="6"/><BR>
名前<INPUT TYPE=text NAME="dname" size="6"/><BR>
<html:button property="b1"  onclick="zikko()">検索実行</html:button>
</html:form>
</BODY>
</html:html>

(上記 < >は、本当は半角)

そして、(4)の部分だが、まず、ActionFormは、こんなかんじで、
package dbsample;
import org.apache.struts.action.*;
public class selectActionForm  extends ActionForm {
	private String para;
    public String getPara() {
        return para;
    }
    public void setPara(String para) {
        this.para = para;
    }
}

(4)を実行するActionは、こんなかんじ
package dbsample;
import java.sql.*;
import java.util.*;

import javax.servlet.http.*;
import org.apache.struts.action.*;
import java.io.*;

public class selectAction extends Action {
	 public ActionForward execute(
		     ActionMapping mapping,
		     ActionForm form,
		     HttpServletRequest request,
		     HttpServletResponse response) {

		//	画面パラメータの取得
	 	selectActionForm myForm = (selectActionForm)form;
		//	セッションの取得
        	HttpSession session = request.getSession();
	        
    		try
    		{
        		Connection con = null;
        		Statement stmt = null;
    			
        		// DB接続(testデータベースに、ログイン名root,パスワードpasswordの場合)
    			Class.forName("org.gjt.mm.mysql.Driver");
   			con = DriverManager.getConnection("jdbc:mysql:///test","root","password");
    			stmt = con.createStatement();

    			//	SQL(画面から引数para)実行
    			ResultSet rs = stmt.executeQuery(myForm.getPara());

    			//	結果をArrayListに
    			ArrayList<HashMap<String,String>> result 
    				= new ArrayList<HashMap<String,String>>();
    			while(rs.next())	//	行数分処理
    			{
    				HashMap<String,String> map = new HashMap<String,String>();
    				//	項目数分出力
    				for(int j = 1 ; j <= rs.getMetaData().getColumnCount() ; j ++)
    				{
    					map.put(rs.getMetaData().getColumnName(j),
    					 		rs.getString(j));
    				}
    				result.add(map);
    			}
    			//	あとしまつ
			stmt.close();
			con.close();
				
			//	結果をセッションにいれる
			session.setAttribute("result",result);
			session.setAttribute("kekka","");
    		}
    		catch(Exception e)
    		{
			session.removeAttribute("result");
			session.setAttribute("kekka",e.toString());
    			return mapping.findForward("error");
    		}
			return mapping.findForward("success");
	 }
}

(上記 < >は、本当は半角)
SQLを受け取るところ、myForm.getPara()になっている。
つまり、javascriptでSQLを作って、そのまま実行している。

そして、結果表示は、こんなかんじ(kekka.jsp)
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
<%@ page contentType="text/html; charset=utf-8" %>

<HTML>
  <HEAD>
    <TITLE>検索結果</TITLE>
  </HEAD>
  <BODY>
    結果<BR>

<table border=1>
<tbody>
<logic:notEmpty name="result" scope="session">
<logic:iterate id="me" name="result" scope="session" indexId="idx">
	<tr>
		<td><bean:write name="me" property="DEPTNO" /></td>
		<td><bean:write name="me" property="DNAME" /></td>
		<td><bean:write name="me" property="LOC" /></td>
	</tr>
</logic:iterate>
</logic:notEmpty>
</tbody>
</table><BR>
<A HREF="index.jsp">戻る</A>
</BODY>
</HTML>

(上記 < >は、本当は半角)

エラーの場合は、エラーメッセージを出している(error.jsp)
<%@ page contentType="text/html; charset=utf-8" %>
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
<HTML>
  <HEAD>
    <TITLE>エラー</TITLE>
  </HEAD>
  <BODY>
    <H2>
      <bean:write name="kekka" scope="session"/>
    </H2>
    <A HREF="index.jsp">戻る</A>
  </BODY>
</HTML>

(上記 < >は、本当は半角)

ついでに書くと、struts-config.xmlは、こんなかんじ
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN"
          "http://struts.apache.org/dtds/struts-config_1_3.dtd">

<struts-config>

    <form-beans>
        <form-bean name="selectForm" type="dbsample.selectActionForm"/>
    </form-beans>

    <action-mappings>
        <action
            path="/select"
            type="dbsample.selectAction"
	    name="selectForm" scope="request" validate="false">
	          <forward name="success" path="/kekka.jsp"/>
	          <forward name="error" path="/error.jsp"/>
	</action>
    </action-mappings>

</struts-config>

(上記 < >は、本当は半角)




■メリット

こりゃー、チョー便利だ。
テーブル項目を追加、変更したい場合,
たとえば、DEPTNOという項目を追加したい場合、
検索部分のJSP(index.jsp)を
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic" %>
<%@ page contentType="text/html; charset=utf-8" %>
<html:html>
<HEAD>
<TITLE>検索</TITLE>
<script type="text/javascript" language="javascript">
<!--

function zikko()
{
	where = "";
	where = addwhere(where,selectForm.basho.value,"LOC","'");
	where = addwhere(where,selectForm.dname.value,"DNAME","'");
	where = addwhere(where,selectForm.deptno.value,"DEPTNO","");
	selectForm.para.value = "SELECT * FROM DEPT";
	if ( where != "" )
	{
		selectForm.para.value += " WHERE " + where;
	}
	selectForm.submit();
}		

function addwhere(where,val,key,flg)
{
	if (val != "")
	{
		if ( where != "" )
		{
			where += " AND";
		}
		where += ( " "+ key+ "=" + flg + val + flg );
	}
	return where;
}
// -->
</script>
</HEAD>
<BODY>
<html:form action="/select">
<html:hidden property="para"/>
場所<INPUT TYPE=text NAME="basho" size="6"/><BR>
名前<INPUT TYPE=text NAME="dname" size="6"/><BR>
番号<INPUT TYPE=text NAME="deptno" size="6"/><BR>
<html:button property="b1"  onclick="zikko()">検索実行</html:button>
</html:form>
</BODY>
</html:html>

(上記 < >は、本当は半角。赤字は修正箇所)
ってなかんじで、値をいれるところと、あとSQL文をつくるところを変えるだけで、本体を何もいじらないでできる。

ってか、テーブル自体変えちゃう事だってできる。
もちろん、変更に伴い、結果表示画面を変えることはある。でも、結果表示もJSPだけなので、結局JSPを書き換えるだけで、Action,ActionFormは変えずに、Tomcatをとめずにできる。

 エラーチェックはJavascriptですればいいし。。。

 つーか、元となるプログラムは
  ・select用(execQuery実行)
  ・1行Update用(execUpdate実行)
  ・トランザクション複数行更新用
 の3つを用意すれば、あとは画面のJSPを作れば、どーにでもなりそうな気がする
(画面制御は、kekka.jspにlogic:forwardを書いて、ステータス(をセッションに入れておき)によって、表示したいJSPにフォワードするようにすれば、struts-configの修正もいらない。
ステータスは、成功時、次に遷移するステータスをjavascriptでSQLと一緒に送るようにすればいい。もちろん、条件とかもあり、それも送らないといけない場合もあるかもしれないけど、まあ、そのへんは適当に拡張してもらって・・・)




■問題点

 しかし、このやり方は致命的問題点がある。

 セキュリティがぼろぼろだ。
 SQLがパラメータなので、テキトーなSQLを送ると、それを実行してしまう。
 テーブル全削除とか・・(^^;)

 なので、このままでは、使えないだろう。

 ただ、テーブルごとに画面を用意し、"SELECT * FORM DEPT "ぐらいを書いておき、Where句のみをJavascriptで作らせ(そのとき、Javascriptで、エラーチェックする)、サーバー側でも、複数文かかれていないか、つまり、";"とかが含まれていないかをチェックし、実行するという形にすれば、かなり柔軟に画面やDBが変えられることになる。

 とはいえ、今のように画面項目を受け渡すようにしてしまうと、DBや画面の項目変更に伴い、プログラムが動く。これは、ちょっと作業工程的に、ためし作りがしにくくなってくる(理由は、UML等各種ダイアグラムのエラーチェック体系化のほうで出てくる)。

 ということで、どこまでjavascriptを使って、クライアント側に柔軟性を持たせるかということが、セキュリティと開発効率の間でトレードオフになってくるわけだが、クラウドとかマッシュアップの流れは、明らかに、クライアント側に柔軟性を持たせる方向に進んでいるといえる。
(かといって、SQL発行までやらせはしないけど。。。)

この記事についてブログを書く
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする
« Strutsの要素技術の基本的構... | トップ | EclipseでWML,XHTML,HTMLを... »
最新の画像もっと見る

Weblog」カテゴリの最新記事