FAQ

S2Dao

SMART deployで複数データソースに対応するには?

はい,可能です.
まず,データソースごとにS2Daoのインターセプターをdiconファイルに定義する.
そのインターセプターをAspectCustomizer#addClassPatternを使って,データソースに対応するDAOに割り当てる.

  • 前提条件
    • データソースとパッケージの対応構成
      データソース1のDAOは,ルートパッケージ名.dao.dao1
      データソース2のDAOは,ルートパッケージ名.dao.dao2
  • JDBCおよびDAO関連のdiconファイルの構成
    データソース1用として,jdbc1.dicon, dao1.dicon
    データソース2用として,jdbc2.dicon, dao2.dicon
---[jdbc.dicon]----------------------------------
<components>
	<include path="jdbc1.dicon"/>
	<include path="jdbc2.dicon"/>
</components>
---[jdbc1.dicon]----------------------------------
<components namespace="jdbc1">
	<include path="jta.dicon"/>
	<include path="jdbc-extension.dicon"/>
...省略...
	<component name="xaDataSource"
		class="org.seasar.extension.dbcp.impl.XADataSourceImpl">
		<property name="driverClassName">
			"org.h2.Driver"
		</property>
		<property name="URL">
			"jdbc:h2:tcp://localhost:9093/db1"
		</property>
		<property name="user">"sa"</property>
		<property name="password">""</property>
	</component>	
...省略...
</components>
---[jdbc2.dicon]----------------------------------
<components namespace="jdbc2">
	<include path="jta.dicon"/>
	<include path="jdbc-extension.dicon"/>
...省略...
	<component name="xaDataSource"
		class="org.seasar.extension.dbcp.impl.XADataSourceImpl">
		<property name="driverClassName">
			"org.h2.Driver"
		</property>
		<property name="URL">
			"jdbc:h2:tcp://localhost:9093/db2"
		</property>
		<property name="user">"sa"</property>
		<property name="password">""</property>
	</component>	
...省略...
</components>
---[dao.dicon]----------------------------------
<components>
	<include path="dao1.dicon"/>
	<include path="dao2.dicon"/>
	<include path="j2ee.dicon"/>
</components>
---[dao1.dicon]----------------------------------
<components namespace="dao1">
	<include path="jdbc1.dicon"/>
	<component name="annotationReaderFactory" class="org.seasar.dao.annotation.tiger.impl.AnnotationReaderFactoryImpl"/>
...省略...
	<component name="interceptor"
		class="org.seasar.dao.interceptors.S2DaoInterceptor"/>
...省略...		
</components>
---[dao2.dicon]----------------------------------
<components namespace="dao2">
	<include path="jdbc2.dicon"/>
	<component name="annotationReaderFactory" class="org.seasar.dao.annotation.tiger.impl.AnnotationReaderFactoryImpl"/>
...省略...
	<component name="interceptor"
		class="org.seasar.dao.interceptors.S2DaoInterceptor"/>
...省略...		
</components>
---[customizer.dicon]----------------------------------
<component name="dao1SupportAspectCustomizer" class="org.seasar.framework.container.customizer.AspectCustomizer">
	<property name="interceptorName">"dao1.interceptor"</property>
	<initMethod name="addClassPattern">
		<arg>"hogehoge.dao.dao1"</arg>
		<arg>".*Dao"</arg>
	</initMethod>
</component>
<component name="dao2SupportAspectCustomizer" class="org.seasar.framework.container.customizer.AspectCustomizer">
	<property name="interceptorName">"dao2.interceptor"</property>
	<initMethod name="addClassPattern">
		<arg>"hogehoge.dao.dao2"</arg>
		<arg>".*Dao"</arg>
	</initMethod>
</component>
<component name="daoCustomizer" class="org.seasar.framework.container.customizer.CustomizerChain">
	<initMethod name="addCustomizer">
		<arg>dao1SupportAspectCustomizer</arg>
	</initMethod>
	<initMethod name="addCustomizer">
		<arg>dao2SupportAspectCustomizer</arg>
	</initMethod>
</component>

参考投稿:

Bindingアノテーションを使用する方法もあります。

どのDBに対応していますか?

S2Daoは以下のDBに対応しています。

  • Oracle
  • PostgreSQL
  • HSQL Database Engine
  • Firebird
  • Microsoft SQL Server
  • MySQL
  • DB2
  • SAP DB (MaxDB)
  • Apache Derby
  • H2 Database Engine(S2Dao 1.0.38〜)

これら以外のDBでも、基本的なSELECT・INSERT・UPDATE・DELETEが使え、LEFT OUTER JOIN構文をサポートしているDBでしたら動作すると思います。

プロシージャ呼び出しについての対応状況はDBに異なるので、S2Daoのドキュメント http://s2dao.seasar.org/ja/ を確認してください。

なんか動かないのだけれど

  • DaoにはBEANアノテーションを、JavaBeans(Entity)にはTABLEアノテーションを書きましょう。
    • BEANアノテーションの例は、Class BEAN = Employee.class;
    • TABLEアノテーションの例は、public static final String TABLE = "EMP";
  • DaoにはS2DaoInterceptorを適用する必要があります。
  • AbstractなDaoを使うことも可能ですが、何らかのDaoインタフェースをimplementsしている必要があります。
  • バージョンアップしたら動かなくなったーー
    • dao.diconなど同梱のdiconがupdateされている可能性があります。リリースノートを確認しましょう。

トランザクション制御はどうなっていますか?

S2Daoとしてはトランザクション制御に関与しません。 S2が持つトランザクション制御機能を利用することを想定しています。

多いパターンは、Daoを呼び出す側のコンポーネントへj2ee.requiredTxアスペクトをかけることになるでしょう。

S2Daoを使った開発をサポートするツールはありますか?

次のものがあります。

updateの際に更新したくない列を指定したい

S2サポーター? (2006-04-29 (土) 19:48:55)

Question
S2Daoの開発の中で、updateBatchを投げる際、更新したくない列を指定
したいのですが、方法がわかりません。

Answer
NO_PERSISTENT_PROPSアノテーションを使用すると更新対象に含まれなくなります。

http://www.seasar.org/s2dao.html#NoPersistentPropsAnnotation
を参照されると良いと思います。

参考投稿
http://ml.seasar.org/archives/seasar-user/2005-November/000360.html
http://ml.seasar.org/archives/seasar-user/2005-November/000361.html

コネクションプーリングが行われていない?

S2サポーター? (2006-04-29 (土) 19:46:22)

Question
S2DAOでconnectionPoolを利用しているのですが、コネクションのプーリングが
行なわれていないような動作をしているので、設定に漏れがあるのではないかと
思い、何かアドバイスを頂ければとメールさせて頂きました。

実行ログを見ると、最初に1件のみ
[物理的なコネクションを取得しました]を表示して、以降はDBにアクセスする
と[論理的なコネクションを取得しました]と出力され、1つ物理コネクションを
使い回しているようなログが出力されています。

この時に、Oracleのv$sessionを見ると論理的なコネクションの数だけセッショ
ンが張られています。

しばらくDBへのアクセスを行なわずにいるとログには[論理的なコネクション
を取得しました]の表示がOracleに張られていたセッションの数だけ表示されま
す。(この時、Oracelのセッションも開放されています)

利用しているSeasarのバージョン
Seasar2.2.10
S2DAO1.0.28

以下のように設定を行なっているのですが、何か過不足がありますでしょうか?

---[j2ee.dicon]----------------------------------
<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN"
"http://www.seasar.org/dtd/components.dtd">
<components namespace="j2ee.dicon">
	<component name="transactionManager"
		class="org.seasar.extension.jta.TransactionManagerImpl"/>
	<component name="requiredTx"
		class="org.seasar.extension.tx.RequiredInterceptor"/>
	<component name="requiresNewTx"
		class="org.seasar.extension.tx.RequiresNewInterceptor"/>
	<component name="mandatoryTx"
		class="org.seasar.extension.tx.MandatoryInterceptor"/>
		
	<component class="org.seasar.extension.jdbc.impl.
BasicResultSetFactory"/>
	<component class="org.seasar.extension.jdbc.impl.
BasicStatementFactory"/>

	
	<component name="xaDataSource"
		class="org.seasar.extension.dbcp.impl.XADataSourceImpl">
		<property name="driverClassName">
			"oracle.jdbc.driver.OracleDriver"
		</property>
		<property name="URL">
			"jdbc:oracle:thin:@192.168.0.5:1521:white"
		</property>
		<property name="user">"serverwatcher"</property>
		<property name="password">"serverwatcher"</property>
	</component>
	
	<component name="connectionPool"
		class="org.seasar.extension.dbcp.impl.ConnectionPoolImpl
">
		<property name="timeout">600</property>
		<property name="maxPoolSize">10</property>
		<destroyMethod name="close"/>
	</component>
	<component name="dataSource"
		class="org.seasar.extension.dbcp.impl.DataSourceImpl"/>
	

</components>
----------------------------------------------------
---[dao.dicon]--------------------------------------
<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN"
"http://www.seasar.org/dtd/components.dtd">
<components namespace="dao">
	<include path="j2ee.dicon"/>
	<component
		class="org.seasar.dao.impl.DaoMetaDataFactoryImpl"/>
	<component name="interceptor"
		class="org.seasar.dao.interceptors.S2DaoInterceptor">
			<aspect>dao.requiredTx</aspect>
	</component>
</components>
----------------------------------------------------

Answer
原因は ServerControllerの constructorにありますね。
S2Containerを要求ごとに diconファイルから構築しているので、結局のとこ
ろVM空間上に複数のコンポーネントセットが存在することになります。

ということは、DataSourceもConnectionPoolも複数あるので、ログにあるよう
な動きになります。

S2の挙動というよりかは、DIの適用パターンにあわせて設計・動きを少し変更
されたほうがよろしいかと思います。

私なら以下の方法を使います

  1. EditLogic が xxxDao を使う(必須)のであれば EditLogicのコンストラ
     クタに引数を追加し、default constructorを削除する。
     これは
        public EditLogic(xxxDao dao) {
           this.dao = dao;
        }
     のようにする
  2. EditLogicは diconに定義する(か、自動登録されるような規則に従った
     上でAutoRegisterを使う)
     これでEditLogicのインスタンス管理はコンテナのもとにあります
  3. EditLogicが必要な部分は S2を経由してインスタンスを取得するように
     します。たとえば、EditLogicが StrutsのActionなのであれば S2Struts
     を用います.
     new EditLogic() している場所を getComponent(EditLogic.class) のよ
     うにすればいいかと思いますが、この方法を用いるのであればインタフェー
     スと実装クラスに分離したほうが良いです。
    (S2JSFの場合は特に意識する必要すらないと思います)



参考投稿
http://ml.seasar.org/archives/seasar-user/2005-November/000446.html
http://ml.seasar.org/archives/seasar-user/2005-November/000481.html

S2DAO、S2JDBC等のSQL発行方法について

S2サポーター? (2006-04-29 (土) 19:42:46)

Question

S2DAO、S2JDBC等でSQLを発行する際にコンパイル済みのSQLを発行するのか、
毎SQLを生成して発行するのか、どちらなのでしょう?サンプルのログをみてい
ると毎回生成しているように思えるのですが。。。

Answer

PreparedStatementに格納してあるプリコンパイルされたSQLを発行しています。

ログではき出されているSQLは、Statementに渡される(発行する)SQLを
そのまま表示している訳ではなく、ログにはき出すためにS2が、パラメータが
埋め込まれたSQLをせっせと作っています。

org.seasar.extension.jdbc.impl.BasicHandlerクラスの
getCompleteSqlメソッドが参考になると思います。
http://svn.seasar.org/browse/trunk/seasar2/s2-extension/src/main/java/org/seasar/extension/jdbc/impl/BasicHandler.java?root=s2container&view=markup


参考投稿
http://ml.seasar.org/archives/seasar-user/2005-November/000431.html
http://ml.seasar.org/archives/seasar-user/2005-November/000452.html

S2DaoでJDKを変えるとClassCastException

S2サポーター? (2006-04-29 (土) 19:40:26)

Question

S2DaoでJDK1.4.2_08だと動くコードがJDK1.4.2_10にして
Daoのメソッドを呼ぶとClassCastExceptionでこけてしまうコードがあります。

DaoのメソッドはBigDecimalを返すメソッドで引数4つを取ります。

public BigDecimal sumTest(String a, String b, String c, String d,
 String e);
public static final String sumTest_ARGS = "a,b,c,d";


SQLファイルは

select 
sum(qty)
from testdat s 
left outer join xx l on s.hoge = l.hoge and s.hoge2 = l.hoge2
where s.a = /*a*/'a'
and s.b = /*b*/'b'
and s.c = /*c*/'0'
and l.d = /*d*/'1'


となっています(フィールド名等はサンプルです)
スタックトレースは

java.lang.ClassCastException
	at sun.nio.cs.ext.JISAutoDetect$Decoder.decodeLoop(Unknown Source)
	at sun.nio.cs.ext.JISAutoDetect$Decoder.decodeLoop(Unknown Source)
	at java.nio.charset.CharsetDecoder.decode(Unknown Source)
	at sun.nio.cs.StreamDecoder$CharsetSD.implRead(Unknown Source)
	at sun.nio.cs.StreamDecoder.read(Unknown Source)
	at java.io.InputStreamReader.read(Unknown Source)
	at java.io.BufferedReader.read1(Unknown Source)
	at java.io.BufferedReader.read(Unknown Source)
	at java.io.Reader.read(Unknown Source)
	at org.seasar.framework.util.ReaderUtil.readText(ReaderUtil.java:27)
	at org.seasar.framework.util.TextUtil.readText(TextUtil.java:18)
	at org.seasar.dao.impl.DaoMetaDataImpl.setupMethod(DaoMetaDataImpl.java:132)
	at org.seasar.dao.impl.DaoMetaDataImpl.setupSqlCommand(DaoMetaDataImpl.java:112)
	at org.seasar.dao.impl.DaoMetaDataImpl.<init>(DaoMetaDataImpl.java:103)
	at org.seasar.dao.impl.DaoMetaDataFactoryImpl.getDaoMetaData(DaoMetaDataFactoryImpl.java:42)
	at org.seasar.dao.interceptors.S2DaoInterceptor.invoke(S2DaoInterceptor.java:34)
	at org.seasar.framework.aop.impl.NestedMethodInvocation.proceed(NestedMethodInvocation.java:26)
	at org.seasar.framework.aop.interceptors.TraceInterceptor.invoke(TraceInterceptor.java:33)
	at org.seasar.framework.aop.impl.NestedMethodInvocation.proceed(NestedMethodInvocation.java:26)
	at org.seasar.framework.aop.interceptors.InterceptorChain.invoke(InterceptorChain.java:24)
	at test.TestDatDao$$EnhancedByS2AOP$$18622f3$$MethodInvocation$$sumTest5.proceed(MethodInvocationClassGenerator.java)
	at test.TestDatDao$$EnhancedByS2AOP$$18622f3.sumTest(TestDatDao$$EnhancedByS2AOP$$18622f3.java)


となっております。
SQLを組み立てる時にこけている様な感じです。
DAOのバージョンはs2-dao-1.0.28.jarでPostgreSQL8.0.3を使っております。
JDKの違いやdaoのバージョンで何か注意する点等ありますでしょうか?

Answer

JDK 1.4.2_10にはJISAutoDetectのバグがあります。
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=25802&forum=12

FIXされるまで1.4.2_10より前のバージョンを使われることをオススメします。

参考投稿
http://ml.seasar.org/archives/seasar-user/2005-November/000429.html
http://ml.seasar.org/archives/seasar-user/2005-November/000430.html

プライマリキーが無いテーブルにS2DAOでアクセスしたい

S2サポーター? (2006-04-29 (土) 19:33:05)

Question

S2Daoのプライマリーキーの処理について伺います。 S2Daoを使ってDB2へのアクセスを勉強しております。

テーブルにプライマリーキーが設定していないと、S2Daoが「・・にプライマリーキーが見付りません」の例外を投げてきます。

当然テーブルに直接プライマリーキーが設定していれば問題がありませんが、既存のテーブルに明示的に主キーではなく、Index(索引)の形で主キーを指定されていることです。

この場合はS2Daoをどのようにすれば「・・にプライマリーキーが見付りません」の例外を回避できるのでしょうか。

Answer

UPDATEやDELETEなどの自動生成を行わなければ、プライマリキーがなくても問題ありません。これは、UPDATEやDELETE文のWHERE句にPKを含むよう自動生成するためです。 したがって、UPDATEやDELETEをsqlファイルやsqlコメントで行えば例外を回避できると思います。

DAOがUPDATEやDELETEメソッドを持たない場合は、何か対応する必要はありません。

テーブルにはPKを付けることを推奨します。

参考投稿

S2で複数DBとのコネクションを取得する方法は?

S2サポーター? (2006-04-29 (土) 19:30:14)

Question
j2ee.diconでの設定を利用すれば自動的に1個のコネクションを得られます。
複数DBとのコネクションを取得するにはどうすればよいのでしょうか。

Answer

まず,j2ee.dicon をコピーして DB 接続ごとの dicon ファイルを
作成してください.
例えば ds1.dicon

<components namespace="ds1">
    <include path="j2ee.dicon"/>

    <component name="dataSource" class="...">
        ・・・
    </component>
    <component name="xaDataSource" class="...">
        ・・・
    </component>
    <component name="connectionPool" class="...">
        ・・・
    </component>
</component>


必要なコンポーネントは 3 つだけです.
transactionManager 等のコンポーネントは削除してください.

同じように ds2.dicon も作成します.

そして j2ee.dicon から name 属性が以下の値になっている<component> 要素を削除します.

dataSource
xaDataSource
connectionPool

ds1.dicon で記述したコネクションを使用するコンポーネント用の
dicon は ds1.dicon をインクルードします.
例えば app1.dicon

<components namespace="app1">
    <include path="ds1.dicon"/>
    ・・・
<components>


ds2.dicon で記述したコネクションを使用するコンポーネント用の
dicon は ds2.dicon をインクルードします.
例えば app2.dicon

<components namespace="app2">
    <include path="ds2.dicon"/>
    ・・・
<components>


もし,ds1.dicon と ds2.dicon に記述したコネクションを同時に
使用するコンポーネントがある場合は,両方をインクルードして
コンポーネントのプロパティに明示的に DataSource を設定します.
例えば app3.dicon

<components namespace="app3">
    <include path="ds1.dicon"/>
    <include path="ds2.dicon"/>

    <component name="..." class="...">
        <property name="dataSource1">ds1.dataSource</property>
        <property name="dataSource2">ds2.dataSource</property>
        ・・・
    </component>
    ・・・
</components>



最後に,app1.dicon や app2.dicon,app3.dicon 等を app.dicon に
インクルードします.

<components>
    <include path="app1.dicon"/>
    <include path="app2.dicon"/>
    <include path="app3.dicon"/>
    ・・・
</components>



実際に確認していないので間違いがあるかもしれませんが,
だいたいこんな感じでできるかと思います.

参考投稿

追記: SelectableDataSourceProxyを使う方法もあります。 http://ml.seasar.org/archives/seasar-user/2006-May/005597.html はちょっと情報が古いですね。。。

複雑なSQLを処理する場合の実装方法

S2サポーター? (2006-04-29 (土) 19:26:23)

Question
複雑なSQLを処理する場合の実装方法について教え頂けませんでしょうか?

Answer
引数の値をSQL文に文字列として直接埋め込む。ドキュメント
「埋め込み変数コメント」を参照。

参考投稿
http://ml.seasar.org/archives/seasar-user/2005-December/000288.html
http://ml.seasar.org/archives/seasar-user/2005-December/000301.html

幾何データ演算子を使いたい

S2サポーター? (2006-04-29 (土) 18:45:30)

Question
s2daoを使用して、PostgresSQLの幾何データ型を扱っているのですが、
PostgresSQLがサポートしている幾何データ演算子の中には
?# のように「?」を含む演算子があります。

例:select A.name from road as A, road as B where A.path ?# B.path and
B.name = 'hoge';

この演算子を使用したSQLを発行しようとすると、「?」がバインド変数とみなされ
て、
java.sql.SQLExceptionが発生してしまいます。

ログで実行されたSQLをみてみると、
select A.name from road as A, road as B where A.path null# B.path and B.name
= 'hoge';
となっています。

org.seasar.framework.util.PreparedStatementUtilで
java.sql.PreparedStatementを使用している以上、
s2daoでPostgresSQLの幾何データ演算子を使用することはできないのでしょうか?

ちなみに、単独でjava.sql.Statementを使用して同じSQLを発行すると、
正常に問い合わせが行われました。

何かよい方法があれば、教えてください。
よろしくお願い致します。


Answer
CREATE OPERATOR文を使用して、新しく作成したOPERATORの中でpath_interを
呼び出すことで、無事解決することができました。

参考投稿
http://ml.seasar.org/archives/seasar-user/2006-January/000175.html
http://ml.seasar.org/archives/seasar-user/2006-January/000176.html
http://ml.seasar.org/archives/seasar-user/2006-Janu

配列からINへのバインドで例外が出る

ドキュメントに

IN /*引数名*/(...)

とあるように、

IN /*dto.aaa*/('aaa')

というふうにコメントより後ろの括弧が必要。

バッチ更新の途中でエラーになった場合に、何件目でエラーになったか知りたい

JDBC APIから知る方法が無いため、無理。

java.sql.Statement#executeBatch()を使用しているのであれば、戻り値のint[]に各コマンドの実行結果が反映されているはずですよね?現実的には、1つのコマンドでも失敗すれば、全てのコマンドが失敗したような状態になってしまった記憶があります。つまりJDBCドライバー次第ってことですね。

SQL文の変数部分にクラス名が含まれてしまう

Question

SQLのパラメータ?の部分に、以下のような文字列が入ってしまいます。

WHERE
  USER./*searchKey*/USER_ID LIKE /*searchValue*/'%t%'
   ↓↓↓↓↓↓↓
WHERE
  USER.'s2.dao.user.UserPagerCondition@1b35125' LIKE 's2.dao.user.UserPagerCondition@1b35125'

Answer

Dao側が

public List findUserByDto(UserPagerCondition dto);

である場合には、SQL側を

USER./*dto.searchKey*/USER_ID LIKE /*dto.searchValue*/'%t%'

のように"dto"を付けてみて頂けますか?

排他制御のTimestamp値にDB側の時間をセットしたい

排他制御には、TimestampよりもVersionNoの方が良い。 レコード更新時の時間を持ちたいのならば、トリガーを使うのが良いのでは。

BLOB型を扱えますか?

はい、扱えます。 JavaBeansのプロパティをbyte[]型にしておくと、S2DaoはPreparedStatementに対してsetBinaryStream、ResultSetに対してgetBytesを呼び出すようになります。

OracleでBLOB型を扱えないようだけれど?

OracleのThinドライバではPreparedStatement#setBinaryStream()がサポートされていないようで、

  • java.sql.Blobを使う
  • ThinドライバではなくOCIドライバを使う

と言った解決方法があるようです。

※バージョンにもよると思いますので、追加情報をお持ちの方にコメント頂きたいです。

3つ以上のテーブルをJOINしたい

Sybaseでストアドプロシージャ実行時にFetchSizeが指定されているとエラーになる

[Seasar-user:5457] のスレッドを参照。

MySQLでクエリキャッシュがヒットしない

[Seasar-user:5447] のスレッドを参照。

DB2でBLOB型のデータを取得時に例外が発生する

[Seasar-user:5458]のスレッドを参照。

更新結果が0件の時に例外を発生させたくない

SQLファイルないしSQLアノテーションでSQLを記述してください。

PosgreSQLとS2Pagerの組み合わせで、limitとoffsetを使用した高速取得使用時にSQLExceptionが発生する。

j2ee.dicon又はjdbc.diconの接続文字列にオプション「protocolVersion=2」を付いているか確認してください。

ヒット数が0件の場合はnullが返るの?

戻り値を配列かListで宣言しているSelect系メソッドの場合は、要素数0の配列かListがreturnされます。 nullではありません。

バッチ更新の場合、IDアノテーションが無視される

現行のS2Daoの仕様です。

これはバッチ更新の場合、複数レコードの更新・削除をまとめて行うためID値の取得を行うタイミングがないためです。(1件ずつINSERTする場合はINSERT文を発行する直前にSEQUENCEからnextvalを取得していますが、バッチ更新の場合はnextvalを取得するタイミングが無いため。)

OracleのNUBER型のカラムにBEAN側のInteger型プロパティを組み合わせると、Insert/Update時に値がとんでもなくなる(例8.3000000000000000000000000000000000E+83)場合がある。

OracleとJDBCドライバの組み合わせによって、この現象が起きることが報告されています。

参考:

http://ml.seasar.org/archives/seasar-user/2006-September/006567.html

http://ml.seasar.org/archives/seasar-user/2007-March/008602.html

以下のような例外が出力されます

org.seasar.dao.MethodSetupFailureRuntimeException: [EDAO0019]test.dao.TestDaoの

updateメソッドの初期化時に例外が発生しました。理由はorg.seasar.dao.PrimaryKeyNotFoundRuntimeException:

[EDAO0009]test.dto.TestDto$$EnhancedByS2AOP$$423a423aにプライマリーキーが見つかりません

原因は三つ考えられます。

  • TestDtoのTABLEアノテーションで指定したテーブルに、プライマリーキーがないか、ユニークキーのみ設定されている。
  • TABLEアノテーションで指定したテーブル名が間違っている
  • Beanのプロパティ名(メソッド名/変数名)が間違っている(カラム名に対応していない)

IDアノテーションのidentity/sequenceはどのDBが対応していますか?

リンク先の表をごらんください。 http://s2dao.seasar.org/ja/s2dao.html#IDAutoGenerate

IDアノテーションが機能しない

IDアノテーションを使用してIDの自動設定を行う場合、Daoが使うDataSourceにトランザクションがかかっている必要があります。

isolationレベルを変更したい

データソース毎にisolationレベルを変更可能です。

diconファイルにて、ConnectionPoolImplのtransactionIsolationLevelプロパティにConnection.TRANSACTION_SERIALIZABLEなどを指定してください。

アプリケーション内で複数のtransactionIsolationLevelを使い分けたい場合は、複数のConnectionPoolImplをdiconで設定してください。

Daoのメソッド呼び出し時にorg.seasar.dao.MethodSetupFailureRuntimeExceptionが発生しました。

この例外は、Daoのメソッドが最初に呼ばれたときに、呼び出したメソッドでなく、Daoの全メソッドの中のいずれかを初期化するときに例外になったことをさしています。

例外メッセージ中に、例外の原因となったメソッドの名前が表示されているので、そのメソッドの定義を見直してください。

プライマリーキーのみのテーブルで例外

プライマリーキーのみのテーブルに対するDaoを作成したところ、以下のような例外が出ました。

org.seasar.dao.MethodSetupFailureRuntimeException:
[EDAO0019]jp.roaso.dao.UsrgrpassignedDaoのupdateメソッドの初期化時に例外が発生しました。
理由はorg.seasar.framework.exception.SRuntimeException: 
[EDAO0020]Primary KeyのみのテーブルをSQL文の自動生成で更新することはできません

更新SQLを自動生成しているメソッドを削除してください。

例えば

public class Employee{
   private int empno;
   private int companyno;
(setter/getter略)
}

のようなEntityがあった場合、empnoがPKだとS2Daoは

update Employee set companyno = ? where empno = ?

のようなSQL文を生成するわけですが、 empnoとcompanynoが両方PKだとSQL文が

update Employee set where emono = ? and companyno = ?

となり、SQL文として意味を成しません。

このため、Daoの初期化時(Dao中のいずれかのメソッドが最初に呼ばれたとき)に、PKだけのテーブルに対してSQL文の自動生成を行うメソッドがあった場合には例外にするようになっています。

BooleanToIntStatementFactoryとPagerStatementFactoryを併用したい

j2ee.diconまたはjdbc.diconのPagerStaementFacotoryにbooleanToIntプロパティを追加してください。

<component class="org.seasar.dao.pager.PagerStatementFactory">
    <property name="booleanToInt">true</property>
</component>

SQLをツールで実行すると結果が取得できるのに、そのSQLをS2Dao経由で実行すると結果が取得できない。

WHERE句でCHAR型のカラムを利用し、そのカラム定義と異なる長さの値をバインドしているとこの現象が起こる場合があります。

CREATE/DROP/TRUNCATEなどのSQLを実行したい

exexuteQuery/executeUpdateで実行できるSQLなら、実行することができます。

その場合、メソッドの名前をexexuteQueryなら検索、executeUpdateなら挿入/更新/削除の命名規則にあわせてください。

ただし、オラクルは、DML以外をPreparedStatementで実行すると不安定になるので、S2Daoから実行するのは、やめたほうが良いと思います。

truncateなどは、Statementで実行してください。

#DataSourceから自前で処理するという意味です。

IFコメントを使用した場合にQueryアノテーションで指定したクエリ全体が無視されてしまう

すべてのIFコメントがfalseと評価され、かつELSEコメントがない場合に起こります。 対策としては次のような方法があります。

S2Pagerのオフセットのはじまりは「1」「0」どちらですか?

S2Pagerのオフセットは「0」からはじまります。OracleRownumPagingSqlRewriterを使用する場合、SQLではオフセットでなく開始位置を指定するので、

SELECT
       *
   FROM
       (
           SELECT
                   ROWNUM AS S2DAO_ROWNUMBER
                   ,S2DAO_ORIGINAL_DATA.*
               FROM
                   (
                       SELECT
                               EMPNO,
                               ENAME,
                               SAL,
                               COMM,
                               DEPTNO
                           FROM
                               EMP
                   ) S2DAO_ORIGINAL_DATA
       )
   WHERE
       S2DAO_ROWNUMBER BETWEEN 1 AND 50
       AND ROWNUM <= 50
   ORDER BY
       S2DAO_ROWNUMBER

のように、オフセットで「0」を指定すると「1」ではじまるSQLが実行されますが、これは仕様となります。

ツールで実行した場合は問題ないSQLがS2Pager(OracleRownumPagingSqlRewriter)使用時にSQLException(ORA-00918: column ambiguously defined )になる

S2PagerのOracleRownumPagingSqlRewriterはオリジナルのSQLをサブクエリとして実行します。このため、例えば

 SELECT
         *
     FROM
         DUAL A
         ,DUAL B
     WHERE
         A.DUMMY = B.DUMMY

のように、同じカラム(エイリアス)をSELECTするSQLをOracleRownumPagingSqlRewriterを使用して実行すると、外側のクエリで列を特定できないためSQLExceptionになる場合があります。

 SELECT
         *
     FROM
         DUAL A join DUAL B
     ON
         A.DUMMY = B.DUMMY

のように標準SQLの結合構文の場合はエラーが出ませんが、今度はメタデータからカラム名を取得できないのでS2Dao側で値をセットできなくなりますので、クエリ内ではカラム名を必ず指定することをおすすめします。

SQLインジェクションへ気をつける必要がありますか?

自動生成SQLや、*.sqlファイルへのバインドを使用する場合は、インジェクション対策は不要です。

インジェクション対策が必要なのは、

  • 埋め込み変数コメントを使う場合
  • EntityManagerでSQL文を文字列結合して作成する場合

です。"'" + 入力値 + "'"のようにSQLを組み立てる際に入力値をエスケープしてください。


トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2012-01-19 (木) 09:51:20 (2014d)