tag:crieit.net,2005:https://crieit.net/tags/hive/feed 「hive」の記事 - Crieit Crieitでタグ「hive」に投稿された最近の記事 2022-05-24T09:12:51+09:00 https://crieit.net/tags/hive/feed tag:crieit.net,2005:PublicArticle/18197 2022-05-24T09:09:10+09:00 2022-05-24T09:12:51+09:00 https://crieit.net/posts/Flutter-Hive-type-UnspecifiedInvalidResult-is-not-a-subtype-of-type-LibraryElementResult-in-type-cast FlutterのHiveで type 'UnspecifiedInvalidResult' is not a subtype of type 'LibraryElementResult' in type cast エラー <p>FlutterのHiveでbuild_runnerしたところ下記のようなエラーが出た。</p> <pre><code class="plain">type 'UnspecifiedInvalidResult' is not a subtype of type 'LibraryElementResult' in type cast [SEVERE] hive_generator:hive_generator on lib/models/message.dart: </code></pre> <p>調べてみたところいくつかの箇所で一旦pubspec.lockを削除したらいける、とのことで試してみたらなおった。依存関係が更新されてしまうのでちょっと怖いが。</p> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/google/json_serializable.dart/issues/924">https://github.com/google/json_serializable.dart/issues/924</a></p> <p>追記)やっぱ仕様変更したライブラリがあって困ったがそれはまた別の話…</p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/17358 2021-06-04T05:01:46+09:00 2021-06-04T05:02:28+09:00 https://crieit.net/posts/apache-bigtop-hive-server2-enable-doas hive.server2.enable.doAs がよく分からなかったので Apache Bigtop で調べてみた <p><code>hive.server2.enable.doAs</code> の設定によって何がどう変わるかよく分からなかったので <a target="_blank" rel="nofollow noopener" href="https://bigtop.apache.org/">Apache Bigtop</a> を使って調べてみました。</p> <p><code>hive.server2.enable.doAs</code> だと長くて煩雑なので以下では適宜 doAs と略しています。</p> <h1 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h1> <p>先にまとめ。調べた結果を基に書いていますが、厳密な裏付けはありません(ソースを読んで調べたりはしていません)。</p> <ul> <li>proxy user というしくみがある <ul> <li><a target="_blank" rel="nofollow noopener" href="https://hadoop.apache.org/docs/r2.8.5/hadoop-project-dist/hadoop-common/Superusers.html">Apache Hadoop 2.8.5 – Proxy user - Superusers Acting On Behalf Of Other Users</a></li> <li>hiveserver2 のプロセスを実行しているユーザではなく、<br /> 他のユーザになりすましてジョブの submit や HDFS へのアクセスを行うしくみ</li> <li>(Hive ではなく)Hadoop の機能</li> <li>core-site.xml の <code>hadoop.proxyuser.〜</code> で設定する</li> <li><code>hive.server2.enable.doAs</code> が false の場合、なりすましを行わず、<br /> hiveserver2 のプロセスを実行しているユーザで操作が実行される</li> </ul></li> <li>なりすましを行うには、下記の両方が必要 <ul> <li>OSのユーザとして存在している</li> <li>core-site.xml の proxyuser の設定に対象ユーザが含まれている(※1)</li> </ul></li> <li>beeline の場合は、なりすましたいユーザ名を <code>-n</code> オプションで指定する</li> <li>なりすましを行うと、HDFS上で作られるデータベースやテーブルのディレクトリ、データファイルの所有者がそのユーザになる</li> </ul> <p><code>hive.server2.enable.doAs</code> で設定しているのは要するに何なのかということで言えば、「Hadoop の proxy user 機能を利用するかどうか」と思ってよさそうな挙動でした。なので、利用する場合は proxy user についても知る必要があります。</p> <p>※1 <a target="_blank" rel="nofollow noopener" href="https://hadoop.apache.org/docs/r2.8.5/hadoop-project-dist/hadoop-common/Superusers.html">Apache Hadoop 2.8.5 – Proxy user - Superusers Acting On Behalf Of Other Users</a> によれば、ホストによる指定、グループによる指定、ユーザによる指定を組み合わせて指定できるようです。</p> <h1 id="バージョンなど"><a href="#%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E3%81%AA%E3%81%A9">バージョンなど</a></h1> <p>Bigtop は下記の時点の master を使っています。</p> <pre><code>34e0bd7182c713b16dce9a4bdc803c8ed7fb9eb3 Thu Jun 11 09:01:26 2020 +0000 </code></pre> <ul> <li>Hadoop 2.8.5</li> <li>Hive 2.3.3</li> </ul> <h1 id="一応公式の説明"><a href="#%E4%B8%80%E5%BF%9C%E5%85%AC%E5%BC%8F%E3%81%AE%E8%AA%AC%E6%98%8E">一応公式の説明</a></h1> <p><a target="_blank" rel="nofollow noopener" href="https://cwiki.apache.org/confluence/display/Hive/Setting+Up+HiveServer2">Setting Up HiveServer2 - Apache Hive - Apache Software Foundation</a></p> <blockquote> Impersonation By default HiveServer2 performs the query processing as the user who submitted the query. But if the following parameter is set to false, the query will run as the user that the hiveserver2 process runs as. hive.server2.enable.doAs – Impersonate the connected user, default true. </blockquote> <p>impersonate は「なりすます」という意味。エラーメッセージでも出てきます。</p> <h1 id="調査1: doAsの設定による違い"><a href="#%E8%AA%BF%E6%9F%BB1%3A+doAs%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%81%AB%E3%82%88%E3%82%8B%E9%81%95%E3%81%84">調査1: doAsの設定による違い</a></h1> <h2 id="準備"><a href="#%E6%BA%96%E5%82%99">準備</a></h2> <p>設定を変えてプロビジョニングしなおすのを繰り返すやり方だと時間がかかってしまうので、Bigtop のリポジトリをクローンしたディレクトリを2つ用意して true/false それぞれの設定にします。</p> <p>デフォルトでは true なので、false の方のディレクトリのみ <code>hive-site.xml</code> を修正。</p> <pre><code class="diff">--- a/bigtop-deploy/puppet/modules/hadoop_hive/templates/hive-site.xml +++ b/bigtop-deploy/puppet/modules/hadoop_hive/templates/hive-site.xml @@ -81,7 +81,7 @@ <property> <name>hive.server2.enable.doAs</name> - <value>true</value> + <value>false</value> </property> <property> </code></pre> <p><code>config_centos-7.yaml</code> を修正して Hive コンポーネントを追加。</p> <pre><code class="diff">--- a/provisioner/docker/config_centos-7.yaml +++ b/provisioner/docker/config_centos-7.yaml @@ -19,6 +19,6 @@ docker: repo: "http://repos.bigtop.apache.org/releases/1.4.0/centos/7/$basearch" distro: centos -components: [hdfs, yarn, mapreduce] +components: [hdfs, yarn, mapreduce, hive] enable_local_repo: false smoke_test_components: [hdfs, yarn, mapreduce] </code></pre> <p>設定の変更はこれだけ。</p> <hr /> <p>単一ノードで create します。</p> <pre><code>time ./docker-hadoop.sh -C config_centos-7.yaml --create 1 </code></pre> <p>これで Hadoop と Hive が使えるようになります。Bigtop すばらしい……ありがたや……。</p> <hr /> <p>コンテナに入る。</p> <pre><code>./docker-hadoop.sh --exec 1 bash </code></pre> <p>以下、コンテナ内の作業。</p> <hr /> <p>testuser というユーザがすでに存在しているので、そっちにスイッチします。<br /> (こういう用途で使うために用意されているものなのか分かっていませんが、とりあえず一般ユーザのつもりで使います)</p> <pre><code>su - testuser </code></pre> <h2 id="接続"><a href="#%E6%8E%A5%E7%B6%9A">接続</a></h2> <p>beeline で hiverserver2 に接続</p> <pre><code>beeline -u "jdbc:hive2://localhost:10000" </code></pre> <ul> <li>doAs=false の場合: 接続に成功する。</li> <li>doAs=true の場合: 接続に失敗して次のようなメッセージが出ます(適宜改行を加えています)。</li> </ul> <pre><code>Error: Could not open client transport with JDBC Uri: jdbc:hive2://localhost:10000: Failed to open new session: java.lang.RuntimeException: org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.security.authorize.AuthorizationException): User: hive is not allowed to impersonate anonymous (state=08S01,code=0) </code></pre> <p>この場合は <code>-n</code> オプションでユーザ名を指定すると接続できるようになります。</p> <pre><code>beeline -u "jdbc:hive2://localhost:10000" -n testuser </code></pre> <hr /> <p>ちなみに、Bigtop の既定の設定では hiveserver2 は hive ユーザで実行されますが、hiveserver2 を root ユーザで実行すると次のようなメッセージになります。</p> <pre><code>User: root is not allowed to impersonate anonymous (state=08S01,code=0) ^^^^ ここが変わる </code></pre> <p>「hiveserver2 の実行ユーザが他のユーザになりすます」ということが試みられているようです。</p> <hr /> <p>OSに存在しないユーザ名を指定した場合</p> <pre><code>beeline -u "jdbc:hive2://localhost:10000" -n nobody </code></pre> <ul> <li>doAs=false の場合: 接続に成功する。</li> <li>doAs=true の場合: 接続に失敗する。</li> </ul> <p>doAs=false の場合、 <code>-n</code> オプションによる指定はいずれにせよ無視されるということでしょうか。</p> <h2 id="create databse"><a href="#create+databse">create databse</a></h2> <h3 id="doAs=true の場合"><a href="#doAs%3Dtrue+%E3%81%AE%E5%A0%B4%E5%90%88">doAs=true の場合</a></h3> <pre><code>$ beeline -u "jdbc:hive2://localhost:10000" -n testuser create database test_db1; => 成功する $ hdfs dfs -ls /user/hive/warehouse Found 1 items drwxrwxrwx - testuser hadoop 0 2020-06-27 05:38 /user/hive/warehouse/test_db1.db </code></pre> <p>所有者=testuser でデータベースのディレクトリが作られました。</p> <h3 id="doAs=false の場合"><a href="#doAs%3Dfalse+%E3%81%AE%E5%A0%B4%E5%90%88">doAs=false の場合</a></h3> <p>一応 <code>-n testuser</code> を付けてみます。</p> <pre><code>$ beeline -u "jdbc:hive2://localhost:10000" -n testuser create database test_db1; => 成功する $ hdfs dfs -ls /user/hive/warehouse Found 1 items drwxrwxrwx - hive hadoop 0 2020-06-27 05:41 /user/hive/warehouse/test_db1.db </code></pre> <p>やはり <code>-n</code> の指定は無視され、所有者=hive でディレクトリが作られました。</p> <h2 id="create table + insert"><a href="#create+table+%2B+insert">create table + insert</a></h2> <h3 id="doAs=true の場合"><a href="#doAs%3Dtrue+%E3%81%AE%E5%A0%B4%E5%90%88">doAs=true の場合</a></h3> <pre><code>$ beeline -u "jdbc:hive2://localhost:10000" -n testuser use test_db1; create table test1 (name string); insert into test1 values ('foo'), ('bar'); Error: org.apache.hive.service.cli.HiveSQLException: Error while processing statement: FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.mr.MapRedTask. Permission denied: user=testuser, access=WRITE, inode="/user":hdfs:hadoop:drwxr-xr-x </code></pre> <p>insert 時に <code>/user</code> のパーミッションで怒られました。<br /> ちなみに <code>test1</code> テーブルは 所有者=testuser で作られています。</p> <pre><code>$ hdfs dfs -ls /user/hive/warehouse/test_db1.db Found 1 items drwxrwxrwx - testuser hadoop 0 2020-06-27 09:48 /user/hive/warehouse/test_db1.db/test1 </code></pre> <p><code>hdfs:///user</code> に実行権限を付けて再度 insert。</p> <pre><code># sudo -u hdfs hdfs dfs -chmod 777 /user $ beeline -u "jdbc:hive2://localhost:10000" -n testuser use test_db1; insert into test1 values ('foo'), ('bar'); </code></pre> <p>今度は成功しました。</p> <pre><code>$ hdfs dfs -ls /user/hive/warehouse/test_db1.db/test1 Found 1 items -rwxrwxrwx 3 testuser hadoop 8 2020-06-27 09:55 /user/hive/warehouse/test_db1.db/test1/000000_0 $ hdfs dfs -text /user/hive/warehouse/test_db1.db/test1/000000_0 foo bar </code></pre> <p>データは所有者=testuser で作られています。</p> <h3 id="doAs=false の場合"><a href="#doAs%3Dfalse+%E3%81%AE%E5%A0%B4%E5%90%88">doAs=false の場合</a></h3> <pre><code>$ beeline -u "jdbc:hive2://localhost:10000" -n testuser use test_db1; create table test1 (name string); insert into test1 values ('foo'), ('bar'); </code></pre> <p>こちらはエラーになりませんでした。</p> <pre><code>$ hdfs dfs -ls /user/hive/warehouse/test_db1.db Found 1 items drwxrwxrwx - hive hadoop 0 2020-06-27 09:58 /user/hive/warehouse/test_db1.db/test1 $ hdfs dfs -ls /user/hive/warehouse/test_db1.db/test1 Found 1 items -rwxrwxrwx 3 hive hadoop 8 2020-06-27 09:58 /user/hive/warehouse/test_db1.db/test1/000000_0 </code></pre> <p>テーブルのディレクトリとデータは所有者=hive で作られています。</p> <h2 id="create external table + insert"><a href="#create+external+table+%2B+insert">create external table + insert</a></h2> <h3 id="doAs=true の場合"><a href="#doAs%3Dtrue+%E3%81%AE%E5%A0%B4%E5%90%88">doAs=true の場合</a></h3> <p><code>table_ext</code> というテーブルを作り、<code>/user/testuser</code> の下にデータを置くことにします。</p> <pre><code>$ hdfs dfs -ls /user ... drwx------ - testuser hadoop 0 2020-06-27 09:54 /user/testuser ... </code></pre> <pre><code>beeline -u "jdbc:hive2://localhost:10000" -n testuser use test_db1; create external table table_ext (name string) location '/user/testuser/table_ext/'; insert into table table_ext values ('foo'), ('bar'); </code></pre> <pre><code>$ hdfs dfs -ls /user/testuser/ Found 2 items drwx------ - testuser hadoop 0 2020-06-27 10:22 /user/testuser/.staging drwx------ - testuser hadoop 0 2020-06-27 10:22 /user/testuser/table_ext $ hdfs dfs -ls /user/testuser/table_ext Found 1 items -rwx------ 3 testuser hadoop 8 2020-06-27 10:22 /user/testuser/table_ext/000000_0 $ hdfs dfs -text /user/testuser/table_ext/000000_0 foo bar </code></pre> <h3 id="doAs=false の場合"><a href="#doAs%3Dfalse+%E3%81%AE%E5%A0%B4%E5%90%88">doAs=false の場合</a></h3> <p>こちらは <code>hdfs:///user/testuser/</code> ディレクトリが存在しなかったので、<code>hdfs:///tmp/</code> の下にデータを置くことにします。</p> <pre><code>beeline -u "jdbc:hive2://localhost:10000" -n testuser use test_db1; create external table table_ext (name string) location '/tmp/table_ext/'; insert into table table_ext values ('foo'), ('bar'); </code></pre> <pre><code>$ hdfs dfs -ls /tmp/ Found 3 items drwxrwxrwx - mapred mapred 0 2020-06-27 04:55 /tmp/hadoop-yarn drwx-wx-wx - hive hadoop 0 2020-06-27 04:56 /tmp/hive drwxrwxrwt - hive hadoop 0 2020-06-27 10:29 /tmp/table_ext $ hdfs dfs -ls /tmp/table_ext Found 1 items -rwxrwxrwt 3 hive hadoop 8 2020-06-27 10:29 /tmp/table_ext/000000_0 $ hdfs dfs -text /tmp/table_ext/000000_0 foo bar </code></pre> <h1 id="調査2: OSのユーザとproxy userの関係"><a href="#%E8%AA%BF%E6%9F%BB2%3A+OS%E3%81%AE%E3%83%A6%E3%83%BC%E3%82%B6%E3%81%A8proxy+user%E3%81%AE%E9%96%A2%E4%BF%82">調査2: OSのユーザとproxy userの関係</a></h1> <p>doAs=false の場合の挙動はなんとなく分かってきましたが、doAs=true の場合のOSのユーザとの関係がよく分からないので、こんどはそこを調べてみます。</p> <p>Hadoop の proxy user というしくみが関わっているようだったので、次の3パターンでどうなるか試します。</p> <ul> <li><code>user_os</code>: OS のユーザのみ存在</li> <li><code>user_proxyuser</code>: proxy user の設定のみ</li> <li><code>user_both</code>: OS のユーザが存在し、かつ proxy user の設定もあり <ul> <li>おそらく testuser と同等</li> </ul></li> </ul> <p>データの配置場所が変わるだけだと思われたので外部テーブルについては省略。</p> <hr /> <p>設定ファイルを修正。下記は master からの差分です。Puppet に詳しくないので、 testuser を grep したりして当たりを付けて適当に修正しました。</p> <pre><code class="diff">--- a/bigtop-deploy/puppet/manifests/cluster.pp +++ b/bigtop-deploy/puppet/manifests/cluster.pp @@ -159,7 +159,7 @@ $roles_map = { class hadoop_cluster_node ( $hadoop_security_authentication = hiera("hadoop::hadoop_security_authentication", "simple"), - $bigtop_real_users = [ 'jenkins', 'testuser', 'hudson' ], + $bigtop_real_users = [ 'jenkins', 'testuser', 'hudson', 'user_os', 'user_both' ], $cluster_components = ["all"] ) { --- a/bigtop-deploy/puppet/modules/hadoop/manifests/init.pp +++ b/bigtop-deploy/puppet/modules/hadoop/manifests/init.pp @@ -20,7 +20,7 @@ class hadoop ($hadoop_security_authentication = "simple", $hadoop_storage_dirs = split($::hadoop_storage_dirs, ";"), $proxyusers = { oozie => { groups => 'hudson,testuser,root,hadoop,jenkins,oozie,hive,httpfs,users', hosts => "*" }, - hive => { groups => 'hudson,testuser,root,hadoop,jenkins,oozie,hive,httpfs,users', hosts => "*" }, + hive => { groups => 'hudson,testuser,user_both,user_proxyuser,root,hadoop,jenkins,oozie,hive,httpfs,users', hosts => "*" }, httpfs => { groups => 'hudson,testuser,root,hadoop,jenkins,oozie,hive,httpfs,users', hosts => "*" } }, $generate_secrets = false, $kms_host = undef, --- a/provisioner/docker/config_centos-7.yaml +++ b/provisioner/docker/config_centos-7.yaml @@ -19,6 +19,6 @@ docker: repo: "http://repos.bigtop.apache.org/releases/1.4.0/centos/7/$basearch" distro: centos -components: [hdfs, yarn, mapreduce] +components: [hdfs, yarn, mapreduce, hive] enable_local_repo: false smoke_test_components: [hdfs, yarn, mapreduce] </code></pre> <p>あと、調査1のときと同様に <code>hdfs:///user</code> のパーミッションを変更しておきます。</p> <pre><code># hdfs dfs -ls / | grep /user drwxr-xr-x - hdfs hadoop 0 2020-06-28 03:13 /user # sudo -u hdfs hdfs dfs -chmod 777 /user </code></pre> <h2 id="OSユーザあり、proxy user 設定なし"><a href="#OS%E3%83%A6%E3%83%BC%E3%82%B6%E3%81%82%E3%82%8A%E3%80%81proxy+user+%E8%A8%AD%E5%AE%9A%E3%81%AA%E3%81%97">OSユーザあり、proxy user 設定なし</a></h2> <pre><code># su - user_os $ beeline -u "jdbc:hive2://localhost:10000" -n user_os Error: Could not open client transport with JDBC Uri: jdbc:hive2://localhost:10000: Failed to open new session: java.lang.RuntimeException: org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.security.authorize.AuthorizationException): User: hive is not allowed to impersonate user_os (state=08S01,code=0) </code></pre> <p>接続できない。</p> <h2 id="OSユーザなし、proxy user 設定あり"><a href="#OS%E3%83%A6%E3%83%BC%E3%82%B6%E3%81%AA%E3%81%97%E3%80%81proxy+user+%E8%A8%AD%E5%AE%9A%E3%81%82%E3%82%8A">OSユーザなし、proxy user 設定あり</a></h2> <p>testuser を使います。</p> <pre><code># su - testuser $ beeline -u "jdbc:hive2://localhost:10000" -n user_proxyuser Error: Could not open client transport with JDBC Uri: jdbc:hive2://localhost:10000: Failed to open new session: java.lang.RuntimeException: org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.security.authorize.AuthorizationException): User: hive is not allowed to impersonate user_proxyuser (state=08S01,code=0) </code></pre> <p>接続できない。</p> <h2 id="OSユーザあり、proxy user 設定あり"><a href="#OS%E3%83%A6%E3%83%BC%E3%82%B6%E3%81%82%E3%82%8A%E3%80%81proxy+user+%E8%A8%AD%E5%AE%9A%E3%81%82%E3%82%8A">OSユーザあり、proxy user 設定あり</a></h2> <pre><code># su - user_both $ beeline -u "jdbc:hive2://localhost:10000" -n user_both create database user_both_db; use user_both_db; create table table1 (name string); insert into table1 values ('foo'), ('bar'); </code></pre> <p>insert まで成功しました。</p> <pre><code>$ hdfs dfs -ls /user/hive/warehouse/ Found 1 items drwxrwxrwx - user_both hadoop 0 2020-06-28 03:19 /user/hive/warehouse/user_both_db.db $ hdfs dfs -ls /user/hive/warehouse/user_both_db.db/ Found 1 items drwxrwxrwx - user_both hadoop 0 2020-06-28 03:22 /user/hive/warehouse/user_both_db.db/table1 $ hdfs dfs -ls /user/hive/warehouse/user_both_db.db/table1 Found 1 items -rwxrwxrwx 3 user_both hadoop 8 2020-06-28 03:22 /user/hive/warehouse/user_both_db.db/table1/000000_0 </code></pre> <p>それぞれ <code>user_both</code> で作られています。testuser と同等の操作ができるようです。</p> <h2 id="OSユーザあり、proxy user 設定なし / グループのみ変更"><a href="#OS%E3%83%A6%E3%83%BC%E3%82%B6%E3%81%82%E3%82%8A%E3%80%81proxy+user+%E8%A8%AD%E5%AE%9A%E3%81%AA%E3%81%97+%2F+%E3%82%B0%E3%83%AB%E3%83%BC%E3%83%97%E3%81%AE%E3%81%BF%E5%A4%89%E6%9B%B4">OSユーザあり、proxy user 設定なし / グループのみ変更</a></h2> <p>user_os の場合接続の時点で失敗しましたが、user_os を proxy user で設定されているグループに所属させるとどうでしょうか。ためしに <code>users</code> というグループでやってみます。</p> <pre><code># id user_os uid=1000(user_os) gid=1000(user_os) groups=1000(user_os) # gpasswd -a user_os users Adding user user_os to group users # id user_os uid=1000(user_os) gid=1000(user_os) groups=1000(user_os),100(users) # su - user_os $ beeline -u "jdbc:hive2://localhost:10000" -n user_os => OK </code></pre> <p>接続できました。insert までやってみます。</p> <pre><code class="sql">create database user_os_db; use user_os_db; create table table1 (name string); insert into table1 values ('foo'), ('bar'); </code></pre> <pre><code>$ hdfs dfs -ls /user/hive/warehouse/ Found 2 items drwxrwxrwx - user_both hadoop 0 2020-06-28 03:19 /user/hive/warehouse/user_both_db.db drwxrwxrwx - user_os hadoop 0 2020-06-28 03:37 /user/hive/warehouse/user_os_db.db $ hdfs dfs -ls /user/hive/warehouse/user_os_db.db/ Found 1 items drwxrwxrwx - user_os hadoop 0 2020-06-28 03:37 /user/hive/warehouse/user_os_db.db/table1 $ hdfs dfs -ls /user/hive/warehouse/user_os_db.db/table1 Found 1 items -rwxrwxrwx 3 user_os hadoop 8 2020-06-28 03:37 /user/hive/warehouse/user_os_db.db/table1/000000_0 </code></pre> <p>所有者=user_os で作られました。なるほど。</p> sonota486 tag:crieit.net,2005:PublicArticle/16174 2020-10-26T08:16:35+09:00 2021-01-30T00:00:54+09:00 https://crieit.net/posts/Flutter-Hive FlutterでHiveというデータベースを使ってみての要所まとめ <p>FlutterでHiveというデータベースを使用してみたが、色々ハマったのでちゃんと動くところまでのメモ。SQLiteのようにテーブル的に保存していくような形。</p> <p><a target="_blank" rel="nofollow noopener" href="https://docs.hivedb.dev/#/">https://docs.hivedb.dev/#/</a></p> <h2 id="Hiveはどうか"><a href="#Hive%E3%81%AF%E3%81%A9%E3%81%86%E3%81%8B">Hiveはどうか</a></h2> <p>元々sqfliteというSQLiteのパッケージを使っていたが、やはりマイグレーションを書くのもモデルをMapに変換したりするのもひたすら面倒くさすぎるのでもっと簡単なものを探していた。下記でたくさんまとめられている。</p> <p><a target="_blank" rel="nofollow noopener" href="https://kabochapo.hateblo.jp/entry/2020/02/01/144411">Dart/FlutterのローカルDBの比較 - のんびり精進</a></p> <p>その中でHiveというパッケージがスター数も多かったため(という理由だけで)使ってみることにした。</p> <p>変換はなくなったし、リレーションもできたりするので今のところ満足。しかしハマりどころも多かったので使い方を含めメモしておく。</p> <h2 id="インストール"><a href="#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">インストール</a></h2> <p>まずFlutterで使うためには下記のパッケージが必要となる。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://pub.dev/packages/hive">hive</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://pub.dev/packages/hive_flutter">hive_flutter</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://pub.dev/packages/hive_generator">hive_generator</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://pub.dev/packages/build_runner">build_runner</a></li> </ul> <p>上の2つは必須。hive_generatorはどちらでも良いが、クラスで作ったデータをテーブルやコレクション的に保存して使いたい場合、TypeAdapterというのを作る必要がある。それを生成してくれるために使う。ただし、自分で書くこともできるため絶対に必須というわけではない。build_runnerはhive_generatorでTypeAdapterを生成するコマンドを実行するために使う。hive_generatorだけインストールして生成コマンドを実行すると下記のようなメッセージが出る。</p> <pre><code class="text">Could not find package "build_runner". Did you forget to add a dependency? </code></pre> <h3 id="インストールできない場合"><a href="#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%84%E5%A0%B4%E5%90%88">インストールできない場合</a></h3> <p>僕の場合、hiveとhive_flutterはインストールできたが、hive_generatorとbuild_runnerがダメだった。既存の色んなパッケージとバージョンが合わなかったため。(追記:2021/1 もしかしたらintlかも。build_runnerを下げたらいけたかも?)</p> <p>他のパッケージも古くなっていそうだったため最新のものにバージョンアップしていけばよかったのかもしれないが、色々いじりすぎて動かなくなったりエラーがでるようになったりすると怖かったため、hive_generatorとbuild_runnerはインストールをしないことにした。TypeAdapterはさほど量も多くないし自分で書こうと思っていたため。</p> <p>ちなみに後述もするが、同様の問題が出ている方は別のまっさらなプロジェクトを作ってそちらにhive関連をインストールして作ったモデルファイルをコピーすれば生成できる。</p> <h2 id="型クラスの作り方"><a href="#%E5%9E%8B%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E4%BD%9C%E3%82%8A%E6%96%B9">型クラスの作り方</a></h2> <p>下記のような感じ。CustomPanel has many CustomPanelCodeという構成の例。</p> <pre><code class="dart">import 'package:anyone_composer/models/custom_panel_code.dart'; import 'package:hive/hive.dart'; @HiveType(typeId: 0) class CustomPanel extends HiveObject { @HiveField(0) String name; @HiveField(1) HiveList<CustomPanelCode> codes; CustomPanel(this.name) { final customPanelCodes = Hive.box<CustomPanelCode>('custom_panel_codes'); codes = HiveList(customPanelCodes); } } </code></pre> <h2 id="起動時の初期化"><a href="#%E8%B5%B7%E5%8B%95%E6%99%82%E3%81%AE%E5%88%9D%E6%9C%9F%E5%8C%96">起動時の初期化</a></h2> <p>hive_flutter側は自動でインポートされないので追記。</p> <pre><code class="dart">import 'package:hive/hive.dart'; import 'package:hive_flutter/hive_flutter.dart'; </code></pre> <pre><code class="dart">void main() async { await Hive.initFlutter(); Hive.registerAdapter(CustomPanelCodeAdapter()); Hive.registerAdapter(CustomPanelAdapter()); await Hive.openBox<CustomPanelCode>('custom_panel_codes'); await Hive.openBox<CustomPanel>('custom_panels'); runApp(MyApp()); } </code></pre> <p>最初だけ、openBoxでロードする。そのあとはアプリケーション内ではawaitなしのboxメソッドで参照を取得することができる。</p> <h3 id="アノテーション"><a href="#%E3%82%A2%E3%83%8E%E3%83%86%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3">アノテーション</a></h3> <p>HiveTypeとHiveFieldは必要。HiveTypeは全体でユニーク、HiveFieldはクラス内でユニーク。</p> <h3 id="Auto Incrementされた値"><a href="#Auto+Increment%E3%81%95%E3%82%8C%E3%81%9F%E5%80%A4">Auto Incrementされた値</a></h3> <p>HiveObjectの継承は、auto_incrementの値を利用する時に必要。こうすることで登録された後は <code>key</code> というプロパティで primary key を参照することが可能になる。</p> <h3 id="リレーション"><a href="#%E3%83%AA%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3">リレーション</a></h3> <p>リレーションは <code>HiveList(ボックスの参照)</code> という形で利用できる。</p> <h2 id="リレーションの利用"><a href="#%E3%83%AA%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E5%88%A9%E7%94%A8">リレーションの利用</a></h2> <p>リレーションは初期化されていれば普通にaddとかで登録できる。ただRDBなどと違うのが、あくまでも実データのリレーションであり、存在しない値を使うことができない。そのため、親データを登録する前にまず実データを登録してからリレーションデータを登録する必要がある。</p> <pre><code class="dart"> await customPanelCodes.addAll(newCustomPanelCodes); customPanel.codes.addAll(newCustomPanelCodes); await customPanels.add(customPanel); </code></pre> <p>一番上の行の事前登録処理がないと下記のようなエラーが出る。</p> <pre><code class="text">Unhandled Exception: HiveError: HiveObjects needs to be in the box "custom_panel_codes". </code></pre> <h2 id="リレーション周りが動かない時"><a href="#%E3%83%AA%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E5%91%A8%E3%82%8A%E3%81%8C%E5%8B%95%E3%81%8B%E3%81%AA%E3%81%84%E6%99%82">リレーション周りが動かない時</a></h2> <p>最初に起動して操作する限りは正常に動いてはいるっぽいのだが、再起動時のデータの読み込みに失敗しているときがあった。どうもリレーション時を利用している時に、データの読み込みに失敗しているらしい。TypeAdapterの <code>reader.read()</code> した値をオブジェクトに代入する時に型エラーが出る。</p> <pre><code class="text">_TypeError (type 'HiveListImpl<HiveObject>' is not a subtype of type 'HiveList<CustomPanelCode>') </code></pre> <p>これが難題で、色々キャストしてみてもうまくいかなかった。</p> <p>ということで、別のまっさらなプロジェクトにhive_generatorを含めたHive関連のパッケージをインストールし、モデルのクラスファイルをコピーしてジェネレートしてみたところ、下記の記述を見つけることができた。</p> <pre><code class="dart"> return CustomPanel( fields[0] as String, )..codes = (fields[1] as HiveList)?.castHiveList(); </code></pre> <p>そう、castHiveListというメソッドを使う……! こんなのほぼ解説されているページもなく、実際のコード例もないのでハマりどころ。支障がなければhive_generatorをちゃんとつかうのが良さそう。最終的に下記のようなアダプタにした。</p> <pre><code class="dart">import 'package:anyone_composer/models/custom_panel.dart'; import 'package:hive/hive.dart'; class CustomPanelAdapter extends TypeAdapter<CustomPanel> { @override final typeId = 0; @override CustomPanel read(BinaryReader reader) { return CustomPanel(reader.read()) ..codes = (reader.read() as HiveList)?.castHiveList(); } @override void write(BinaryWriter writer, CustomPanel obj) { writer.write(obj.name); writer.write(obj.codes); } } </code></pre> <h2 id="hive_generatorの使い方"><a href="#hive_generator%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9">hive_generatorの使い方</a></h2> <p>まずモデル定義したファイルのimportの下あたりに下記のような行を追加する。</p> <pre><code class="dart">part 'custom_panel.g.dart'; </code></pre> <p>ファイルが無いため最初はエラーになるが、生成を実行するとここにファイルが作られる。実行コマンドは下記。</p> <pre><code class="text">flutter packages pub run build_runner build </code></pre> <p>ちなみにリレーションを使っている場合はまずリレーションのないモデルのアダプターを一通り生成しておいてからリレーションのあるモデルを生成する必要がある。リレーション込でいきなりやってしまうとエラーになる。</p> <p>あと、試しにアダプタを別フォルダに生成してもらうと思ったがダメだった(今のところは)。</p> <p>細かくは下記の本家ドキュメント。</p> <p><a target="_blank" rel="nofollow noopener" href="https://docs.hivedb.dev/#/custom-objects/generate_adapter">Generate adapter</a></p> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p>以上、使い始めたばかりの情報なので今後も何か出るかも。</p> だら@Crieit開発者