[seasar-dotnet:1006] Re: 動的に複数DBファイルを作成して、複数同時にアクセス

koyak [E-MAIL ADDRESS DELETED]
2008年 10月 18日 (土) 22:39:25 JST


大下さん

小谷です。
返信が遅くなってしまい、申し訳ありません。

>#すみませんが、Quillの方は、まだドキュメントを深く読んでおらず、検証コードもまだ書いておりません。
ごめんなさい。
ドキュメントは今リアルタイムで書いている途中なので、
データソースまわりの細かいことに関してのドキュメントはまだありません。。。

S2DaoInterceptorの適用についての話題も出てきてはいますが
まずはデータソースについて考えさせて下さい。

とりあえず"動的な接続"の要件は基本的にこの二つ、ですよね?
1.テーブル構造は固定
2.接続先は最初は存在しない(動的に追加してやる必要がある)

1.の要件からDBFlute(S2Dao)を用いることは可能、で
2.をどうするかなのですが

(以下、少々長いです)
Quillではデータソースの設定元がQuillContainerかS2Containerかといった
設定元を意識するのは初期化での設定読込時だけで、それ以降は
SelectableDataSourceProxyWithDictionary
を共通データソースとして使っています。
そのため、データソースの追加、切替はこちらの方を使っていただきたく思います。
(これまたドキュメントにもコメントにも書いていないので恐縮ですが
SelectableDataSourceProxyWithContainerは
Quillを一切使わない場合のデータソース切替を想定して作っています)

データソースをアプリ実行中に追加したい場合は大下さんが仰る通り、
SelectableDataSourceProxyWithDictionary#RegistDataSourceメソッドを呼び
「アプリケーション中で一意なデータソース名」と
「IDataSource実装クラスのインスタンス(データソース)」を
渡してください。
SelectableDataSourceProxyWithDictionary#DataSourceCollection
(型はIDictionary<string, IDataSource>)
というプロパティにデータソースが追加されます。

この「IDataSource実装クラスのインスタンス(データソース)」が
大下さん側で作っていただくことになる部分になります。
(TxDataSourceもIDataSourceを実装しているクラスです)

追加したデータソースを使いたい場合は
SelectableDataSourceProxyWithDictionary#SetDataSourceNameメソッドを呼び、
引数にRegisterDataSourceで設定したデータソース名を指定して下さい。

データソースが既に設定されているかどうかは
DataSourceCollection.ContainsKeyにデータソース名を渡せば
確認できるはずです。

データソース自体を動的に追加する用途は想定していなかったため、
データソースを破棄するメソッドはありません。
もし不要になった分を破棄する必要がある場合は
破棄するデータソースの後始末(Closeを呼ぶ)をした後に
DataSourceCollection.Removeを呼んでやる必要があります。

データソースについては上記の処理を書いていただくことで
対応できるのではないかという気がします。

次にS2DaoInterceptorなどのAspectに関しては
極力コンポーネントはS2Container+diconで管理する、というのでなければ
Quill側の設定(S2Dao属性)をお使いいただいた方が無難かと思います。
(DBFluteを使う場合は自動生成したコードにこの属性が既に付与されています)

初めの方に書きました通りデータソースは動的でも静的でも、
一つでも複数でも必ずSelectableDataSourceProxyWithContainerを間にはさむように
なっているのでS2DaoInterceptorの適用やトランザクションの設定で
データソースの設定を特別に意識する(※)必要はない(はず)です。

※1トランザクションで一度に使われるデータソースが一つの場合


2008/10/18 14:36 kubo <[E-MAIL ADDRESS DELETED]>:
> 久保(jflute)です。
>
>> ・SelectableDataSourceProxyWithContainer#SetDataSourceName
>> というメソッドがありますが、DataSourceの名前の文字列を設定するもののようです。
>
> なるほど、ちょっと微妙に記憶違いでした。
> 失礼しました。
>
>> ・Seasar.Quill.Database.DataSource.Impl.SelectableDataSourceProxyWithDictionary
>> というクラスには、
>> ・SelectableDataSourceProxyWithDictionary#RegistDataSource
>> というメソッドがあるので、これでできるのかなぁと想像しています。
>
> 自分がイメージしてたメソッドはこっちですね。
> 試してみる価値はあるかと思います。
>
>
> # すいません、自分最近すっかりQuillばかりで
> # S2Container.NETは1年以上触ってないので
> # 記憶が全部上書きされてる....
>
> 2008/10/18 Yoshihiro OSHITA <[E-MAIL ADDRESS DELETED]>:
>> 久保様、
>> 大下です、お世話になっています。
>>> DataSourceをつくったものの、
>>>
>>> それはどこに登録しておけばいいのかよくわからないのと
>>> あれっ、SelectableDataSourceProxyWithContainerにSetするクチは
>>> ないでしょうか?(今ソース見れなくて確認ができてないですが)
>>
>> ・Seasar.Extension.ADO.Impl.SelectableDataSourceProxyWithContainerクラス
>> の方には、ないようにお見受けします。
>> ・SelectableDataSourceProxyWithContainer#SetDataSourceName
>> というメソッドがありますが、DataSourceの名前の文字列を設定するもののようです。
>>
>> ただ、
>> ・Seasar.Quill.Database.DataSource.Impl.SelectableDataSourceProxyWithDictionary
>> というクラスには、
>> ・SelectableDataSourceProxyWithDictionary#RegistDataSource
>> というメソッドがあるので、これでできるのかなぁと想像しています。
>> #すみませんが、Quillの方は、まだドキュメントを深く読んでおらず、検証コードもまだ書いておりません。
>>
>>>色々やってうまく行かない場合は、ADO.NET直接利用の方が良いかもです。
>>
>> そうですね…。
>> S2Dao.NETの恩恵にあずかってデータアクセス層のモジュール開発効率をあげるつもりだったのですが、検証の方に時間を掛けすぎてしまいました。
>> 小谷さんがおっしゃるようなQuillを使う方法で、うまくいかないようであれば、S2Dao.NETの使用は諦めようかと思います。
>> ひとまず、SelectableDataSourceProxyWithDictionaryクラスを使う方法で検証コードを書いてみようと思います。
>> ただ、DataSourceインスタンスを動的に作成して(必要に応じて増やす)ということが可能なのかが一番ひっかかるのですが、小谷さんが何か返信してくださることをお待ちしております。
>>
>> 2008/10/18 12:19 kubo <[E-MAIL ADDRESS DELETED]>:
>>>
>>> 久保です。
>>>
>>> > DataSourceをつくったものの、それはどこに登録しておけばいいのかよくわからないのと
>>> あれっ、SelectableDataSourceProxyWithContainerにSetするクチは
>>> ないでしょうか?(今ソース見れなくて確認ができてないですが)
>>> こいつに独自に作ったものをぶち込むイメージでした。
>>> すると、(うまくいけば)あらかじめ作っておいた動的に作ったDB用の
>>> DaoインターフェースでそのDataSourceが利用できるかなと。
>>> (ここは小谷くんの方が詳しい。。。Help!)
>>>
>>> > S2DaoIntercepterをDiconファイルに書かずにAcpectできそうにない
>>> Enhance(Java用語かな!?)してあげないといけません。
>>> そこまで自分でやるよりは、S2DaoIntercepterでやっている中の処理を
>>> まんま普通に実行するクラスを作成した方が良さそうです。
>>>
>>> > #なんだか、すごく変なことやってますねぇ、すみません。
>>> 上記いずれにせよ、S2Daoの仕組みを、想定していない
>>> 別の目的に当てはめようとしているため、結構無理が生じるかと思います。
>>> 色々やってうまく行かない場合は、ADO.NET直接利用の方が良いかもです。
>>> (検証時間に余裕があるならよいのですが)
>>>
>>> 2008/10/18 Yoshihiro OSHITA <[E-MAIL ADDRESS DELETED]>:
>>> > 小谷様、
>>> > お世話になっています、大下です。
>>> >
>>> >>「いくつかある接続先の中から何らかのルールで選択する」
>>> >>というものでしたら
>>> >
>>> > 接続先が最初からあるわけでなく、必要に応じて作成し増やしていく必要があります。
>>> > たとえば、
>>> > ---------------
>>> > // 接続文字列を作成する
>>> > SQLiteConnectionStringBuilder conn = new
>>> > SQLiteConnectionStringBuilder();
>>> > conn.DataSource = Guid.NewGuid().ToString + ".SQ3";// 一意なDBファイル名をここで作成
>>> > (略)
>>> > ---------------
>>> > などのようにして、動的に作成します。
>>> > (コードは、さきほど、久保さんにお送りしたメールから抜粋しています)
>>> > このような場合でも適用可能でしょうか?
>>> >
>>> > 2008/10/17 13:56 koyak <[E-MAIL ADDRESS DELETED]>:
>>> >>
>>> >> 大下さん
>>> >>
>>> >> 小谷です。
>>> >>
>>> >> >>固定構造のテーブル
>>> >>
>>> >> >> トランザクションの扱いがうまく行かないかもですが、
>>> >> >> QuillのSelectableDataSourceProxyWithDictionaryを使って、
>>> >> >> DataSourceを切り替えることが可能かもしれません。
>>> >>
>>> >> 接続文字列の"動的な設定"が
>>> >> 「いくつかある接続先の中から何らかのルールで選択する」
>>> >> というものでしたら
>>> >>
>>> >>
>>> >> Seasar.Quill.Database.DataSource.Impl.SelectableDataSourceProxyWithDictionary
>>> >> というクラスを使用する選択肢も使えるかと思います。
>>> >>
>>> >> 使い方はドキュメントの方には未だに書けていないのですが、
>>> >> 1.接続先の候補の数だけ設定ファイルのquillセクションdataSources内に書く
>>> >> (各データソースの設定は以下のページをご参照下さい
>>> >> http://s2container.net.seasar.org/ja/quill.html#nodicon_config)
>>> >> 2.接続先の切り替えロジックをもつクラスにSelectableDataSourceProxyWithDictionary型の
>>> >> 変数を用意する(インジェクションされるようにする)。
>>> >> 3.2で用意したインスタンスのSetDataSourceNameメソッドを呼び、
>>> >> 必要に応じて次に使いたいデータソース名を渡す
>>> >>
>>> >> という形で接続先を切り替えることができます。
>>> >>
>>> >> ただ、二相コミットのような機能は実装していないので
>>> >> 1トランザクションで一度に使うデータソースは一つまで、
>>> >> である必要があります。
>>> >>
>>> >> 2008/10/17 10:41 kubo <[E-MAIL ADDRESS DELETED]>:
>>> >> > 久保(jflute)です。
>>> >> >
>>> >> > すいません、QuillじゃなくてS2Container.NETにも
>>> >> > SelectableDataSourceProxyWithDictionaryはあるようです。
>>> >> >
>>> >> >
>>> >> > # 既にS2Container.NETの知識が忘却の彼方・・・
>>> >> >
>>> >> > 2008/10/17 kubo <[E-MAIL ADDRESS DELETED]>:
>>> >> >> 久保(jflute)です。
>>> >> >>
>>> >> >> 了解です。
>>> >> >> それならば、事前にEntityもSQLも作成しておくことは
>>> >> >> 可能ということですね。
>>> >> >>
>>> >> >> トランザクションの扱いがうまく行かないかもですが、
>>> >> >> QuillのSelectableDataSourceProxyWithDictionaryを使って、
>>> >> >> DataSourceを切り替えることが可能かもしれません。
>>> >> >> これは、冗長化したDBのDataSourceを状況によって
>>> >> >> 切り替えるもので、ちょっと今回と用途は違いますが、
>>> >> >> 「このEntityを使う時はこっちのDataSourceを利用」
>>> >> >> という形で切り替えられるかもしれません。
>>> >> >>
>>> >> >> それでダメなら、ADO.NETを直利用にならざるを
>>> >> >> 得ないかと思います。
>>> >> >>
>>> >> >>
>>> >> >> 2008/10/16 Yoshihiro OSHITA <[E-MAIL ADDRESS DELETED]>:
>>> >> >>> 久保様、
>>> >> >>> お世話になっています、大下です。
>>> >> >>>
>>> >> >>> テーブル構造自体は決まっており、動的に変更することはありません。
>>> >> >>> よって、前者の
>>> >> >>>
>>> >> >>>>固定構造のテーブル
>>> >> >>>
>>> >> >>> をもったDBファイルを(複数)作成することになります。
>>> >> >>>
>>> >> >>> 2008/10/16 21:13 kubo <[E-MAIL ADDRESS DELETED]>:
>>> >> >>>>
>>> >> >>>> 久保(jflute)です。
>>> >> >>>>
>>> >> >>>> SQLiteのDBファイルを動的に作成した後、
>>> >> >>>> その作成したDBには固定構造のテーブルが作成されるのでしょうか?
>>> >> >>>> それともそのテーブル構造も動的で開発時には決定しないものでしょうか?
>>> >> >>>> 少なくとも後者であれば、固定のDaoとEntityが準備できないため、
>>> >> >>>> S2Daoの利用は難しいかと思います。
>>> >> >>>>
>>> >> >>>> 2008/10/16 Yoshihiro OSHITA <[E-MAIL ADDRESS DELETED]>:
>>> >> >>>> > 小谷様、
>>> >> >>>> > お世話になっています、大下です。
>>> >> >>>> > 回答ありがとうございます。
>>> >> >>>> > TxDataSourceのソース、確認してみます。
>>> >> >>>> > ただ、私の扱っているケースだとSQLiteのDBファイルを動的に作成してやる必要があるのですが、
>>> >> >>>> > これをSeasarでやるには、動的にTxDataSourceを作成して、
>>> >> >>>> > コンテナにデプロイしてやる必要があるのでしょうか?
>>> >> >>>> >
>>> >> >>>> > 2008/10/16 10:52 koyak <[E-MAIL ADDRESS DELETED]>:
>>> >> >>>> >>
>>> >> >>>> >> 小谷です。
>>> >> >>>> >>
>>> >> >>>> >> データソース+トランザクションで関連するソースは
>>> >> >>>> >> 色々ありますが
>>> >> >>>> >> データソースの中身を追うのであれば
>>> >> >>>> >> Seasar.Extension.Tx.Impl.TxDataSource
>>> >> >>>> >>
>>> >> >>>> >>
>>> >> >>>> >> Seasar.Extension.ADO.Impl.DataSourceImpl (TxDataSourceがこれを継承しているので)
>>> >> >>>> >>
>>> >> >>>> >> トランザクション制御の中身を追うのであれば
>>> >> >>>> >> Seasar.Extension.Tx.Impl.XxxTxHandlerのHandleTransactionメソッド
>>> >> >>>> >> (一番読みやすいのは多分LocalRequiredTxHandler)
>>> >> >>>> >>
>>> >> >>>> >> あたりから読んでいくのが個人的にはオススメです。
>>> >> >>>> >>
>>> >> >>>> >> また、(何の保証もできないのですが(^^;)
>>> >> >>>> >> 一からデータソース相当のものを書かなくても
>>> >> >>>> >> TxDataSourceを継承して一部を必要な分だけ書き換える、
>>> >> >>>> >> (コンストラクタやGetConnectionなど)
>>> >> >>>> >> という手もあるかもしれません。
>>> >> >>>> >>
>>> >> >>>> >> 2008/10/16 0:59 Y. Oshita <[E-MAIL ADDRESS DELETED]>:
>>> >> >>>> >> > お世話になっています、大下です。
>>> >> >>>> >> > 何度も恐縮です。
>>> >> >>>> >> > ソースを読むといったものの、どのあたりを見ればいいか悩んでおります。
>>> >> >>>> >> > トランザクションを使うので、Seasar.Extension.Tx.Impl.TxDataSourceあたりでしょうか?
>>> >> >>>> >> >
>>> >> >>>> >> > 2008/10/16 0:06 Yoshihiro OSHITA
>>> >> >>>> >> > <[E-MAIL ADDRESS DELETED]>:
>>> >> >>>> >> >>
>>> >> >>>> >> >> 久保様、杉本様、
>>> >> >>>> >> >> お世話になっています、大下です。
>>> >> >>>> >> >> 回答ありがとうございました。
>>> >> >>>> >> >> ソースを読んでみることにします。
>>> >> >>>> >> >>
>>> >> >>>> >> >> 2008/10/15 22:41 Kazuya Sugimoto <[E-MAIL ADDRESS DELETED]>:
>>> >> >>>> >> >>>
>>> >> >>>> >> >>> 杉本です。
>>> >> >>>> >> >>>
>>> >> >>>> >> >>> > 基本的にプロセス起動時に読んでそれ以降は見ないのでは。。。
>>> >> >>>> >> >>> > (他のコミッタの方、どうです!?)
>>> >> >>>> >> >>> アプリケーション構成ファイルを書き換えても、ConfigurationManagerで取得する
>>> >> >>>> >> >>> 値は起動時から変わらないと思います。
>>> >> >>>> >> >>>
>>> >> >>>> >> >>> >>> Seasar.NET自体が内部でやっている「DataSourceの生成」と同じことを
>>> >> >>>> >> >>> >>> アプリ上で自分自身で行う必要があるかと思います。
>>> >> >>>> >> >>> > が、制御しやすく一番わかりやすいかと思います。
>>> >> >>>> >> >>> 私もこういう方法になるんじゃないかと思います。
>>> >> >>>> >> >>>
>>> >> >>>> >> >>>
>>> >> >>>> >> >>> 2008/10/15 22:21 kubo <[E-MAIL ADDRESS DELETED]>:
>>> >> >>>> >> >>> > 久保です。
>>> >> >>>> >> >>> >
>>> >> >>>> >> >>> >> If false, the database will be created automatically.
>>> >> >>>> >> >>> >> とあるように、自動的に「Library.SQ3」というDBファイルを作成してくれます。
>>> >> >>>> >> >>> >
>>> >> >>>> >> >>> > なるほど、この辺は組み込み系DB特有のものですね。
>>> >> >>>> >> >>> > JavaでもH2やApacheDerbyが同じような機能もってます。
>>> >> >>>> >> >>> >
>>> >> >>>> >> >>> >> 直接App.configにxmlをがりがり書き込めばいいのかもしれませんが、まだ試してい
>>> >> >>>> >> >>> >> ません。
>>> >> >>>> >> >>> >> それに、このようにして書いた接続文字列をコンテナが読み取ってくれるのだろうか
>>> >> >>>> >> >>> >> という疑問もあります。
>>> >> >>>> >> >>> >
>>> >> >>>> >> >>> > 個人的な見解では、ダメなような気がします。
>>> >> >>>> >> >>> > .NETがどのようにApp.configを扱っているか次第ですが、
>>> >> >>>> >> >>> > 基本的にプロセス起動時に読んでそれ以降は見ないのでは。。。
>>> >> >>>> >> >>> > (他のコミッタの方、どうです!?)
>>> >> >>>> >> >>> >
>>> >> >>>> >> >>> >>> Seasar.NET自体が内部でやっている「DataSourceの生成」と同じことを
>>> >> >>>> >> >>> >>> アプリ上で自分自身で行う必要があるかと思います。
>>> >> >>>> >> >>> > が、制御しやすく一番わかりやすいかと思います。
>>> >> >>>> >> >>> >
>>> >> >>>> >> >>> > 2008/10/15 Yoshihiro OSHITA
>>> >> >>>> >> >>> > <[E-MAIL ADDRESS DELETED]>:
>>> >> >>>> >> >>> >> 久保様、
>>> >> >>>> >> >>> >> お世話になります、大下です。
>>> >> >>>> >> >>> >>
>>> >> >>>> >> >>> >>> アプリケーションのプログラムから「新しい接続先」をCREATEして、
>>> >> >>>> >> >>> >>> その作ったばかりの接続先にDBアクセスするということでしょうか?
>>> >> >>>> >> >>> >>
>>> >> >>>> >> >>> >> そうですね。接続文字列は、
>>> >> >>>> >> >>> >>
>>> >> >>>> >> >>> >> "Data Source=Library.SQ3;FailIfMissing=False;…;Default
>>> >> >>>> >> >>> >> Timeout=30"
>>> >> >>>> >> >>> >>
>>> >> >>>> >> >>> >> のようにしています。「FailIfMissing=False」 としておくと、
>>> >> >>>> >> >>> >> (SQLite.NETのヘルプから抜粋しますが、)
>>> >> >>>> >> >>> >> If set to true, will throw an exception if the database
>>> >> >>>> >> >>> >> specified
>>> >> >>>> >> >>> >> in
>>> >> >>>> >> >>> >> the
>>> >> >>>> >> >>> >> connection string does not exist.
>>> >> >>>> >> >>> >> If false, the database will be created automatically.
>>> >> >>>> >> >>> >> とあるように、自動的に「Library.SQ3」というDBファイルを作成してくれます。
>>> >> >>>> >> >>> >>
>>> >> >>>> >> >>> >> ただ、実際のプログラムではDBファイル名を動的に作成するので、
>>> >> >>>> >> >>> >> あらかじめ設定ファイルに記述しておくことはできません。
>>> >> >>>> >> >>> >>
>>> >> >>>> >> >>> >> ならば、動的につくったDBファイル名を含む接続文字列をApp.configに書き込んでや
>>> >> >>>> >> >>> >> ればどうだろう
>>> >> >>>> >> >>> >> と思ったのですが、接続文字列はアプリケーション設定に属するらしく、プログラム
>>> >> >>>> >> >>> >> 上から書き込めませんでした。
>>> >> >>>> >> >>> >> 直接App.configにxmlをがりがり書き込めばいいのかもしれませんが、まだ試してい
>>> >> >>>> >> >>> >> ません。
>>> >> >>>> >> >>> >> それに、このようにして書いた接続文字列をコンテナが読み取ってくれるのだろうか
>>> >> >>>> >> >>> >> という疑問もあります。
>>> >> >>>> >> >>> >>
>>> >> >>>> >> >>> >> でも、久保さんのお言葉から察すると、
>>> >> >>>> >> >>> >>
>>> >> >>>> >> >>> >>> Seasar.NET自体が内部でやっている「DataSourceの生成」と同じことを
>>> >> >>>> >> >>> >>> アプリ上で自分自身で行う必要があるかと思います。
>>> >> >>>> >> >>> >>
>>> >> >>>> >> >>> >> をやる必要がありそうですね?
>>> >> >>>> >> >>> >>
>>> >> >>>> >> >>> >>> 久保(jflute)です。
>>> >> >>>> >> >>> >>>
>>> >> >>>> >> >>> >>>> SQLiteのDBアクセスにS2Container.NET,S2Dao.NETを利用しているのですが、
>>> >> >>>> >> >>> >>>> 動的に作成した複数のDBファイルへの同時アクセスはどのようにすればよいで
>>> >> >>>> >> >>> >> しょうか?
>>> >> >>>> >> >>> >>>
>>> >> >>>> >> >>> >>> SQLiteは使ったことないのですが、確認させて下さい。
>>> >> >>>> >> >>> >>> アプリケーションのプログラムから「新しい接続先」をCREATEして、
>>> >> >>>> >> >>> >>> その作ったばかりの接続先にDBアクセスするということでしょうか?
>>> >> >>>> >> >>> >>>
>>> >> >>>> >> >>> >>>> にあるように、きめ打ちで「Library.SQ3」を指定してやる分には、うまく動いて
>>> >> >>>> >> >>> >> いるのですが、
>>> >> >>>> >> >>> >>>> このDBファイルを動的に複数作成して、複数同時にデータを書き込んだりする必
>>> >> >>>> >> >>> >> 要があります。
>>> >> >>>> >> >>> >>>> この場合は、どのように設定すればよいでしょうか?
>>> >> >>>> >> >>> >>>
>>> >> >>>> >> >>> >>> 少なくとも設定ファイルに書くやり方では無理だと思います(静的なので)。
>>> >> >>>> >> >>> >>> Seasar.NET自体が内部でやっている「DataSourceの生成」と同じことを
>>> >> >>>> >> >>> >>> アプリ上で自分自身で行う必要があるかと思います。
>>> >> >>>> >> >>> >>> また、DataSourceを動的に作れたとしても静的なDaoインターフェースから
>>> >> >>>> >> >>> >>> その作ったばかりの接続先にアクセスするのは難しいかと思いますので、
>>> >> >>>> >> >>> >>> その場合ADO.NETを直接利用する形になるかと思います。
>>> >> >>>> >> >>> >>>
>>> >> >>>> >> >>> >>> #
>>> >> >>>> >> >>> >>> # コミッタの方で同じようなことやったことある人いらっしゃいます?
>>> >> >>>> >> >>> >>> #
>>
>>
>> _______________________________________________
>> seasar-dotnet mailing list
>> [E-MAIL ADDRESS DELETED]
>> https://ml.seasar.org/mailman/listinfo/seasar-dotnet
>>
>>
> _______________________________________________
> seasar-dotnet mailing list
> [E-MAIL ADDRESS DELETED]
> https://ml.seasar.org/mailman/listinfo/seasar-dotnet
>


seasar-dotnet メーリングリストの案内