CINXE.COM

WildFly 34にCDIと組み合わせたDoma 3を使ったアプリケーションをデプロイする(Arquillianでのインテグレーションテスト付き) - CLOVER🍀

<!DOCTYPE html> <html lang="ja" data-admin-domain="//blog.hatena.ne.jp" data-admin-origin="https://blog.hatena.ne.jp" data-author="Kazuhira" data-avail-langs="ja en" data-blog="kazuhira-r.hatenablog.com" data-blog-host="kazuhira-r.hatenablog.com" data-blog-is-public="1" data-blog-name="CLOVER🍀" data-blog-owner="Kazuhira" data-blog-show-ads="1" data-blog-show-sleeping-ads="" data-blog-uri="https://kazuhira-r.hatenablog.com/" data-blog-uuid="10257846132617921657" data-blogs-uri-base="https://kazuhira-r.hatenablog.com" data-brand="hatenablog" data-data-layer="{&quot;hatenablog&quot;:{&quot;admin&quot;:{},&quot;analytics&quot;:{&quot;brand_property_id&quot;:&quot;&quot;,&quot;measurement_id&quot;:&quot;G-VZ4P54MK8T&quot;,&quot;non_sampling_property_id&quot;:&quot;&quot;,&quot;property_id&quot;:&quot;&quot;,&quot;separated_property_id&quot;:&quot;UA-29716941-26&quot;},&quot;blog&quot;:{&quot;blog_id&quot;:&quot;10257846132617921657&quot;,&quot;content_seems_japanese&quot;:&quot;true&quot;,&quot;disable_ads&quot;:&quot;&quot;,&quot;enable_ads&quot;:&quot;true&quot;,&quot;enable_keyword_link&quot;:&quot;true&quot;,&quot;entry_show_footer_related_entries&quot;:&quot;true&quot;,&quot;force_pc_view&quot;:&quot;false&quot;,&quot;is_public&quot;:&quot;true&quot;,&quot;is_responsive_view&quot;:&quot;false&quot;,&quot;is_sleeping&quot;:&quot;false&quot;,&quot;lang&quot;:&quot;ja&quot;,&quot;name&quot;:&quot;CLOVER\ud83c\udf40&quot;,&quot;owner_name&quot;:&quot;Kazuhira&quot;,&quot;uri&quot;:&quot;https://kazuhira-r.hatenablog.com/&quot;},&quot;brand&quot;:&quot;hatenablog&quot;,&quot;page_id&quot;:&quot;entry&quot;,&quot;permalink_entry&quot;:{&quot;author_name&quot;:&quot;Kazuhira&quot;,&quot;categories&quot;:&quot;Doma\tWildFly\tArquillian\tMaven\tCDI&quot;,&quot;character_count&quot;:31519,&quot;date&quot;:&quot;2024-11-25&quot;,&quot;entry_id&quot;:&quot;6802418398306472777&quot;,&quot;first_category&quot;:&quot;Doma&quot;,&quot;hour&quot;:&quot;0&quot;,&quot;title&quot;:&quot;WildFly 34\u306bCDI\u3068\u7d44\u307f\u5408\u308f\u305b\u305fDoma 3\u3092\u4f7f\u3063\u305f\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u30c7\u30d7\u30ed\u30a4\u3059\u308b\uff08Arquillian\u3067\u306e\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u30c6\u30b9\u30c8\u4ed8\u304d\uff09&quot;,&quot;uri&quot;:&quot;https://kazuhira-r.hatenablog.com/entry/2024/11/25/001157&quot;},&quot;pro&quot;:&quot;free&quot;,&quot;router_type&quot;:&quot;blogs&quot;}}" data-device="pc" data-dont-recommend-pro="false" data-global-domain="https://hatena.blog" data-globalheader-color="b" data-globalheader-type="pc" data-has-touch-view="1" data-help-url="https://help.hatenablog.com" data-page="entry" data-parts-domain="https://hatenablog-parts.com" data-plus-available="" data-pro="false" data-router-type="blogs" data-sentry-dsn="https://03a33e4781a24cf2885099fed222b56d@sentry.io/1195218" data-sentry-environment="production" data-sentry-sample-rate="0.1" data-static-domain="https://cdn.blog.st-hatena.com" data-version="b06a9d4929119667e7027e25c25079" data-initial-state="{}" > <head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# article: http://ogp.me/ns/article#"> <meta name="robots" content="max-image-preview:large" /> <meta charset="utf-8"/> <meta http-equiv="X-UA-Compatible" content="IE=7; IE=9; IE=10; IE=11" /> <title>WildFly 34にCDIと組み合わせたDoma 3を使ったアプリケーションをデプロイする(Arquillianでのインテグレーションテスト付き) - CLOVER🍀</title> <link rel="canonical" href="https://kazuhira-r.hatenablog.com/entry/2024/11/25/001157"/> <meta itemprop="name" content="WildFly 34にCDIと組み合わせたDoma 3を使ったアプリケーションをデプロイする(Arquillianでのインテグレーションテスト付き) - CLOVER🍀"/> <meta itemprop="image" content="https://ogimage.blog.st-hatena.com/10257846132617921657/6802418398306472777/1732461445"/> <meta property="og:title" content="WildFly 34にCDIと組み合わせたDoma 3を使ったアプリケーションをデプロイする(Arquillianでのインテグレーションテスト付き) - CLOVER🍀"/> <meta property="og:type" content="article"/> <meta property="og:url" content="https://kazuhira-r.hatenablog.com/entry/2024/11/25/001157"/> <meta property="og:image" content="https://ogimage.blog.st-hatena.com/10257846132617921657/6802418398306472777/1732461445"/> <meta property="og:image:alt" content="WildFly 34にCDIと組み合わせたDoma 3を使ったアプリケーションをデプロイする(Arquillianでのインテグレーションテスト付き) - CLOVER🍀"/> <meta property="og:description" content="これは、なにをしたくて書いたもの? このブログではJakarta EE(Java EE)環境ではないところではデータベースアクセスにDomaをよく使っているのですが、そういえば Jakarta EE(Java EE)のアプリケーションサーバーで使うように設定したことがないな、と思いまして。 1度やってみることにしました。デプロイするアプリケーションサーバーはWildFly 34.0.1.Finalとします。 ついでにArquillianを使ったテストも書いてみます。 DomaをCDIと組み合わせる DomaをJakarta Contexts and Dependency Injection(以…" /> <meta property="og:site_name" content="CLOVER🍀"/> <meta property="article:published_time" content="1732461117" /> <meta property="article:tag" content="Doma" /> <meta property="article:tag" content="WildFly" /> <meta property="article:tag" content="Arquillian" /> <meta property="article:tag" content="Maven" /> <meta property="article:tag" content="CDI" /> <meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:image" content="https://ogimage.blog.st-hatena.com/10257846132617921657/6802418398306472777/1732461445" /> <meta name="twitter:title" content="WildFly 34にCDIと組み合わせたDoma 3を使ったアプリケーションをデプロイする(Arquillianでのインテグレーションテスト付き) - CLOVER🍀" /> <meta name="twitter:description" content="これは、なにをしたくて書いたもの? このブログではJakarta EE(Java EE)環境ではないところではデータベースアクセスにDomaをよく使っているのですが、そういえば Jakarta EE(Java EE)のアプリケーションサーバーで使うように設定したことがないな、と思いまして。 1度やってみることにしました…" /> <meta name="twitter:app:name:iphone" content="はてなブログアプリ" /> <meta name="twitter:app:id:iphone" content="583299321" /> <meta name="twitter:app:url:iphone" content="hatenablog:///open?uri=https%3A%2F%2Fkazuhira-r.hatenablog.com%2Fentry%2F2024%2F11%2F25%2F001157" /> <meta name="twitter:site" content="@kazuhira_r" /> <meta name="description" content="これは、なにをしたくて書いたもの? このブログではJakarta EE(Java EE)環境ではないところではデータベースアクセスにDomaをよく使っているのですが、そういえば Jakarta EE(Java EE)のアプリケーションサーバーで使うように設定したことがないな、と思いまして。 1度やってみることにしました。デプロイするアプリケーションサーバーはWildFly 34.0.1.Finalとします。 ついでにArquillianを使ったテストも書いてみます。 DomaをCDIと組み合わせる DomaをJakarta Contexts and Dependency Injection(以…" /> <script id="embed-gtm-data-layer-loader" data-data-layer-page-specific="{&quot;hatenablog&quot;:{&quot;blogs_permalink&quot;:{&quot;has_related_entries_with_elasticsearch&quot;:&quot;true&quot;,&quot;is_author_pro&quot;:&quot;false&quot;,&quot;is_blog_sleeping&quot;:&quot;false&quot;,&quot;entry_afc_issued&quot;:&quot;false&quot;,&quot;blog_afc_issued&quot;:&quot;false&quot;}}}" > (function() { function loadDataLayer(elem, attrName) { if (!elem) { return {}; } var json = elem.getAttribute(attrName); if (!json) { return {}; } return JSON.parse(json); } var globalVariables = loadDataLayer( document.documentElement, 'data-data-layer' ); var pageSpecificVariables = loadDataLayer( document.getElementById('embed-gtm-data-layer-loader'), 'data-data-layer-page-specific' ); var variables = [globalVariables, pageSpecificVariables]; if (!window.dataLayer) { window.dataLayer = []; } for (var i = 0; i < variables.length; i++) { window.dataLayer.push(variables[i]); } })(); </script> <!-- Google Tag Manager --> <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); })(window,document,'script','dataLayer','GTM-P4CXTW');</script> <!-- End Google Tag Manager --> <link rel="shortcut icon" href="https://kazuhira-r.hatenablog.com/icon/favicon"> <link rel="apple-touch-icon" href="https://kazuhira-r.hatenablog.com/icon/touch"> <link rel="icon" sizes="192x192" href="https://kazuhira-r.hatenablog.com/icon/link"> <link rel="alternate" type="application/atom+xml" title="Atom" href="https://kazuhira-r.hatenablog.com/feed"/> <link rel="alternate" type="application/rss+xml" title="RSS2.0" href="https://kazuhira-r.hatenablog.com/rss"/> <link rel="alternate" type="application/json+oembed" href="https://hatena.blog/oembed?url=https%3A%2F%2Fkazuhira-r.hatenablog.com%2Fentry%2F2024%2F11%2F25%2F001157&amp;format=json" title="oEmbed Profile of WildFly 34にCDIと組み合わせたDoma 3を使ったアプリケーションをデプロイする(Arquillianでのインテグレーションテスト付き)"/> <link rel="alternate" type="text/xml+oembed" href="https://hatena.blog/oembed?url=https%3A%2F%2Fkazuhira-r.hatenablog.com%2Fentry%2F2024%2F11%2F25%2F001157&amp;format=xml" title="oEmbed Profile of WildFly 34にCDIと組み合わせたDoma 3を使ったアプリケーションをデプロイする(Arquillianでのインテグレーションテスト付き)"/> <link rel="author" href="http://www.hatena.ne.jp/Kazuhira/"> <link rel="stylesheet" type="text/css" href="https://cdn.blog.st-hatena.com/css/blog.css?version=b06a9d4929119667e7027e25c25079"/> <link rel="stylesheet" type="text/css" href="https://usercss.blog.st-hatena.com/blog_style/10257846132617921657/631863f85a86318ae70c47237b8e4ad12e353cc2"/> <script> </script> <style> div#google_afc_user, div.google-afc-user-container, div.google_afc_image, div.google_afc_blocklink { display: block !important; } </style> <script src="https://cdn.pool.st-hatena.com/valve/valve.js" async></script> <script id="test-valve-definition"> var valve = window.valve || []; valve.push(function(v) { v.config({ service: 'blog', content: { result: 'adtrust', documentIds: ["blog:entry:6802418398306472777"] } }); v.defineDFPSlot({"lazy":1,"sizes":{"mappings":[[[320,568],[[336,280],[300,250],"fluid"]],[[0,0],[[300,250]]]]},"slotId":"ad-in-entry","unit":"/4374287/blog_pc_entry_sleep_in-article"}); v.defineDFPSlot({"lazy":"","sizes":[[300,250],[336,280],[468,60],"fluid"],"slotId":"google_afc_user_container_0","unit":"/4374287/blog_user"}); v.sealDFPSlots(); }); </script> <script type="application/ld+json">{"@context":"http://schema.org","@type":"Article","dateModified":"2024-11-25T00:17:25+09:00","datePublished":"2024-11-25T00:11:57+09:00","headline":"WildFly 34にCDIと組み合わせたDoma 3を使ったアプリケーションをデプロイする(Arquillianでのインテグレーションテスト付き)","image":["https://cdn.blog.st-hatena.com/images/theme/og-image-1500.png"]}</script> <script type="module"> import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11.2.1/dist/mermaid.esm.min.mjs'; mermaid.initialize({ startOnLoad: true }); </script> </head> <body class="page-entry category-Doma category-WildFly category-Arquillian category-Maven category-CDI globalheader-ng-enabled"> <div id="globalheader-container" data-brand="hatenablog" > <iframe id="globalheader" height="37" frameborder="0" allowTransparency="true"></iframe> </div> <nav class=" blog-controlls "> <div class="blog-controlls-blog-icon"> <a href="https://kazuhira-r.hatenablog.com/"> <img src="https://cdn.blog.st-hatena.com/images/admin/blog-icon-noimage.png" alt="CLOVER🍀"/> </a> </div> <div class="blog-controlls-title"> <a href="https://kazuhira-r.hatenablog.com/">CLOVER🍀</a> </div> <a href="https://blog.hatena.ne.jp/Kazuhira/kazuhira-r.hatenablog.com/subscribe?utm_medium=button&amp;utm_source=blogs_topright_button&amp;utm_campaign=subscribe_blog" class="blog-controlls-subscribe-btn test-blog-header-controlls-subscribe"> 読者になる </a> </nav> <div id="container"> <div id="container-inner"> <header id="blog-title" data-brand="hatenablog"> <div id="blog-title-inner" > <div id="blog-title-content"> <h1 id="title"><a href="https://kazuhira-r.hatenablog.com/">CLOVER🍀</a></h1> <h2 id="blog-description">That was when it all began.</h2> </div> </div> </header> <div id="content" class="hfeed" > <div id="content-inner"> <div id="wrapper"> <div id="main"> <div id="main-inner"> <!-- google_ad_section_start --> <!-- rakuten_ad_target_begin --> <article class="entry hentry test-hentry js-entry-article date-first autopagerize_page_element chars-20000 words-2000 mode-markdown entry-odd" id="entry-6802418398306472777" data-keyword-campaign="" data-uuid="6802418398306472777" data-publication-type="entry"> <div class="entry-inner"> <header class="entry-header"> <div class="date entry-date first"> <a href="https://kazuhira-r.hatenablog.com/archive/2024/11/25" rel="nofollow"> <time datetime="2024-11-24T15:11:57Z" title="2024-11-24T15:11:57Z"> <span class="date-year">2024</span><span class="hyphen">-</span><span class="date-month">11</span><span class="hyphen">-</span><span class="date-day">25</span> </time> </a> </div> <h1 class="entry-title"> <a href="https://kazuhira-r.hatenablog.com/entry/2024/11/25/001157" class="entry-title-link bookmark">WildFly 34にCDIと組み合わせたDoma 3を使ったアプリケーションをデプロイする(Arquillianでのインテグレーションテスト付き)</a> </h1> <div class="entry-categories categories"> <a href="https://kazuhira-r.hatenablog.com/archive/category/Doma" class="entry-category-link category-Doma">Doma</a> <a href="https://kazuhira-r.hatenablog.com/archive/category/WildFly" class="entry-category-link category-WildFly">WildFly</a> <a href="https://kazuhira-r.hatenablog.com/archive/category/Arquillian" class="entry-category-link category-Arquillian">Arquillian</a> <a href="https://kazuhira-r.hatenablog.com/archive/category/Maven" class="entry-category-link category-Maven">Maven</a> <a href="https://kazuhira-r.hatenablog.com/archive/category/CDI" class="entry-category-link category-CDI">CDI</a> </div> </header> <div class="entry-content hatenablog-entry"> <h4 id="これはなにをしたくて書いたもの">これは、なにをしたくて書いたもの?</h4> <p>このブログでは<a class="keyword" href="https://d.hatena.ne.jp/keyword/Jakarta">Jakarta</a> EE(<a class="keyword" href="https://d.hatena.ne.jp/keyword/Java%20EE">Java EE</a>)環境ではないところではデータベースアクセスに<a class="keyword" href="https://d.hatena.ne.jp/keyword/Doma">Doma</a>をよく使っているのですが、そういえば<br/> <a class="keyword" href="https://d.hatena.ne.jp/keyword/Jakarta">Jakarta</a> EE(<a class="keyword" href="https://d.hatena.ne.jp/keyword/Java%20EE">Java EE</a>)の<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A2%A5%D7%A5%EA%A5%B1%A1%BC%A5%B7%A5%E7%A5%F3%A5%B5%A1%BC%A5%D0">アプリケーションサーバ</a>ーで使うように設定したことがないな、と思いまして。</p> <p>1度やってみることにしました。デプロイする<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A2%A5%D7%A5%EA%A5%B1%A1%BC%A5%B7%A5%E7%A5%F3%A5%B5%A1%BC%A5%D0">アプリケーションサーバ</a>ーは<a class="keyword" href="https://d.hatena.ne.jp/keyword/WildFly">WildFly</a> 34.0.1.Finalとします。</p> <p>ついでにArquillianを使ったテストも書いてみます。</p> <h4 id="DomaをCDIと組み合わせる"><a class="keyword" href="https://d.hatena.ne.jp/keyword/Doma">Doma</a>を<a class="keyword" href="https://d.hatena.ne.jp/keyword/CDI">CDI</a>と組み合わせる</h4> <p><a class="keyword" href="https://d.hatena.ne.jp/keyword/Doma">Doma</a>を<a class="keyword" href="https://d.hatena.ne.jp/keyword/Jakarta">Jakarta</a> Contexts and <a class="keyword" href="https://d.hatena.ne.jp/keyword/Dependency">Dependency</a> Injection(以降<a class="keyword" href="https://d.hatena.ne.jp/keyword/CDI">CDI</a>)と組み合わせるには、<a class="keyword" href="https://d.hatena.ne.jp/keyword/Doma">Doma</a>のドキュメントのこちらのページを参考にします。</p> <p><a href="https://doma.readthedocs.io/ja/3.1.0/config/#advanced-definition">設定 / 設定クラスの定義 / 高度な定義</a></p> <p>今回はDaoとConfigを<a class="keyword" href="https://d.hatena.ne.jp/keyword/CDI">CDI</a>管理Beanとすることを考えます。</p> <p>Daoの実装クラスに付与する<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A2%A5%CE%A5%C6%A1%BC%A5%B7%A5%E7%A5%F3">アノテーション</a>を制御するには、<code>AnnotateWith</code>と<code>Annotation</code>の2つの<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A2%A5%CE%A5%C6%A1%BC%A5%B7%A5%E7%A5%F3">アノテーション</a>を使います。</p> <p>ドキュメントの例はこちらです。</p> <pre class="code lang-java" data-lang="java" data-unlink><span class="synPreProc">@Dao</span> <span class="synPreProc">@AnnotateWith</span>(annotations = { <span class="synPreProc">@Annotation</span>(target = AnnotationTarget.CONSTRUCTOR, type = javax.inject.Inject.<span class="synType">class</span>), <span class="synPreProc">@Annotation</span>(target = AnnotationTarget.CONSTRUCTOR_PARAMETER, type = javax.inject.Named.<span class="synType">class</span>, elements = <span class="synConstant">&quot;</span><span class="synSpecial">\&quot;</span><span class="synConstant">config</span><span class="synSpecial">\&quot;</span><span class="synConstant">&quot;</span>) }) <span class="synType">public</span> <span class="synType">interface</span> EmployeeDao { <span class="synPreProc">@Select</span> Employee selectById(Integer id); } </pre> <p>ここで、Configをあらかじめ<a class="keyword" href="https://d.hatena.ne.jp/keyword/CDI">CDI</a>管理Beanとして定義しておいて</p> <pre class="code lang-java" data-lang="java" data-unlink><span class="synPreProc">@ApplicationScoped</span> <span class="synType">public</span> <span class="synType">class</span> DomaConfig <span class="synType">implements</span> Config { <span class="synIdentifier">...</span> } </pre> <p>Daoを<a class="keyword" href="https://d.hatena.ne.jp/keyword/CDI">CDI</a>管理Beanとして定義しつつ、Config`をインジェクションするにはこんな感じで書きます。</p> <pre class="code lang-java" data-lang="java" data-unlink><span class="synPreProc">@Dao</span> <span class="synPreProc">@AnnotateWith</span>(annotations = { <span class="synPreProc">@Annotation</span>(target = AnnotationTarget.CLASS, type = ApplicationScoped.<span class="synType">class</span>), <span class="synPreProc">@Annotation</span>(target = AnnotationTarget.CONSTRUCTOR, type = Inject.<span class="synType">class</span>) }) <span class="synType">public</span> <span class="synType">interface</span> EmployeeDao { <span class="synPreProc">@Select</span> Employee selectById(Integer id); } </pre> <p>生成されるDaoの実装クラスはこんなイメージになります。</p> <pre class="code lang-java" data-lang="java" data-unlink><span class="synPreProc">@ApplicationScoped</span> <span class="synType">public</span> <span class="synType">class</span> EmployeeDaoImpl { <span class="synPreProc">@Inject</span> <span class="synType">public</span> EmployeeDaoImpl(Config config) { <span class="synIdentifier">...</span> } <span class="synIdentifier">...</span>. } </pre> <p>なお、以下の<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A2%A5%CE%A5%C6%A1%BC%A5%B7%A5%E7%A5%F3">アノテーション</a>の定義は別の<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A2%A5%CE%A5%C6%A1%BC%A5%B7%A5%E7%A5%F3">アノテーション</a>にまとめることもできます。<code>@Dao</code><a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A2%A5%CE%A5%C6%A1%BC%A5%B7%A5%E7%A5%F3">アノテーション</a>はその中には含められません。</p> <pre class="code lang-java" data-lang="java" data-unlink><span class="synPreProc">@AnnotateWith</span>(annotations = { <span class="synPreProc">@Annotation</span>(target = AnnotationTarget.CLASS, type = ApplicationScoped.<span class="synType">class</span>), <span class="synPreProc">@Annotation</span>(target = AnnotationTarget.CONSTRUCTOR, type = Inject.<span class="synType">class</span>) }) </pre> <p>では、試していってみましょう。</p> <h4 id="環境">環境</h4> <p>今回の環境はこちら。</p> <pre class="code shell" data-lang="shell" data-unlink>$ java --version openjdk 21.0.5 2024-10-15 OpenJDK Runtime Environment (build 21.0.5+11-Ubuntu-1ubuntu124.04) OpenJDK 64-Bit Server VM (build 21.0.5+11-Ubuntu-1ubuntu124.04, mixed mode, sharing) $ mvn --version Apache Maven 3.9.9 (8e8579a9e76f7d015ee5ec7bfcdc97d260186937) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 21.0.5, vendor: Ubuntu, runtime: /usr/lib/jvm/java-21-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: &#34;linux&#34;, version: &#34;6.8.0-49-generic&#34;, arch: &#34;amd64&#34;, family: &#34;unix&#34;</pre> <p>データベースには<a class="keyword" href="https://d.hatena.ne.jp/keyword/MySQL">MySQL</a>を使います。<a class="keyword" href="https://d.hatena.ne.jp/keyword/MySQL">MySQL</a>には172.17.0.2でアクセスできるものとします。</p> <pre class="code lang-sql" data-lang="sql" data-unlink> MySQL localhost:<span class="synConstant">3306</span> ssl practice SQL &gt; select version(); +<span class="synComment">-----------+</span> | version() | +<span class="synComment">-----------+</span> | <span class="synConstant">8.4</span>.<span class="synConstant">3</span> | +<span class="synComment">-----------+</span> <span class="synConstant">1</span> <span class="synSpecial">row</span> <span class="synStatement">in</span> <span class="synStatement">set</span> (<span class="synConstant">0</span>.<span class="synConstant">0187</span> sec) </pre> <h4 id="Domaを使ったアプリケーションを書く"><a class="keyword" href="https://d.hatena.ne.jp/keyword/Doma">Doma</a>を使ったアプリケーションを書く</h4> <p>まずは<a class="keyword" href="https://d.hatena.ne.jp/keyword/Doma">Doma</a>を使ったアプリケーションを書いていきます。</p> <p>テーブル定義。お題は書籍にします。</p> <pre class="code lang-sql" data-lang="sql" data-unlink><span class="synStatement">create</span> <span class="synSpecial">table</span> book( isbn <span class="synType">varchar</span>(<span class="synConstant">14</span>), title <span class="synType">varchar</span>(<span class="synConstant">100</span>), price <span class="synType">int</span>, publish_date <span class="synType">date</span>, primary key(isbn) ); </pre> <p><a class="keyword" href="https://d.hatena.ne.jp/keyword/Maven">Maven</a>依存関係など。</p> <pre class="code lang-xml" data-lang="xml" data-unlink> <span class="synIdentifier">&lt;properties&gt;</span> <span class="synIdentifier">&lt;maven</span><span class="synComment">.</span><span class="synIdentifier">compiler</span><span class="synComment">.</span><span class="synIdentifier">release&gt;</span>21<span class="synIdentifier">&lt;/maven</span><span class="synComment">.</span><span class="synIdentifier">compiler</span><span class="synComment">.</span><span class="synIdentifier">release&gt;</span> <span class="synIdentifier">&lt;project</span><span class="synComment">.</span><span class="synIdentifier">build</span><span class="synComment">.</span><span class="synIdentifier">sourceEncoding&gt;</span>UTF-8<span class="synIdentifier">&lt;/project</span><span class="synComment">.</span><span class="synIdentifier">build</span><span class="synComment">.</span><span class="synIdentifier">sourceEncoding&gt;</span> <span class="synIdentifier">&lt;project</span><span class="synComment">.</span><span class="synIdentifier">reporting</span><span class="synComment">.</span><span class="synIdentifier">outputEncoding&gt;</span>UTF-8<span class="synIdentifier">&lt;/project</span><span class="synComment">.</span><span class="synIdentifier">reporting</span><span class="synComment">.</span><span class="synIdentifier">outputEncoding&gt;</span> <span class="synIdentifier">&lt;/properties&gt;</span> <span class="synIdentifier">&lt;dependencyManagement&gt;</span> <span class="synIdentifier">&lt;dependencies&gt;</span> <span class="synIdentifier">&lt;dependency&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>org.wildfly.bom<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>wildfly-ee-with-tools<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>34.0.1.Final<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;type&gt;</span>pom<span class="synIdentifier">&lt;/type&gt;</span> <span class="synIdentifier">&lt;scope&gt;</span>import<span class="synIdentifier">&lt;/scope&gt;</span> <span class="synIdentifier">&lt;/dependency&gt;</span> <span class="synIdentifier">&lt;dependency&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>org.junit<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>junit-bom<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>5.11.3<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;type&gt;</span>pom<span class="synIdentifier">&lt;/type&gt;</span> <span class="synIdentifier">&lt;scope&gt;</span>import<span class="synIdentifier">&lt;/scope&gt;</span> <span class="synIdentifier">&lt;/dependency&gt;</span> <span class="synIdentifier">&lt;/dependencies&gt;</span> <span class="synIdentifier">&lt;/dependencyManagement&gt;</span> <span class="synIdentifier">&lt;dependencies&gt;</span> <span class="synIdentifier">&lt;dependency&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>jakarta.ws.rs<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>jakarta.ws.rs-api<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;/dependency&gt;</span> <span class="synIdentifier">&lt;dependency&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>jakarta.enterprise<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>jakarta.enterprise.cdi-api<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;/dependency&gt;</span> <span class="synIdentifier">&lt;dependency&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>jakarta.transaction<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>jakarta.transaction-api<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;/dependency&gt;</span> <span class="synIdentifier">&lt;dependency&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>org.seasar.doma<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>doma-core<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>3.1.0<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;/dependency&gt;</span> <span class="synIdentifier">&lt;/dependencies&gt;</span> <span class="synIdentifier">&lt;build&gt;</span> <span class="synIdentifier">&lt;finalName&gt;</span>ROOT<span class="synIdentifier">&lt;/finalName&gt;</span> <span class="synIdentifier">&lt;plugins&gt;</span> <span class="synIdentifier">&lt;plugin&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>org.apache.maven.plugins<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>maven-compiler-plugin<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>3.13.0<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;configuration&gt;</span> <span class="synIdentifier">&lt;annotationProcessorPaths&gt;</span> <span class="synIdentifier">&lt;path&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>org.seasar.doma<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>doma-processor<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>3.1.0<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;/path&gt;</span> <span class="synIdentifier">&lt;/annotationProcessorPaths&gt;</span> <span class="synIdentifier">&lt;/configuration&gt;</span> <span class="synIdentifier">&lt;/plugin&gt;</span> <span class="synIdentifier">&lt;plugin&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>org.apache.maven.plugins<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>maven-war-plugin<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>3.4.0<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;configuration&gt;</span> <span class="synIdentifier">&lt;failOnMissingWebXml&gt;</span>false<span class="synIdentifier">&lt;/failOnMissingWebXml&gt;</span> <span class="synIdentifier">&lt;/configuration&gt;</span> <span class="synIdentifier">&lt;/plugin&gt;</span> <span class="synIdentifier">&lt;/plugins&gt;</span> <span class="synIdentifier">&lt;/build&gt;</span> </pre> <p>ひとまず、テストで必要なもの以外を並べています。</p> <p><a class="keyword" href="https://d.hatena.ne.jp/keyword/Doma">Doma</a>に関するものは<a class="keyword" href="https://d.hatena.ne.jp/keyword/doma">doma</a>-coreと</p> <pre class="code lang-xml" data-lang="xml" data-unlink> <span class="synIdentifier">&lt;dependency&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>org.seasar.doma<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>doma-core<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>3.1.0<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;/dependency&gt;</span> </pre> <p>Pluggable Annotation Processing <a class="keyword" href="https://d.hatena.ne.jp/keyword/API">API</a>の設定ですね。</p> <pre class="code lang-xml" data-lang="xml" data-unlink> <span class="synIdentifier">&lt;plugin&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>org.apache.maven.plugins<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>maven-compiler-plugin<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>3.13.0<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;configuration&gt;</span> <span class="synIdentifier">&lt;annotationProcessorPaths&gt;</span> <span class="synIdentifier">&lt;path&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>org.seasar.doma<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>doma-processor<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>3.1.0<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;/path&gt;</span> <span class="synIdentifier">&lt;/annotationProcessorPaths&gt;</span> <span class="synIdentifier">&lt;/configuration&gt;</span> <span class="synIdentifier">&lt;/plugin&gt;</span> </pre> <p><a class="keyword" href="https://d.hatena.ne.jp/keyword/Doma">Doma</a>のConfigクラスの実装。<a class="keyword" href="https://d.hatena.ne.jp/keyword/CDI">CDI</a>管理Beanとして定義します。</p> <p><code>src/main/java/org/littlewings/wildfly/doma/config/DomaConfig.java</code></p> <pre class="code lang-java" data-lang="java" data-unlink><span class="synPreProc">package</span> org.littlewings.wildfly.doma.config; <span class="synPreProc">import</span> javax.sql.DataSource; <span class="synPreProc">import</span> jakarta.annotation.Resource; <span class="synPreProc">import</span> jakarta.enterprise.context.ApplicationScoped; <span class="synPreProc">import</span> org.seasar.doma.jdbc.Config; <span class="synPreProc">import</span> org.seasar.doma.jdbc.Naming; <span class="synPreProc">import</span> org.seasar.doma.jdbc.dialect.Dialect; <span class="synPreProc">import</span> org.seasar.doma.jdbc.dialect.MysqlDialect; <span class="synPreProc">@ApplicationScoped</span> <span class="synType">public</span> <span class="synType">class</span> DomaConfig <span class="synType">implements</span> Config { <span class="synPreProc">@Resource</span>(name = <span class="synConstant">&quot;java:jboss/datasources/MySqlDs&quot;</span>) <span class="synType">private</span> DataSource dataSource; <span class="synType">private</span> Dialect dialect = <span class="synStatement">new</span> MysqlDialect(); <span class="synPreProc">@Override</span> <span class="synType">public</span> DataSource getDataSource() { <span class="synStatement">return</span> dataSource; } <span class="synPreProc">@Override</span> <span class="synType">public</span> Dialect getDialect() { <span class="synStatement">return</span> dialect; } <span class="synPreProc">@Override</span> <span class="synType">public</span> Naming getNaming() { <span class="synStatement">return</span> Naming.SNAKE_LOWER_CASE; } } </pre> <p><code>DataSource</code>はJNDIでインジェクションしていますが、この定義はまた後で。</p> <p>エンティティ。</p> <p><code>src/main/java/org/littlewings/wildfly/doma/entity/Book.java</code></p> <pre class="code lang-java" data-lang="java" data-unlink><span class="synPreProc">package</span> org.littlewings.wildfly.doma.entity; <span class="synPreProc">import</span> java.time.LocalDate; <span class="synPreProc">import</span> org.seasar.doma.Entity; <span class="synPreProc">import</span> org.seasar.doma.Id; <span class="synPreProc">@Entity</span> <span class="synType">public</span> record Book( <span class="synPreProc">@Id</span> String isbn, String title, Integer price, LocalDate publishDate ) { } </pre> <p>Dao。</p> <p><code>src/main/java/org/littlewings/wildfly/doma/dao/BookDao.java</code></p> <pre class="code lang-java" data-lang="java" data-unlink><span class="synPreProc">package</span> org.littlewings.wildfly.doma.dao; <span class="synPreProc">import</span> java.util.List; <span class="synPreProc">import</span> jakarta.enterprise.context.ApplicationScoped; <span class="synPreProc">import</span> jakarta.inject.Inject; <span class="synPreProc">import</span> org.littlewings.wildfly.doma.entity.Book; <span class="synPreProc">import</span> org.seasar.doma.AnnotateWith; <span class="synPreProc">import</span> org.seasar.doma.Annotation; <span class="synPreProc">import</span> org.seasar.doma.AnnotationTarget; <span class="synPreProc">import</span> org.seasar.doma.Dao; <span class="synPreProc">import</span> org.seasar.doma.Delete; <span class="synPreProc">import</span> org.seasar.doma.Insert; <span class="synPreProc">import</span> org.seasar.doma.Select; <span class="synPreProc">import</span> org.seasar.doma.Sql; <span class="synPreProc">import</span> org.seasar.doma.Update; <span class="synPreProc">import</span> org.seasar.doma.jdbc.Result; <span class="synPreProc">@Dao</span> <span class="synPreProc">@AnnotateWith</span>(annotations = { <span class="synPreProc">@Annotation</span>(target = AnnotationTarget.CLASS, type = ApplicationScoped.<span class="synType">class</span>), <span class="synPreProc">@Annotation</span>(target = AnnotationTarget.CONSTRUCTOR, type = Inject.<span class="synType">class</span>) }) <span class="synType">public</span> <span class="synType">interface</span> BookDao { <span class="synPreProc">@Select</span> Book findByIsbn(String isbn); <span class="synPreProc">@Select</span> List&lt;Book&gt; findAllByPriceAsc(); <span class="synPreProc">@Insert</span> Result&lt;Book&gt; insert(Book book); <span class="synPreProc">@Update</span> Result&lt;Book&gt; update(Book book); <span class="synPreProc">@Delete</span> Result&lt;Book&gt; delete(Book book); } </pre> <p>この定義でDaoの実装クラスが<a class="keyword" href="https://d.hatena.ne.jp/keyword/CDI">CDI</a>管理Beanになり、実装したConfigの<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>がインジェクションされます。</p> <pre class="code lang-java" data-lang="java" data-unlink><span class="synPreProc">@Dao</span> <span class="synPreProc">@AnnotateWith</span>(annotations = { <span class="synPreProc">@Annotation</span>(target = AnnotationTarget.CLASS, type = ApplicationScoped.<span class="synType">class</span>), <span class="synPreProc">@Annotation</span>(target = AnnotationTarget.CONSTRUCTOR, type = Inject.<span class="synType">class</span>) }) <span class="synType">public</span> <span class="synType">interface</span> BookDao { </pre> <p>説明に書いたように、こんな感じで<code>@AnnotateWith</code>と<code>@Annotation</code><a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A2%A5%CE%A5%C6%A1%BC%A5%B7%A5%E7%A5%F3">アノテーション</a>をまとめた<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A2%A5%CE%A5%C6%A1%BC%A5%B7%A5%E7%A5%F3">アノテーション</a>を作成して</p> <p><code>src/main/java/org/littlewings/wildfly/doma/dao/WithCdi.java</code></p> <pre class="code lang-java" data-lang="java" data-unlink><span class="synPreProc">package</span> org.littlewings.wildfly.doma.dao; <span class="synPreProc">import</span> java.lang.annotation.Documented; <span class="synPreProc">import</span> java.lang.annotation.ElementType; <span class="synPreProc">import</span> java.lang.annotation.Inherited; <span class="synPreProc">import</span> java.lang.annotation.Retention; <span class="synPreProc">import</span> java.lang.annotation.RetentionPolicy; <span class="synPreProc">import</span> java.lang.annotation.Target; <span class="synPreProc">import</span> jakarta.enterprise.context.ApplicationScoped; <span class="synPreProc">import</span> jakarta.inject.Inject; <span class="synPreProc">import</span> org.seasar.doma.AnnotateWith; <span class="synPreProc">import</span> org.seasar.doma.Annotation; <span class="synPreProc">import</span> org.seasar.doma.AnnotationTarget; <span class="synPreProc">@AnnotateWith</span>(annotations = { <span class="synPreProc">@Annotation</span>(target = AnnotationTarget.CLASS, type = ApplicationScoped.<span class="synType">class</span>), <span class="synPreProc">@Annotation</span>(target = AnnotationTarget.CONSTRUCTOR, type = Inject.<span class="synType">class</span>) }) <span class="synPreProc">@Documented</span> <span class="synPreProc">@Inherited</span> <span class="synPreProc">@Target</span>(ElementType.TYPE) <span class="synPreProc">@Retention</span>(RetentionPolicy.RUNTIME) <span class="synType">public</span> <span class="synType">@interface</span> WithCdi { } </pre> <p>これをDaoに付与してもOKです。</p> <pre class="code lang-java" data-lang="java" data-unlink><span class="synPreProc">@Dao</span> <span class="synPreProc">@WithCdi</span> <span class="synType">public</span> <span class="synType">interface</span> BookDao { </pre> <p>Daoに対応する<a class="keyword" href="https://d.hatena.ne.jp/keyword/SQL">SQL</a>ファイル。<code>@Sql</code>で定義してもよかったのですが、今回はこちらの構成にしました。</p> <p><code>src/main/resources/META-INF/org/littlewings/wildfly/doma/dao/BookDao/findByIsbn.sql</code></p> <pre class="code lang-sql" data-lang="sql" data-unlink><span class="synStatement">select</span> <span class="synComment">/*%expand*/</span>* <span class="synSpecial">from</span> book <span class="synSpecial">where</span> isbn = <span class="synComment">/* isbn */</span><span class="synSpecial">'</span><span class="synConstant">abcde</span><span class="synSpecial">'</span> </pre> <p><code>src/main/resources/META-INF/org/littlewings/wildfly/doma/dao/BookDao/findAllByPriceAsc.sql</code></p> <pre class="code lang-sql" data-lang="sql" data-unlink><span class="synStatement">select</span> <span class="synComment">/*%expand*/</span>* <span class="synSpecial">from</span> book <span class="synSpecial">order</span> <span class="synSpecial">by</span> price <span class="synSpecial">asc</span> </pre> <p><a class="keyword" href="https://d.hatena.ne.jp/keyword/Jakarta">Jakarta</a> RESTful Web Services(以降<a class="keyword" href="https://d.hatena.ne.jp/keyword/JAX-RS">JAX-RS</a>)リソースクラス。</p> <p><code>src/main/java/org/littlewings/wildfly/doma/resource/BooksResource.java</code></p> <pre class="code lang-java" data-lang="java" data-unlink><span class="synPreProc">package</span> org.littlewings.wildfly.doma.resource; <span class="synPreProc">import</span> java.util.List; <span class="synPreProc">import</span> jakarta.enterprise.context.ApplicationScoped; <span class="synPreProc">import</span> jakarta.inject.Inject; <span class="synPreProc">import</span> jakarta.transaction.Transactional; <span class="synPreProc">import</span> jakarta.ws.rs.Consumes; <span class="synPreProc">import</span> jakarta.ws.rs.DELETE; <span class="synPreProc">import</span> jakarta.ws.rs.GET; <span class="synPreProc">import</span> jakarta.ws.rs.POST; <span class="synPreProc">import</span> jakarta.ws.rs.PUT; <span class="synPreProc">import</span> jakarta.ws.rs.Path; <span class="synPreProc">import</span> jakarta.ws.rs.PathParam; <span class="synPreProc">import</span> jakarta.ws.rs.Produces; <span class="synPreProc">import</span> jakarta.ws.rs.core.MediaType; <span class="synPreProc">import</span> org.littlewings.wildfly.doma.dao.BookDao; <span class="synPreProc">import</span> org.littlewings.wildfly.doma.entity.Book; <span class="synPreProc">@Path</span>(<span class="synConstant">&quot;/books&quot;</span>) <span class="synPreProc">@ApplicationScoped</span> <span class="synPreProc">@Transactional</span> <span class="synType">public</span> <span class="synType">class</span> BooksResource { <span class="synPreProc">@Inject</span> <span class="synType">private</span> BookDao bookDao; <span class="synPreProc">@GET</span> <span class="synPreProc">@Path</span>(<span class="synConstant">&quot;/{isbn}&quot;</span>) <span class="synPreProc">@Produces</span>(MediaType.APPLICATION_JSON) <span class="synType">public</span> Book findByIsbn(<span class="synPreProc">@PathParam</span>(<span class="synConstant">&quot;isbn&quot;</span>) String isbn) { <span class="synStatement">return</span> bookDao.findByIsbn(isbn); } <span class="synPreProc">@GET</span> <span class="synPreProc">@Produces</span>(MediaType.APPLICATION_JSON) <span class="synType">public</span> List&lt;Book&gt; findAll() { <span class="synStatement">return</span> bookDao.findAllByPriceAsc(); } <span class="synPreProc">@POST</span> <span class="synPreProc">@Consumes</span>(MediaType.APPLICATION_JSON) <span class="synType">public</span> <span class="synType">void</span> register(Book book) { bookDao.insert(book); } <span class="synPreProc">@PUT</span> <span class="synPreProc">@Path</span>(<span class="synConstant">&quot;/{isbn}&quot;</span>) <span class="synPreProc">@Consumes</span>(MediaType.APPLICATION_JSON) <span class="synType">public</span> <span class="synType">void</span> update(<span class="synPreProc">@PathParam</span>(<span class="synConstant">&quot;isbn&quot;</span>) String isbn, Book book) { Book registeredBook = bookDao.findByIsbn(isbn); <span class="synStatement">if</span> (registeredBook == <span class="synConstant">null</span>) { <span class="synStatement">return</span>; } <span class="synStatement">if</span> (!isbn.equals(book.isbn())) { <span class="synStatement">return</span>; } bookDao.update(book); } <span class="synPreProc">@DELETE</span> <span class="synPreProc">@Path</span>(<span class="synConstant">&quot;/{isbn}&quot;</span>) <span class="synPreProc">@Consumes</span>(MediaType.APPLICATION_JSON) <span class="synType">public</span> <span class="synType">void</span> delete(<span class="synPreProc">@PathParam</span>(<span class="synConstant">&quot;isbn&quot;</span>) String isbn) { Book book = bookDao.findByIsbn(isbn); <span class="synStatement">if</span> (book != <span class="synConstant">null</span>) { bookDao.delete(book); } } } </pre> <p><a class="keyword" href="https://d.hatena.ne.jp/keyword/JAX-RS">JAX-RS</a>の有効化。</p> <p><code>src/main/java/org/littlewings/wildfly/doma/RestApplication.java</code></p> <pre class="code lang-java" data-lang="java" data-unlink><span class="synPreProc">package</span> org.littlewings.wildfly.doma; <span class="synPreProc">import</span> jakarta.ws.rs.ApplicationPath; <span class="synPreProc">import</span> jakarta.ws.rs.core.Application; <span class="synPreProc">@ApplicationPath</span>(<span class="synConstant">&quot;/&quot;</span>) <span class="synType">public</span> <span class="synType">class</span> RestApplication <span class="synType">extends</span> Application { } </pre> <h4 id="WildFlyをプロビジョニングする"><a class="keyword" href="https://d.hatena.ne.jp/keyword/WildFly">WildFly</a>をプロビジョニングする</h4> <p><a class="keyword" href="https://d.hatena.ne.jp/keyword/WildFly">WildFly</a>へのデプロイは、<a class="keyword" href="https://d.hatena.ne.jp/keyword/WildFly">WildFly</a> <a class="keyword" href="https://d.hatena.ne.jp/keyword/Maven">Maven</a> Pluginで行うことにします。デプロイというか、プロビジョニングですね。</p> <pre class="code lang-xml" data-lang="xml" data-unlink> <span class="synIdentifier">&lt;plugin&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>org.wildfly.plugins<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>wildfly-maven-plugin<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>5.0.1.Final<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;executions&gt;</span> <span class="synIdentifier">&lt;execution&gt;</span> <span class="synIdentifier">&lt;id&gt;</span>package<span class="synIdentifier">&lt;/id&gt;</span> <span class="synIdentifier">&lt;goals&gt;</span> <span class="synIdentifier">&lt;goal&gt;</span>package<span class="synIdentifier">&lt;/goal&gt;</span> <span class="synIdentifier">&lt;/goals&gt;</span> <span class="synIdentifier">&lt;/execution&gt;</span> <span class="synIdentifier">&lt;/executions&gt;</span> <span class="synIdentifier">&lt;configuration&gt;</span> <span class="synIdentifier">&lt;overwrite-provisioned-server&gt;</span>true<span class="synIdentifier">&lt;/overwrite-provisioned-server&gt;</span> <span class="synIdentifier">&lt;discover-provisioning-info&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>34.0.1.Final<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;add-ons&gt;</span> <span class="synIdentifier">&lt;add-on&gt;</span>mysql<span class="synIdentifier">&lt;/add-on&gt;</span> <span class="synIdentifier">&lt;/add-ons&gt;</span> <span class="synIdentifier">&lt;/discover-provisioning-info&gt;</span> <span class="synIdentifier">&lt;env&gt;</span> <span class="synIdentifier">&lt;MYSQL_DATASOURCE&gt;</span>MySqlDs<span class="synIdentifier">&lt;/MYSQL_DATASOURCE&gt;</span> <span class="synIdentifier">&lt;MYSQL_URL&gt;</span>${jdbc.url}<span class="synIdentifier">&lt;/MYSQL_URL&gt;</span> <span class="synIdentifier">&lt;MYSQL_USER&gt;</span>${jdbc.user}<span class="synIdentifier">&lt;/MYSQL_USER&gt;</span> <span class="synIdentifier">&lt;MYSQL_PASSWORD&gt;</span>${jdbc.password}<span class="synIdentifier">&lt;/MYSQL_PASSWORD&gt;</span> <span class="synIdentifier">&lt;/env&gt;</span> <span class="synIdentifier">&lt;/configuration&gt;</span> <span class="synIdentifier">&lt;/plugin&gt;</span> </pre> <p><a class="keyword" href="https://d.hatena.ne.jp/keyword/MySQL">MySQL</a>のデータソースは、Galleon Feature Packs for integrating datasources into <a class="keyword" href="https://d.hatena.ne.jp/keyword/WildFly">WildFly</a> and <a class="keyword" href="https://d.hatena.ne.jp/keyword/WildFly">WildFly</a> <a class="keyword" href="https://d.hatena.ne.jp/keyword/Preview">Preview</a>を使って定義します。</p> <p><a href="https://github.com/wildfly-extras/wildfly-datasources-galleon-pack">GitHub - wildfly-extras/wildfly-datasources-galleon-pack: WildFly Feature Pack for DataSources</a></p> <p><a class="keyword" href="https://d.hatena.ne.jp/keyword/WildFly">WildFly</a> Glowのアドオンとしては<code>mysql</code>になりますね。</p> <pre class="code lang-xml" data-lang="xml" data-unlink> <span class="synIdentifier">&lt;add-ons&gt;</span> <span class="synIdentifier">&lt;add-on&gt;</span>mysql<span class="synIdentifier">&lt;/add-on&gt;</span> <span class="synIdentifier">&lt;/add-ons&gt;</span> </pre> <p>設定は<a class="keyword" href="https://d.hatena.ne.jp/keyword/%B4%C4%B6%AD%CA%D1%BF%F4">環境変数</a>で行います。ここでの設定は<code>wildfly:dev</code>や<code>wildfly:run</code>ゴールやテストで動かす時に使います。</p> <pre class="code lang-xml" data-lang="xml" data-unlink> <span class="synIdentifier">&lt;env&gt;</span> <span class="synIdentifier">&lt;MYSQL_DATASOURCE&gt;</span>MySqlDs<span class="synIdentifier">&lt;/MYSQL_DATASOURCE&gt;</span> <span class="synIdentifier">&lt;MYSQL_URL&gt;</span>${jdbc.url}<span class="synIdentifier">&lt;/MYSQL_URL&gt;</span> <span class="synIdentifier">&lt;MYSQL_USER&gt;</span>${jdbc.user}<span class="synIdentifier">&lt;/MYSQL_USER&gt;</span> <span class="synIdentifier">&lt;MYSQL_PASSWORD&gt;</span>${jdbc.password}<span class="synIdentifier">&lt;/MYSQL_PASSWORD&gt;</span> <span class="synIdentifier">&lt;/env&gt;</span> </pre> <p>それぞれの<a class="keyword" href="https://d.hatena.ne.jp/keyword/%B4%C4%B6%AD%CA%D1%BF%F4">環境変数</a>の意味はこちらを参照。</p> <p><a href="https://github.com/wildfly-extras/wildfly-datasources-galleon-pack/tree/9.0.0.Final/doc/mysql">https://github.com/wildfly-extras/wildfly-datasources-galleon-pack/tree/9.0.0.Final/doc/mysql</a></p> <p><code>MYSQL_DATASOURCE</code><a class="keyword" href="https://d.hatena.ne.jp/keyword/%B4%C4%B6%AD%CA%D1%BF%F4">環境変数</a>は、データソース名の一部になります。</p> <p>デフォルトだと<code>java:jboss/datasources/${MYSQL_DATASOURCE}</code>というJNDI名になるので、Configで指定していたJNDI名の前提はこういう<br/> 背景になっています。</p> <pre class="code lang-java" data-lang="java" data-unlink><span class="synPreProc">@ApplicationScoped</span> <span class="synType">public</span> <span class="synType">class</span> DomaConfig <span class="synType">implements</span> Config { <span class="synPreProc">@Resource</span>(name = <span class="synConstant">&quot;java:jboss/datasources/MySqlDs&quot;</span>) <span class="synType">private</span> DataSource dataSource; </pre> <p>ちなみに各<a class="keyword" href="https://d.hatena.ne.jp/keyword/%B4%C4%B6%AD%CA%D1%BF%F4">環境変数</a>の値がプロパティになっていますが、今後のテストのことがあって<code>properties</code>に定義しておきました。</p> <pre class="code lang-xml" data-lang="xml" data-unlink> <span class="synIdentifier">&lt;properties&gt;</span> <span class="synIdentifier">&lt;maven</span><span class="synComment">.</span><span class="synIdentifier">compiler</span><span class="synComment">.</span><span class="synIdentifier">release&gt;</span>21<span class="synIdentifier">&lt;/maven</span><span class="synComment">.</span><span class="synIdentifier">compiler</span><span class="synComment">.</span><span class="synIdentifier">release&gt;</span> <span class="synIdentifier">&lt;project</span><span class="synComment">.</span><span class="synIdentifier">build</span><span class="synComment">.</span><span class="synIdentifier">sourceEncoding&gt;</span>UTF-8<span class="synIdentifier">&lt;/project</span><span class="synComment">.</span><span class="synIdentifier">build</span><span class="synComment">.</span><span class="synIdentifier">sourceEncoding&gt;</span> <span class="synIdentifier">&lt;project</span><span class="synComment">.</span><span class="synIdentifier">reporting</span><span class="synComment">.</span><span class="synIdentifier">outputEncoding&gt;</span>UTF-8<span class="synIdentifier">&lt;/project</span><span class="synComment">.</span><span class="synIdentifier">reporting</span><span class="synComment">.</span><span class="synIdentifier">outputEncoding&gt;</span> <span class="synIdentifier">&lt;jdbc</span><span class="synComment">.</span><span class="synIdentifier">url&gt;</span><span class="synType">&lt;![</span><span class="synStatement">CDATA</span><span class="synType">[</span><span class="synConstant">jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=utf-8&amp;connectionCollation=utf8mb4_0900_bin</span><span class="synType">]]&gt;</span><span class="synIdentifier">&lt;/jdbc</span><span class="synComment">.</span><span class="synIdentifier">url&gt;</span> <span class="synIdentifier">&lt;jdbc</span><span class="synComment">.</span><span class="synIdentifier">user&gt;</span>kazuhira<span class="synIdentifier">&lt;/jdbc</span><span class="synComment">.</span><span class="synIdentifier">user&gt;</span> <span class="synIdentifier">&lt;jdbc</span><span class="synComment">.</span><span class="synIdentifier">password&gt;</span>password<span class="synIdentifier">&lt;/jdbc</span><span class="synComment">.</span><span class="synIdentifier">password&gt;</span> <span class="synIdentifier">&lt;/properties&gt;</span> </pre> <p>では、確認してみましょう。先に書いたように、今回は<code>wildfly:run</code>ゴールで起動させます。</p> <pre class="code shell" data-lang="shell" data-unlink>$ mvn wildfly:run ## 以下でも可 $ mvn compile wildfly:dev</pre> <p><code>package</code>でプロビジョニングした<a class="keyword" href="https://d.hatena.ne.jp/keyword/WildFly">WildFly</a>を起動してもいいのですが、その場合は<a class="keyword" href="https://d.hatena.ne.jp/keyword/MySQL">MySQL</a>への接続設定をまた<a class="keyword" href="https://d.hatena.ne.jp/keyword/%B4%C4%B6%AD%CA%D1%BF%F4">環境変数</a>で行うことになるので<br/> ちょっと面倒です。</p> <p><code>wildfly:run</code>や<code>wildfly:dev</code>だと今回の設定の<code>pom.xml</code>に書いた<a class="keyword" href="https://d.hatena.ne.jp/keyword/%B4%C4%B6%AD%CA%D1%BF%F4">環境変数</a>の値で起動します。</p> <p>簡単に確認。</p> <pre class="code shell" data-lang="shell" data-unlink>$ curl -XPOST -H &#39;Content-Type: application/json&#39; localhost:8080/books -d &#39;{&#34;isbn&#34;: &#34;978-4621303252&#34;, &#34;title&#34;: &#34;Effective Java 第3版&#34;, &#34;price&#34;: 4400, &#34;publishDate&#34;: &#34;2018-10-30&#34;}&#39; $ curl localhost:8080/books/978-4621303252 {&#34;isbn&#34;:&#34;978-4621303252&#34;,&#34;price&#34;:4400,&#34;publishDate&#34;:&#34;2018-10-30&#34;,&#34;title&#34;:&#34;Effective Java 第3版&#34;} $ curl localhost:8080/books [{&#34;isbn&#34;:&#34;978-4621303252&#34;,&#34;price&#34;:4400,&#34;publishDate&#34;:&#34;2018-10-30&#34;,&#34;title&#34;:&#34;Effective Java 第3版&#34;}]</pre> <p>その他のパターンは、テストで確認します。</p> <p>ちなみに、この設定だとDaoを使う度に<a class="keyword" href="https://d.hatena.ne.jp/keyword/SQL">SQL</a>ログが出力されます。</p> <pre class="code shell" data-lang="shell" data-unlink>23:09:22,876 INFO [org.seasar.doma.jdbc.UtilLoggingJdbcLogger] (default task-1) [DOMA2076] SQL LOG : PATH=[META-INF/org/littlewings/wildfly/doma/dao/BookDao/findByIsbn.sql], select isbn, title, price, publish_date from book where isbn = &#39;978-4621303252&#39;</pre> <p>これが気になる場合は、<code>UtilLoggingJdbcLogger</code>のログレベルを下げるとよいでしょう。</p> <pre class="code lang-java" data-lang="java" data-unlink><span class="synPreProc">@ApplicationScoped</span> <span class="synType">public</span> <span class="synType">class</span> DomaConfig <span class="synType">implements</span> Config { <span class="synPreProc">@Resource</span>(name = <span class="synConstant">&quot;java:jboss/datasources/MySqlDs&quot;</span>) <span class="synType">private</span> DataSource dataSource; <span class="synType">private</span> Dialect dialect = <span class="synStatement">new</span> MysqlDialect(); <span class="synType">private</span> JdbcLogger jdbcLogger = <span class="synStatement">new</span> UtilLoggingJdbcLogger(Level.FINE); <span class="synPreProc">@Override</span> <span class="synType">public</span> DataSource getDataSource() { <span class="synStatement">return</span> dataSource; } <span class="synPreProc">@Override</span> <span class="synType">public</span> Dialect getDialect() { <span class="synStatement">return</span> dialect; } <span class="synPreProc">@Override</span> <span class="synType">public</span> Naming getNaming() { <span class="synStatement">return</span> Naming.SNAKE_LOWER_CASE; } <span class="synPreProc">@Override</span> <span class="synType">public</span> JdbcLogger getJdbcLogger() { <span class="synStatement">return</span> jdbcLogger; } } </pre> <p>ログレベルがわかりにくかったら、<code>Slf4jJdbcLogger</code>を使ってもいいかもしれません。</p> <p><a href="https://doma.readthedocs.io/ja/3.1.0/slf4j-support/">SLF4J &#x30B5;&#x30DD;&#x30FC;&#x30C8; &mdash; Doma &#x30C9;&#x30AD;&#x30E5;&#x30E1;&#x30F3;&#x30C8;</a></p> <p><code>Slf4jJdbcLogger</code>を使った場合、<a class="keyword" href="https://d.hatena.ne.jp/keyword/Logback">Logback</a>は不要です。どのログライブラリーを使った場合でも、<a class="keyword" href="https://d.hatena.ne.jp/keyword/WildFly">WildFly</a>にデプロイした場合は<br/> <a class="keyword" href="https://d.hatena.ne.jp/keyword/JBoss">JBoss</a> LogManagerにすべて流れるようになっています。</p> <h4 id="Arquillianでインテグレーションテストを書く">Arquillianでインテグレーションテストを書く</h4> <p>最後はテストコードで確認します。Arquillianを使ったインテグレーションテストを書きましょう。</p> <p>テストライブラリーを依存関係に追加。</p> <pre class="code lang-xml" data-lang="xml" data-unlink> <span class="synIdentifier">&lt;dependency&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>org.junit.jupiter<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>junit-jupiter<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;scope&gt;</span>test<span class="synIdentifier">&lt;/scope&gt;</span> <span class="synIdentifier">&lt;/dependency&gt;</span> <span class="synIdentifier">&lt;dependency&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>org.assertj<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>assertj-core<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>3.26.3<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;scope&gt;</span>test<span class="synIdentifier">&lt;/scope&gt;</span> <span class="synIdentifier">&lt;/dependency&gt;</span> <span class="synIdentifier">&lt;dependency&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>io.rest-assured<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>rest-assured<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>5.5.0<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;scope&gt;</span>test<span class="synIdentifier">&lt;/scope&gt;</span> <span class="synIdentifier">&lt;/dependency&gt;</span> <span class="synIdentifier">&lt;dependency&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>com.fasterxml.jackson.core<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>jackson-databind<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>2.18.1<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;scope&gt;</span>test<span class="synIdentifier">&lt;/scope&gt;</span> <span class="synIdentifier">&lt;/dependency&gt;</span> <span class="synIdentifier">&lt;dependency&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>com.fasterxml.jackson.datatype<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>jackson-datatype-jsr310<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>2.18.1<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;scope&gt;</span>test<span class="synIdentifier">&lt;/scope&gt;</span> <span class="synIdentifier">&lt;/dependency&gt;</span> <span class="synIdentifier">&lt;dependency&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>com.mysql<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>mysql-connector-j<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>9.1.0<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;scope&gt;</span>test<span class="synIdentifier">&lt;/scope&gt;</span> <span class="synIdentifier">&lt;/dependency&gt;</span> <span class="synIdentifier">&lt;dependency&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>org.jboss.arquillian.junit5<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>arquillian-junit5-container<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;scope&gt;</span>test<span class="synIdentifier">&lt;/scope&gt;</span> <span class="synIdentifier">&lt;/dependency&gt;</span> <span class="synIdentifier">&lt;dependency&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>org.wildfly.arquillian<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>wildfly-arquillian-container-remote<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;scope&gt;</span>test<span class="synIdentifier">&lt;/scope&gt;</span> <span class="synIdentifier">&lt;/dependency&gt;</span> <span class="synIdentifier">&lt;dependency&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>org.jboss.shrinkwrap.resolver<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>shrinkwrap-resolver-depchain<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>3.3.2<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;scope&gt;</span>test<span class="synIdentifier">&lt;/scope&gt;</span> <span class="synIdentifier">&lt;type&gt;</span>pom<span class="synIdentifier">&lt;/type&gt;</span> <span class="synIdentifier">&lt;/dependency&gt;</span> </pre> <p>ArquillianはRemoteで使います。<a class="keyword" href="https://d.hatena.ne.jp/keyword/MySQL">MySQL</a>のConnector/Jがあらためて依存関係に入っているのは、テストコードで事前にデータの削除を<br/> 行うためです。</p> <p>またArquillianでデプロイする<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A2%A1%BC%A5%AB%A5%A4%A5%D6">アーカイブ</a>に<a class="keyword" href="https://d.hatena.ne.jp/keyword/Doma">Doma</a>を含めるためShrinkwrap Resolverも使います。</p> <p><a href="https://github.com/shrinkwrap/resolver">GitHub - shrinkwrap/resolver: ShrinkWrap Resolvers</a></p> <p><a class="keyword" href="https://d.hatena.ne.jp/keyword/Maven">Maven</a><a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%D7%A5%E9%A5%B0%A5%A4%A5%F3">プラグイン</a>の設定は、こうなりました。</p> <pre class="code lang-xml" data-lang="xml" data-unlink> <span class="synIdentifier">&lt;plugin&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>org.apache.maven.plugins<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>maven-failsafe-plugin<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>3.5.2<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;executions&gt;</span> <span class="synIdentifier">&lt;execution&gt;</span> <span class="synIdentifier">&lt;goals&gt;</span> <span class="synIdentifier">&lt;goal&gt;</span>integration-test<span class="synIdentifier">&lt;/goal&gt;</span> <span class="synIdentifier">&lt;goal&gt;</span>verify<span class="synIdentifier">&lt;/goal&gt;</span> <span class="synIdentifier">&lt;/goals&gt;</span> <span class="synIdentifier">&lt;/execution&gt;</span> <span class="synIdentifier">&lt;/executions&gt;</span> <span class="synIdentifier">&lt;configuration&gt;</span> <span class="synIdentifier">&lt;environmentVariables&gt;</span> <span class="synIdentifier">&lt;MYSQL_URL&gt;</span>${jdbc.url}<span class="synIdentifier">&lt;/MYSQL_URL&gt;</span> <span class="synIdentifier">&lt;MYSQL_USER&gt;</span>${jdbc.user}<span class="synIdentifier">&lt;/MYSQL_USER&gt;</span> <span class="synIdentifier">&lt;MYSQL_PASSWORD&gt;</span>${jdbc.password}<span class="synIdentifier">&lt;/MYSQL_PASSWORD&gt;</span> <span class="synIdentifier">&lt;/environmentVariables&gt;</span> <span class="synIdentifier">&lt;/configuration&gt;</span> <span class="synIdentifier">&lt;/plugin&gt;</span> <span class="synIdentifier">&lt;plugin&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>org.wildfly.plugins<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>wildfly-maven-plugin<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>5.0.1.Final<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;executions&gt;</span> <span class="synIdentifier">&lt;execution&gt;</span> <span class="synIdentifier">&lt;id&gt;</span>package<span class="synIdentifier">&lt;/id&gt;</span> <span class="synIdentifier">&lt;goals&gt;</span> <span class="synIdentifier">&lt;goal&gt;</span>package<span class="synIdentifier">&lt;/goal&gt;</span> <span class="synIdentifier">&lt;/goals&gt;</span> <span class="synIdentifier">&lt;/execution&gt;</span> <span class="synIdentifier">&lt;execution&gt;</span> <span class="synIdentifier">&lt;id&gt;</span>start-before-integration-test<span class="synIdentifier">&lt;/id&gt;</span> <span class="synIdentifier">&lt;phase&gt;</span>pre-integration-test<span class="synIdentifier">&lt;/phase&gt;</span> <span class="synIdentifier">&lt;goals&gt;</span> <span class="synIdentifier">&lt;goal&gt;</span>start<span class="synIdentifier">&lt;/goal&gt;</span> <span class="synIdentifier">&lt;/goals&gt;</span> <span class="synIdentifier">&lt;/execution&gt;</span> <span class="synIdentifier">&lt;execution&gt;</span> <span class="synIdentifier">&lt;id&gt;</span>shutdown-after-integration-test<span class="synIdentifier">&lt;/id&gt;</span> <span class="synIdentifier">&lt;phase&gt;</span>post-integration-test<span class="synIdentifier">&lt;/phase&gt;</span> <span class="synIdentifier">&lt;goals&gt;</span> <span class="synIdentifier">&lt;goal&gt;</span>shutdown<span class="synIdentifier">&lt;/goal&gt;</span> <span class="synIdentifier">&lt;/goals&gt;</span> <span class="synIdentifier">&lt;/execution&gt;</span> <span class="synIdentifier">&lt;/executions&gt;</span> <span class="synIdentifier">&lt;configuration&gt;</span> <span class="synIdentifier">&lt;overwrite-provisioned-server&gt;</span>true<span class="synIdentifier">&lt;/overwrite-provisioned-server&gt;</span> <span class="synIdentifier">&lt;discover-provisioning-info&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>34.0.1.Final<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;add-ons&gt;</span> <span class="synIdentifier">&lt;add-on&gt;</span>mysql<span class="synIdentifier">&lt;/add-on&gt;</span> <span class="synIdentifier">&lt;/add-ons&gt;</span> <span class="synIdentifier">&lt;/discover-provisioning-info&gt;</span> <span class="synIdentifier">&lt;env&gt;</span> <span class="synIdentifier">&lt;MYSQL_DATASOURCE&gt;</span>MySqlDs<span class="synIdentifier">&lt;/MYSQL_DATASOURCE&gt;</span> <span class="synIdentifier">&lt;MYSQL_URL&gt;</span>${jdbc.url}<span class="synIdentifier">&lt;/MYSQL_URL&gt;</span> <span class="synIdentifier">&lt;MYSQL_USER&gt;</span>${jdbc.user}<span class="synIdentifier">&lt;/MYSQL_USER&gt;</span> <span class="synIdentifier">&lt;MYSQL_PASSWORD&gt;</span>${jdbc.password}<span class="synIdentifier">&lt;/MYSQL_PASSWORD&gt;</span> <span class="synIdentifier">&lt;/env&gt;</span> <span class="synIdentifier">&lt;/configuration&gt;</span> <span class="synIdentifier">&lt;/plugin&gt;</span> </pre> <p><a class="keyword" href="https://d.hatena.ne.jp/keyword/Maven">Maven</a> Failsafe Pluginを追加してインテグレーションテストが実行できるようにしているのと、<a class="keyword" href="https://d.hatena.ne.jp/keyword/%B4%C4%B6%AD%CA%D1%BF%F4">環境変数</a>を設定してクライアント側の<br/> テストコードから<a class="keyword" href="https://d.hatena.ne.jp/keyword/JDBC">JDBC</a>接続情報を参照できるようにしています。</p> <pre class="code lang-xml" data-lang="xml" data-unlink> <span class="synIdentifier">&lt;plugin&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>org.apache.maven.plugins<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>maven-failsafe-plugin<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>3.5.2<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;executions&gt;</span> <span class="synIdentifier">&lt;execution&gt;</span> <span class="synIdentifier">&lt;goals&gt;</span> <span class="synIdentifier">&lt;goal&gt;</span>integration-test<span class="synIdentifier">&lt;/goal&gt;</span> <span class="synIdentifier">&lt;goal&gt;</span>verify<span class="synIdentifier">&lt;/goal&gt;</span> <span class="synIdentifier">&lt;/goals&gt;</span> <span class="synIdentifier">&lt;/execution&gt;</span> <span class="synIdentifier">&lt;/executions&gt;</span> <span class="synIdentifier">&lt;configuration&gt;</span> <span class="synIdentifier">&lt;environmentVariables&gt;</span> <span class="synIdentifier">&lt;MYSQL_URL&gt;</span>${jdbc.url}<span class="synIdentifier">&lt;/MYSQL_URL&gt;</span> <span class="synIdentifier">&lt;MYSQL_USER&gt;</span>${jdbc.user}<span class="synIdentifier">&lt;/MYSQL_USER&gt;</span> <span class="synIdentifier">&lt;MYSQL_PASSWORD&gt;</span>${jdbc.password}<span class="synIdentifier">&lt;/MYSQL_PASSWORD&gt;</span> <span class="synIdentifier">&lt;/environmentVariables&gt;</span> <span class="synIdentifier">&lt;/configuration&gt;</span> <span class="synIdentifier">&lt;/plugin&gt;</span> </pre> <p><a class="keyword" href="https://d.hatena.ne.jp/keyword/WildFly">WildFly</a> <a class="keyword" href="https://d.hatena.ne.jp/keyword/Maven">Maven</a> Pluginではインテグレーションテストの前後で<a class="keyword" href="https://d.hatena.ne.jp/keyword/WildFly">WildFly</a>の起動と停止を行うようにしました。</p> <pre class="code lang-xml" data-lang="xml" data-unlink> <span class="synIdentifier">&lt;plugin&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>org.wildfly.plugins<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>wildfly-maven-plugin<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>5.0.1.Final<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;executions&gt;</span> <span class="synIdentifier">&lt;execution&gt;</span> <span class="synIdentifier">&lt;id&gt;</span>package<span class="synIdentifier">&lt;/id&gt;</span> <span class="synIdentifier">&lt;goals&gt;</span> <span class="synIdentifier">&lt;goal&gt;</span>package<span class="synIdentifier">&lt;/goal&gt;</span> <span class="synIdentifier">&lt;/goals&gt;</span> <span class="synIdentifier">&lt;/execution&gt;</span> <span class="synIdentifier">&lt;execution&gt;</span> <span class="synIdentifier">&lt;id&gt;</span>start-before-integration-test<span class="synIdentifier">&lt;/id&gt;</span> <span class="synIdentifier">&lt;phase&gt;</span>pre-integration-test<span class="synIdentifier">&lt;/phase&gt;</span> <span class="synIdentifier">&lt;goals&gt;</span> <span class="synIdentifier">&lt;goal&gt;</span>start<span class="synIdentifier">&lt;/goal&gt;</span> <span class="synIdentifier">&lt;/goals&gt;</span> <span class="synIdentifier">&lt;/execution&gt;</span> <span class="synIdentifier">&lt;execution&gt;</span> <span class="synIdentifier">&lt;id&gt;</span>shutdown-after-integration-test<span class="synIdentifier">&lt;/id&gt;</span> <span class="synIdentifier">&lt;phase&gt;</span>post-integration-test<span class="synIdentifier">&lt;/phase&gt;</span> <span class="synIdentifier">&lt;goals&gt;</span> <span class="synIdentifier">&lt;goal&gt;</span>shutdown<span class="synIdentifier">&lt;/goal&gt;</span> <span class="synIdentifier">&lt;/goals&gt;</span> <span class="synIdentifier">&lt;/execution&gt;</span> <span class="synIdentifier">&lt;/executions&gt;</span> <span class="synIdentifier">&lt;configuration&gt;</span> <span class="synIdentifier">&lt;overwrite-provisioned-server&gt;</span>true<span class="synIdentifier">&lt;/overwrite-provisioned-server&gt;</span> <span class="synIdentifier">&lt;discover-provisioning-info&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>34.0.1.Final<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;add-ons&gt;</span> <span class="synIdentifier">&lt;add-on&gt;</span>mysql<span class="synIdentifier">&lt;/add-on&gt;</span> <span class="synIdentifier">&lt;/add-ons&gt;</span> <span class="synIdentifier">&lt;/discover-provisioning-info&gt;</span> <span class="synIdentifier">&lt;env&gt;</span> <span class="synIdentifier">&lt;MYSQL_DATASOURCE&gt;</span>MySqlDs<span class="synIdentifier">&lt;/MYSQL_DATASOURCE&gt;</span> <span class="synIdentifier">&lt;MYSQL_URL&gt;</span>${jdbc.url}<span class="synIdentifier">&lt;/MYSQL_URL&gt;</span> <span class="synIdentifier">&lt;MYSQL_USER&gt;</span>${jdbc.user}<span class="synIdentifier">&lt;/MYSQL_USER&gt;</span> <span class="synIdentifier">&lt;MYSQL_PASSWORD&gt;</span>${jdbc.password}<span class="synIdentifier">&lt;/MYSQL_PASSWORD&gt;</span> <span class="synIdentifier">&lt;/env&gt;</span> <span class="synIdentifier">&lt;/configuration&gt;</span> <span class="synIdentifier">&lt;/plugin&gt;</span> </pre> <p>なお、<code>wildfly:start</code>ゴールではプロジェクトの<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A2%A1%BC%A5%C6%A5%A3%A5%D5%A5%A1%A5%AF%A5%C8">アーティファクト</a>をデプロイしない状態の<a class="keyword" href="https://d.hatena.ne.jp/keyword/WildFly">WildFly</a>が起動します。</p> <p>テストコードはこちら。</p> <p>Daoのテスト。</p> <p><code>src/test/java/org/littlewings/wildfly/doma/BookDaoIT.java</code></p> <pre class="code lang-java" data-lang="java" data-unlink><span class="synPreProc">package</span> org.littlewings.wildfly.doma; <span class="synPreProc">import</span> java.io.File; <span class="synPreProc">import</span> java.io.IOException; <span class="synPreProc">import</span> java.nio.file.Path; <span class="synPreProc">import</span> java.sql.Connection; <span class="synPreProc">import</span> java.sql.PreparedStatement; <span class="synPreProc">import</span> java.sql.SQLException; <span class="synPreProc">import</span> java.time.LocalDate; <span class="synPreProc">import</span> java.util.List; <span class="synPreProc">import</span> javax.sql.DataSource; <span class="synPreProc">import</span> jakarta.annotation.Resource; <span class="synPreProc">import</span> jakarta.inject.Inject; <span class="synPreProc">import</span> org.jboss.arquillian.container.test.api.Deployment; <span class="synPreProc">import</span> org.jboss.arquillian.junit5.ArquillianExtension; <span class="synPreProc">import</span> org.jboss.modules.maven.ArtifactCoordinates; <span class="synPreProc">import</span> org.jboss.modules.maven.MavenResolver; <span class="synPreProc">import</span> org.jboss.shrinkwrap.api.ShrinkWrap; <span class="synPreProc">import</span> org.jboss.shrinkwrap.api.spec.WebArchive; <span class="synPreProc">import</span> org.jboss.shrinkwrap.resolver.api.maven.Maven; <span class="synPreProc">import</span> org.junit.jupiter.api.BeforeEach; <span class="synPreProc">import</span> org.junit.jupiter.api.Test; <span class="synPreProc">import</span> org.junit.jupiter.api.extension.ExtendWith; <span class="synPreProc">import</span> org.littlewings.wildfly.doma.dao.BookDao; <span class="synPreProc">import</span> org.littlewings.wildfly.doma.entity.Book; <span class="synPreProc">import static</span> org.assertj.core.api.Assertions.assertThat; <span class="synPreProc">@ExtendWith</span>(ArquillianExtension.<span class="synType">class</span>) <span class="synType">class</span> BookDaoIT { <span class="synPreProc">@Resource</span>(name = <span class="synConstant">&quot;java:jboss/datasources/MySqlDs&quot;</span>) <span class="synType">private</span> DataSource dataSource; <span class="synPreProc">@Inject</span> <span class="synType">private</span> BookDao bookDao; <span class="synPreProc">@Deployment</span> <span class="synType">static</span> WebArchive createDeployment() <span class="synType">throws</span> IOException { File[] compileAndRuntimeScopeDependencyFiles = Maven .resolver() .loadPomFromFile(<span class="synConstant">&quot;pom.xml&quot;</span>) .importCompileAndRuntimeDependencies() .resolve() .withTransitivity() .asFile(); File mainResources = Path.of(<span class="synConstant">&quot;src/main/resources&quot;</span>).toFile(); <span class="synStatement">return</span> ShrinkWrap .create(WebArchive.<span class="synType">class</span>) .addPackages(<span class="synConstant">true</span>, RestApplication.<span class="synType">class</span>.getPackage()) .addAsResource(mainResources, <span class="synConstant">&quot;&quot;</span>) .addAsLibraries(compileAndRuntimeScopeDependencyFiles) .addAsLibraries( MavenResolver .createDefaultResolver() .resolveJarArtifact(ArtifactCoordinates.fromString(<span class="synConstant">&quot;org.assertj:assertj-core:3.26.3&quot;</span>)) ); } <span class="synPreProc">@BeforeEach</span> <span class="synType">void</span> setUp() <span class="synType">throws</span> SQLException { <span class="synStatement">try</span> (Connection connection = dataSource.getConnection(); PreparedStatement ps = connection.prepareStatement(<span class="synConstant">&quot;truncate table book&quot;</span>)) { ps.executeUpdate(); } } <span class="synPreProc">@Test</span> <span class="synType">void</span> test() { Book effectiveJava = <span class="synStatement">new</span> Book(<span class="synConstant">&quot;978-4621303252&quot;</span>, <span class="synConstant">&quot;Effective Java 第3版&quot;</span>, <span class="synConstant">4400</span>, LocalDate.of(<span class="synConstant">2018</span>, <span class="synConstant">10</span>, <span class="synConstant">30</span>)); Book testingJava = <span class="synStatement">new</span> Book(<span class="synConstant">&quot;978-4297144357&quot;</span>, <span class="synConstant">&quot;Javaエンジニアのための ソフトウェアテスト実践入門 ~自動化と生成AIによるモダンなテスト技法~&quot;</span>, <span class="synConstant">3520</span>, LocalDate.of(<span class="synConstant">2024</span>, <span class="synConstant">10</span>, <span class="synConstant">3</span>)); Book gettingStartedJava = <span class="synStatement">new</span> Book(<span class="synConstant">&quot;978-4774189093&quot;</span>, <span class="synConstant">&quot;Java本格入門 ~モダンスタイルによる基礎からオブジェクト指向・実用ライブラリまで&quot;</span>, <span class="synConstant">3278</span>, LocalDate.of(<span class="synConstant">2017</span>, <span class="synConstant">4</span>, <span class="synConstant">18</span>)); <span class="synComment">// 登録</span> <span class="synStatement">for</span> (Book book : List.of(effectiveJava, testingJava, gettingStartedJava)) { bookDao.insert(book); } <span class="synComment">// 全件取得</span> assertThat(bookDao.findAllByPriceAsc()) .hasSize(<span class="synConstant">3</span>) .isEqualTo(List.of(gettingStartedJava, testingJava, effectiveJava)); <span class="synComment">// 1件取得</span> assertThat(bookDao.findByIsbn(effectiveJava.isbn())) .isEqualTo(effectiveJava); <span class="synComment">// 削除</span> bookDao.delete(gettingStartedJava); assertThat(bookDao.findAllByPriceAsc()) .hasSize(<span class="synConstant">2</span>) .isEqualTo(List.of(testingJava, effectiveJava)); <span class="synComment">// 更新</span> Book priceDownEffectiveJava = <span class="synStatement">new</span> Book(<span class="synConstant">&quot;978-4621303252&quot;</span>, <span class="synConstant">&quot;Effective Java 第3版&quot;</span>, <span class="synConstant">3000</span>, LocalDate.of(<span class="synConstant">2018</span>, <span class="synConstant">10</span>, <span class="synConstant">30</span>)); bookDao.update(priceDownEffectiveJava); assertThat(bookDao.findByIsbn(priceDownEffectiveJava.isbn())) .isEqualTo(priceDownEffectiveJava); } } </pre> <p>テストの開始前にデータを削除するため、<code>DataSource</code>をJNDIで取得してインジェクションしています。</p> <pre class="code lang-java" data-lang="java" data-unlink> <span class="synPreProc">@Resource</span>(name = <span class="synConstant">&quot;java:jboss/datasources/MySqlDs&quot;</span>) <span class="synType">private</span> DataSource dataSource; <span class="synPreProc">@BeforeEach</span> <span class="synType">void</span> setUp() <span class="synType">throws</span> SQLException { <span class="synStatement">try</span> (Connection connection = dataSource.getConnection(); PreparedStatement ps = connection.prepareStatement(<span class="synConstant">&quot;truncate table book&quot;</span>)) { ps.executeUpdate(); } } </pre> <p>デプロイする<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A2%A1%BC%A5%AB%A5%A4%A5%D6">アーカイブ</a>は<a class="keyword" href="https://d.hatena.ne.jp/keyword/Doma">Doma</a>があるので<code>compile</code>および<code>runtime</code>スコープの依存関係を含みつつ、<code>src/main/resources</code>配下の<br/> リソース一式も含めます。最後にテストで使うAssertJも含めておきます。</p> <pre class="code lang-xml" data-lang="xml" data-unlink> @Deployment static WebArchive createDeployment() throws IOException { File[] compileAndRuntimeScopeDependencyFiles = Maven .resolver() .loadPomFromFile(&quot;pom.xml&quot;) .importCompileAndRuntimeDependencies() .resolve() .withTransitivity() .asFile(); File mainResources = Path.of(&quot;src/main/resources&quot;).toFile(); return ShrinkWrap .create(WebArchive.class) .addPackages(true, RestApplication.class.getPackage()) .addAsResource(mainResources, &quot;&quot;) .addAsLibraries(compileAndRuntimeScopeDependencyFiles) .addAsLibraries( MavenResolver .createDefaultResolver() .resolveJarArtifact(ArtifactCoordinates.fromString(&quot;org.assertj:assertj-core:3.26.3&quot;)) ); } </pre> <p>ここでのポイントはこのあたりですね。</p> <p>続いて<a class="keyword" href="https://d.hatena.ne.jp/keyword/JAX-RS">JAX-RS</a>リソースクラスのテストコード。こちらはクライアントとして動作します。</p> <p><code>src/test/java/org/littlewings/wildfly/doma/BooksResourceIT.java</code></p> <pre class="code lang-java" data-lang="java" data-unlink><span class="synPreProc">package</span> org.littlewings.wildfly.doma; <span class="synPreProc">import</span> java.io.File; <span class="synPreProc">import</span> java.net.URL; <span class="synPreProc">import</span> java.nio.file.Path; <span class="synPreProc">import</span> java.sql.Connection; <span class="synPreProc">import</span> java.sql.DriverManager; <span class="synPreProc">import</span> java.sql.PreparedStatement; <span class="synPreProc">import</span> java.sql.SQLException; <span class="synPreProc">import</span> java.time.LocalDate; <span class="synPreProc">import</span> java.util.List; <span class="synPreProc">import</span> com.fasterxml.jackson.databind.ObjectMapper; <span class="synPreProc">import</span> com.fasterxml.jackson.databind.SerializationFeature; <span class="synPreProc">import</span> com.fasterxml.jackson.databind.util.StdDateFormat; <span class="synPreProc">import</span> io.restassured.RestAssured; <span class="synPreProc">import</span> io.restassured.common.mapper.TypeRef; <span class="synPreProc">import</span> io.restassured.config.ObjectMapperConfig; <span class="synPreProc">import</span> jakarta.ws.rs.ApplicationPath; <span class="synPreProc">import</span> jakarta.ws.rs.core.HttpHeaders; <span class="synPreProc">import</span> jakarta.ws.rs.core.MediaType; <span class="synPreProc">import</span> jakarta.ws.rs.core.Response; <span class="synPreProc">import</span> org.jboss.arquillian.container.test.api.Deployment; <span class="synPreProc">import</span> org.jboss.arquillian.container.test.api.RunAsClient; <span class="synPreProc">import</span> org.jboss.arquillian.junit5.ArquillianExtension; <span class="synPreProc">import</span> org.jboss.arquillian.test.api.ArquillianResource; <span class="synPreProc">import</span> org.jboss.shrinkwrap.api.ShrinkWrap; <span class="synPreProc">import</span> org.jboss.shrinkwrap.api.spec.WebArchive; <span class="synPreProc">import</span> org.jboss.shrinkwrap.resolver.api.maven.Maven; <span class="synPreProc">import</span> org.junit.jupiter.api.BeforeEach; <span class="synPreProc">import</span> org.junit.jupiter.api.Test; <span class="synPreProc">import</span> org.junit.jupiter.api.extension.ExtendWith; <span class="synPreProc">import</span> org.littlewings.wildfly.doma.entity.Book; <span class="synPreProc">import static</span> io.restassured.RestAssured.given; <span class="synPreProc">import static</span> org.assertj.core.api.Assertions.assertThat; <span class="synPreProc">@ExtendWith</span>(ArquillianExtension.<span class="synType">class</span>) <span class="synPreProc">@RunAsClient</span> <span class="synType">class</span> BooksResourceIT { <span class="synPreProc">@ArquillianResource</span> <span class="synType">private</span> URL deploymentUrl; <span class="synType">private</span> String resourcePrefix = RestApplication.<span class="synType">class</span> .getAnnotation(ApplicationPath.<span class="synType">class</span>) .value() .replaceFirst(<span class="synConstant">&quot;^/&quot;</span>, <span class="synConstant">&quot;&quot;</span>); <span class="synPreProc">@Deployment</span> <span class="synType">static</span> WebArchive createDeployment() { File[] compileAndRuntimeScopeDependencyFiles = Maven .resolver() .loadPomFromFile(<span class="synConstant">&quot;pom.xml&quot;</span>) .importCompileAndRuntimeDependencies() .resolve() .withTransitivity() .asFile(); File mainResources = Path.of(<span class="synConstant">&quot;src/main/resources&quot;</span>).toFile(); <span class="synStatement">return</span> ShrinkWrap .create(WebArchive.<span class="synType">class</span>) .addPackages(<span class="synConstant">true</span>, RestApplication.<span class="synType">class</span>.getPackage()) .addAsResource(mainResources, <span class="synConstant">&quot;&quot;</span>) .addAsLibraries(compileAndRuntimeScopeDependencyFiles); } <span class="synPreProc">@BeforeEach</span> <span class="synType">void</span> setUp() <span class="synType">throws</span> SQLException { RestAssured.baseURI = deploymentUrl + resourcePrefix; RestAssured.config = RestAssured.config().objectMapperConfig(ObjectMapperConfig.objectMapperConfig().jackson2ObjectMapperFactory( (cls, charset) -&gt; <span class="synStatement">new</span> ObjectMapper() .findAndRegisterModules() .setDateFormat(<span class="synStatement">new</span> StdDateFormat()) .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, <span class="synConstant">false</span>) )); <span class="synStatement">try</span> (Connection connection = DriverManager.getConnection(System.getenv(<span class="synConstant">&quot;MYSQL_URL&quot;</span>), System.getenv(<span class="synConstant">&quot;MYSQL_USER&quot;</span>), System.getenv(<span class="synConstant">&quot;MYSQL_PASSWORD&quot;</span>)); PreparedStatement ps = connection.prepareStatement(<span class="synConstant">&quot;truncate table book&quot;</span>)) { ps.executeUpdate(); } } <span class="synPreProc">@Test</span> <span class="synType">void</span> test() { Book effectiveJava = <span class="synStatement">new</span> Book(<span class="synConstant">&quot;978-4621303252&quot;</span>, <span class="synConstant">&quot;Effective Java 第3版&quot;</span>, <span class="synConstant">4400</span>, LocalDate.of(<span class="synConstant">2018</span>, <span class="synConstant">10</span>, <span class="synConstant">30</span>)); Book testingJava = <span class="synStatement">new</span> Book(<span class="synConstant">&quot;978-4297144357&quot;</span>, <span class="synConstant">&quot;Javaエンジニアのための ソフトウェアテスト実践入門 ~自動化と生成AIによるモダンなテスト技法~&quot;</span>, <span class="synConstant">3520</span>, LocalDate.of(<span class="synConstant">2024</span>, <span class="synConstant">10</span>, <span class="synConstant">3</span>)); Book gettingStartedJava = <span class="synStatement">new</span> Book(<span class="synConstant">&quot;978-4774189093&quot;</span>, <span class="synConstant">&quot;Java本格入門 ~モダンスタイルによる基礎からオブジェクト指向・実用ライブラリまで&quot;</span>, <span class="synConstant">3278</span>, LocalDate.of(<span class="synConstant">2017</span>, <span class="synConstant">4</span>, <span class="synConstant">18</span>)); <span class="synComment">// 登録</span> <span class="synStatement">for</span> (Book book : List.of(effectiveJava, testingJava, gettingStartedJava)) { given() .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) .body(book) .when() .post(<span class="synConstant">&quot;/books&quot;</span>) .then() .statusCode(Response.Status.NO_CONTENT.getStatusCode()); } <span class="synComment">// 全件取得</span> List&lt;Book&gt; allBooks = given() .when() .get(<span class="synConstant">&quot;/books&quot;</span>) .then() .statusCode(Response.Status.OK.getStatusCode()) .contentType(MediaType.APPLICATION_JSON) .extract() .as(<span class="synStatement">new</span> TypeRef&lt;&gt;() { }); assertThat(allBooks) .isEqualTo(List.of(gettingStartedJava, testingJava, effectiveJava)); <span class="synComment">// 1件取得</span> Book foundBook = given() .pathParams(<span class="synConstant">&quot;isbn&quot;</span>, effectiveJava.isbn()) .when() .get(<span class="synConstant">&quot;/books/{isbn}&quot;</span>) .then() .statusCode(Response.Status.OK.getStatusCode()) .contentType(MediaType.APPLICATION_JSON) .extract() .as(Book.<span class="synType">class</span>); assertThat(foundBook).isEqualTo(effectiveJava); <span class="synComment">// 削除</span> given() .pathParams(<span class="synConstant">&quot;isbn&quot;</span>, gettingStartedJava.isbn()) .when() .delete(<span class="synConstant">&quot;/books/{isbn}&quot;</span>) .then() .statusCode(Response.Status.NO_CONTENT.getStatusCode()); <span class="synComment">// 全件取得</span> List&lt;Book&gt; allBooks2 = given() .when() .get(<span class="synConstant">&quot;/books&quot;</span>) .then() .statusCode(Response.Status.OK.getStatusCode()) .contentType(MediaType.APPLICATION_JSON) .extract() .as(<span class="synStatement">new</span> TypeRef&lt;&gt;() { }); assertThat(allBooks2) .isEqualTo(List.of(testingJava, effectiveJava)); <span class="synComment">// 更新</span> Book priceDownEffectiveJava = <span class="synStatement">new</span> Book(<span class="synConstant">&quot;978-4621303252&quot;</span>, <span class="synConstant">&quot;Effective Java 第3版&quot;</span>, <span class="synConstant">3000</span>, LocalDate.of(<span class="synConstant">2018</span>, <span class="synConstant">10</span>, <span class="synConstant">30</span>)); given() .pathParams(<span class="synConstant">&quot;isbn&quot;</span>, priceDownEffectiveJava.isbn()) .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON) .body(priceDownEffectiveJava) .when() .put(<span class="synConstant">&quot;/books/{isbn}&quot;</span>) .then() .statusCode(Response.Status.NO_CONTENT.getStatusCode()); <span class="synComment">// 1件取得</span> Book foundBook2 = given() .pathParams(<span class="synConstant">&quot;isbn&quot;</span>, priceDownEffectiveJava.isbn()) .when() .get(<span class="synConstant">&quot;/books/{isbn}&quot;</span>) .then() .statusCode(Response.Status.OK.getStatusCode()) .contentType(MediaType.APPLICATION_JSON) .extract() .as(Book.<span class="synType">class</span>); assertThat(foundBook2).isEqualTo(priceDownEffectiveJava); } } </pre> <p>デプロイする<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A2%A1%BC%A5%AB%A5%A4%A5%D6">アーカイブ</a>は、<code>test</code>スコープのものを含めなくていいので少しだけ単純になっています。</p> <pre class="code lang-java" data-lang="java" data-unlink> <span class="synPreProc">@Deployment</span> <span class="synType">static</span> WebArchive createDeployment() { File[] compileAndRuntimeScopeDependencyFiles = Maven .resolver() .loadPomFromFile(<span class="synConstant">&quot;pom.xml&quot;</span>) .importCompileAndRuntimeDependencies() .resolve() .withTransitivity() .asFile(); File mainResources = Path.of(<span class="synConstant">&quot;src/main/resources&quot;</span>).toFile(); <span class="synStatement">return</span> ShrinkWrap .create(WebArchive.<span class="synType">class</span>) .addPackages(<span class="synConstant">true</span>, RestApplication.<span class="synType">class</span>.getPackage()) .addAsResource(mainResources, <span class="synConstant">&quot;&quot;</span>) .addAsLibraries(compileAndRuntimeScopeDependencyFiles); } </pre> <p>データの削除は、<a class="keyword" href="https://d.hatena.ne.jp/keyword/WildFly">WildFly</a>に登録してある<code>DataSource</code>を使えないので自分で<a class="keyword" href="https://d.hatena.ne.jp/keyword/JDBC">JDBC</a>接続を開いています。</p> <pre class="code lang-java" data-lang="java" data-unlink> <span class="synPreProc">@BeforeEach</span> <span class="synType">void</span> setUp() <span class="synType">throws</span> SQLException { RestAssured.baseURI = deploymentUrl + resourcePrefix; RestAssured.config = RestAssured.config().objectMapperConfig(ObjectMapperConfig.objectMapperConfig().jackson2ObjectMapperFactory( (cls, charset) -&gt; <span class="synStatement">new</span> ObjectMapper() .findAndRegisterModules() .setDateFormat(<span class="synStatement">new</span> StdDateFormat()) .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, <span class="synConstant">false</span>) )); <span class="synStatement">try</span> (Connection connection = DriverManager.getConnection(System.getenv(<span class="synConstant">&quot;MYSQL_URL&quot;</span>), System.getenv(<span class="synConstant">&quot;MYSQL_USER&quot;</span>), System.getenv(<span class="synConstant">&quot;MYSQL_PASSWORD&quot;</span>)); PreparedStatement ps = connection.prepareStatement(<span class="synConstant">&quot;truncate table book&quot;</span>)) { ps.executeUpdate(); } } </pre> <p><a class="keyword" href="https://d.hatena.ne.jp/keyword/Maven">Maven</a> Failsafe Pluginで<a class="keyword" href="https://d.hatena.ne.jp/keyword/%B4%C4%B6%AD%CA%D1%BF%F4">環境変数</a>を定義して、内容はプロパティとして設定したのはこのためですね。</p> <pre class="code lang-xml" data-lang="xml" data-unlink> <span class="synIdentifier">&lt;plugin&gt;</span> <span class="synIdentifier">&lt;groupId&gt;</span>org.apache.maven.plugins<span class="synIdentifier">&lt;/groupId&gt;</span> <span class="synIdentifier">&lt;artifactId&gt;</span>maven-failsafe-plugin<span class="synIdentifier">&lt;/artifactId&gt;</span> <span class="synIdentifier">&lt;version&gt;</span>3.5.2<span class="synIdentifier">&lt;/version&gt;</span> <span class="synIdentifier">&lt;executions&gt;</span> <span class="synIdentifier">&lt;execution&gt;</span> <span class="synIdentifier">&lt;goals&gt;</span> <span class="synIdentifier">&lt;goal&gt;</span>integration-test<span class="synIdentifier">&lt;/goal&gt;</span> <span class="synIdentifier">&lt;goal&gt;</span>verify<span class="synIdentifier">&lt;/goal&gt;</span> <span class="synIdentifier">&lt;/goals&gt;</span> <span class="synIdentifier">&lt;/execution&gt;</span> <span class="synIdentifier">&lt;/executions&gt;</span> <span class="synIdentifier">&lt;configuration&gt;</span> <span class="synIdentifier">&lt;environmentVariables&gt;</span> <span class="synIdentifier">&lt;MYSQL_URL&gt;</span>${jdbc.url}<span class="synIdentifier">&lt;/MYSQL_URL&gt;</span> <span class="synIdentifier">&lt;MYSQL_USER&gt;</span>${jdbc.user}<span class="synIdentifier">&lt;/MYSQL_USER&gt;</span> <span class="synIdentifier">&lt;MYSQL_PASSWORD&gt;</span>${jdbc.password}<span class="synIdentifier">&lt;/MYSQL_PASSWORD&gt;</span> <span class="synIdentifier">&lt;/environmentVariables&gt;</span> <span class="synIdentifier">&lt;/configuration&gt;</span> <span class="synIdentifier">&lt;/plugin&gt;</span> </pre> <p>あとJacksonの<code>ObjectMapper</code>のカスタマイズも行っています。</p> <pre class="code lang-xml" data-lang="xml" data-unlink> RestAssured.config = RestAssured.config().objectMapperConfig(ObjectMapperConfig.objectMapperConfig().jackson2ObjectMapperFactory( (cls, charset) -&gt; new ObjectMapper() .findAndRegisterModules() .setDateFormat(new StdDateFormat()) .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) )); </pre> <p>あとは<code>mvn verify</code>を実行すると、<a class="keyword" href="https://d.hatena.ne.jp/keyword/WildFly">WildFly</a>をプロビジョニングしてインテグレーションテストを行います。</p> <pre class="code shell" data-lang="shell" data-unlink>$ mvn verify ## または $ mvn integration-test</pre> <p>インテグレーションテストの前後には、プロビジョニングした<a class="keyword" href="https://d.hatena.ne.jp/keyword/WildFly">WildFly</a>の起動と停止が行われます。</p> <p>こんなところでしょうか。</p> <h4 id="オマケ">オマケ</h4> <p>インテグレーションテストで毎回<a class="keyword" href="https://d.hatena.ne.jp/keyword/WildFly">WildFly</a>の起動と停止をしていると重たいので、以下で<a class="keyword" href="https://d.hatena.ne.jp/keyword/%A5%A2%A1%BC%A5%C6%A5%A3%A5%D5%A5%A1%A5%AF%A5%C8">アーティファクト</a>をデプロイしていない<a class="keyword" href="https://d.hatena.ne.jp/keyword/WildFly">WildFly</a>を<br/> プロビジョニングして起動していました。</p> <pre class="code shell" data-lang="shell" data-unlink>$ mvn package -DskipTests=true -Dwildfly.package.deployment.skip=true &amp;&amp; mvn wildfly:start</pre> <p>テストを実行する際には、<a class="keyword" href="https://d.hatena.ne.jp/keyword/WildFly">WildFly</a>に関するゴールをスキップして実行しています。</p> <pre class="code shell" data-lang="shell" data-unlink>$ mvn verify -Dwildfly.skip -Dwildfly.package.skip ## または $ mvn integration-test -Dwildfly.skip -Dwildfly.package.skip</pre> <p>こうすると<a class="keyword" href="https://d.hatena.ne.jp/keyword/WildFly">WildFly</a>の起動・停止の分は短縮されるので気軽にテストを実行できるようになります。</p> <p><a class="keyword" href="https://d.hatena.ne.jp/keyword/IDE">IDE</a>上でテストクラスを直接実行しても、<a class="keyword" href="https://d.hatena.ne.jp/keyword/Maven">Maven</a>のライフサイクルに沿って<a class="keyword" href="https://d.hatena.ne.jp/keyword/WildFly">WildFly</a>の起動・停止は行われないので、ちょうどいい感じで<br/> 扱えました。</p> <h4 id="おわりに">おわりに</h4> <p><a class="keyword" href="https://d.hatena.ne.jp/keyword/WildFly">WildFly</a> 34に、<a class="keyword" href="https://d.hatena.ne.jp/keyword/CDI">CDI</a>と組み合わせた<a class="keyword" href="https://d.hatena.ne.jp/keyword/Doma">Doma</a> 3を使ったアプリケーションをデプロイしてみました。</p> <p><a class="keyword" href="https://d.hatena.ne.jp/keyword/Doma">Doma</a>と<a class="keyword" href="https://d.hatena.ne.jp/keyword/CDI">CDI</a>の組み合わせにはそこまで苦労しませんでしたが、Arquillianでテストを書くところにとても時間がかかりました…。</p> <p>いつまで経ってもArquillianには慣れない気がしますが、テストコードとしては書くようにしたいなと思っていたりします。</p> </div> <footer class="entry-footer"> <div class="entry-tags-wrapper"> <div class="entry-tags"> </div> </div> <p class="entry-footer-section track-inview-by-gtm" data-gtm-track-json="{&quot;area&quot;: &quot;finish_reading&quot;}"> <span class="author vcard"><span class="fn" data-load-nickname="1" data-user-name="Kazuhira" >Kazuhira</span></span> <span class="entry-footer-time"><a href="https://kazuhira-r.hatenablog.com/entry/2024/11/25/001157"><time data-relative datetime="2024-11-24T15:11:57Z" title="2024-11-24T15:11:57Z" class="updated">2024-11-25 00:11</time></a></span> <span class=" entry-footer-subscribe " data-test-blog-controlls-subscribe> <a href="https://blog.hatena.ne.jp/Kazuhira/kazuhira-r.hatenablog.com/subscribe?utm_medium=button&amp;utm_campaign=subscribe_blog&amp;utm_source=blogs_entry_footer"> 読者になる </a> </span> </p> <div class="hatena-star-container" data-hatena-star-container data-hatena-star-url="https://kazuhira-r.hatenablog.com/entry/2024/11/25/001157" data-hatena-star-title="WildFly 34にCDIと組み合わせたDoma 3を使ったアプリケーションをデプロイする(Arquillianでのインテグレーションテスト付き)" data-hatena-star-variant="profile-icon" data-hatena-star-profile-url-template="https://blog.hatena.ne.jp/{username}/" ></div> <div class="social-buttons"> <div class="social-button-item"> <a href="https://b.hatena.ne.jp/entry/s/kazuhira-r.hatenablog.com/entry/2024/11/25/001157" class="hatena-bookmark-button" data-hatena-bookmark-url="https://kazuhira-r.hatenablog.com/entry/2024/11/25/001157" data-hatena-bookmark-layout="vertical-balloon" data-hatena-bookmark-lang="ja" title="この記事をはてなブックマークに追加"><img src="https://b.st-hatena.com/images/entry-button/button-only.gif" alt="この記事をはてなブックマークに追加" width="20" height="20" style="border: none;" /></a> </div> <div class="social-button-item"> <div class="fb-share-button" data-layout="box_count" data-href="https://kazuhira-r.hatenablog.com/entry/2024/11/25/001157"></div> </div> <div class="social-button-item"> <a class="entry-share-button entry-share-button-twitter test-share-button-twitter" href="https://x.com/intent/tweet?text=WildFly+34%E3%81%ABCDI%E3%81%A8%E7%B5%84%E3%81%BF%E5%90%88%E3%82%8F%E3%81%9B%E3%81%9FDoma+3%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9F%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E3%81%99%E3%82%8B%EF%BC%88Arquillian%E3%81%A7%E3%81%AE%E3%82%A4%E3%83%B3%E3%83%86%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%86%E3%82%B9%E3%83%88%E4%BB%98%E3%81%8D%EF%BC%89+-+CLOVER%F0%9F%8D%80&amp;url=https%3A%2F%2Fkazuhira-r.hatenablog.com%2Fentry%2F2024%2F11%2F25%2F001157" title="X(Twitter)で投稿する" ></a> </div> </div> <div class="google-afc-image test-google-rectangle-ads"> <div id="google_afc_user_container_0" class="google-afc-user-container google_afc_blocklink2_5 google_afc_boder" data-test-unit="/4374287/blog_user"></div> <a href="http://blog.hatena.ne.jp/guide/pro" class="open-pro-modal" data-guide-pro-modal-ad-url="https://hatena.blog/guide/pro/modal/ad">広告を非表示にする</a> </div> <div class="customized-footer"> <div class="entry-footer-modules" id="entry-footer-secondary-modules"> <div class="hatena-module hatena-module-related-entries" > <!-- Hatena-Epic-has-related-entries-with-elasticsearch:true --> <div class="hatena-module-title"> 関連記事 </div> <div class="hatena-module-body"> <ul class="related-entries hatena-urllist urllist-with-thumbnails"> <li class="urllist-item related-entries-item"> <div class="urllist-item-inner related-entries-item-inner"> <div class="urllist-date-link related-entries-date-link"> <a href="https://kazuhira-r.hatenablog.com/archive/2024/10/19" rel="nofollow"> <time datetime="2024-10-19T07:40:49Z" title="2024年10月19日"> 2024-10-19 </time> </a> </div> <a href="https://kazuhira-r.hatenablog.com/entry/2024/10/19/164049" class="urllist-title-link related-entries-title-link urllist-title related-entries-title">Jakarta ServletのServletContainerInitializerを試す</a> <div class="urllist-entry-body related-entries-entry-body">これは、なにをしたくて書いたもの? ServletContainerInitiali…</div> </div> </li> <li class="urllist-item related-entries-item"> <div class="urllist-item-inner related-entries-item-inner"> <div class="urllist-date-link related-entries-date-link"> <a href="https://kazuhira-r.hatenablog.com/archive/2024/06/01" rel="nofollow"> <time datetime="2024-06-01T12:35:21Z" title="2024年6月1日"> 2024-06-01 </time> </a> </div> <a href="https://kazuhira-r.hatenablog.com/entry/2024/06/01/213521" class="urllist-title-link related-entries-title-link urllist-title related-entries-title">WildFlyのプロビジョニングをWildFly Glow × WildFly Maven Plug…</a> <div class="urllist-entry-body related-entries-entry-body">これは、なにをしたくて書いたもの? WildFly 32.0.0.Finalのリ…</div> </div> </li> <li class="urllist-item related-entries-item"> <div class="urllist-item-inner related-entries-item-inner"> <a class="urllist-image-link related-entries-image-link" href="https://kazuhira-r.hatenablog.com/entry/2023/09/28/003829"> <img alt="WildFlyのMicroProfile Telemetryサブシステムを使って、トレースを試す" src="https://cdn.image.st-hatena.com/image/square/16fbe4eb83cdc214ada1f8c9793622f681278f9f/backend=imagemagick;height=100;version=1;width=100/https%3A%2F%2Fcdn-ak.f.st-hatena.com%2Fimages%2Ffotolife%2FK%2FKazuhira%2F20230928%2F20230928001736.png" class="urllist-image related-entries-image" title="WildFlyのMicroProfile Telemetryサブシステムを使って、トレースを試す" width="100" height="100" loading="lazy"> </a> <div class="urllist-date-link related-entries-date-link"> <a href="https://kazuhira-r.hatenablog.com/archive/2023/09/28" rel="nofollow"> <time datetime="2023-09-27T15:38:29Z" title="2023年9月28日"> 2023-09-28 </time> </a> </div> <a href="https://kazuhira-r.hatenablog.com/entry/2023/09/28/003829" class="urllist-title-link related-entries-title-link urllist-title related-entries-title">WildFlyのMicroProfile Telemetryサブシステムを使って、トレースを試す</a> <div class="urllist-entry-body related-entries-entry-body">これは、なにをしたくて書いたもの? WildFlyでOpenTelemetryを…</div> </div> </li> <li class="urllist-item related-entries-item"> <div class="urllist-item-inner related-entries-item-inner"> <div class="urllist-date-link related-entries-date-link"> <a href="https://kazuhira-r.hatenablog.com/archive/2023/09/18" rel="nofollow"> <time datetime="2023-09-18T14:35:09Z" title="2023年9月18日"> 2023-09-18 </time> </a> </div> <a href="https://kazuhira-r.hatenablog.com/entry/2023/09/18/233509" class="urllist-title-link related-entries-title-link urllist-title related-entries-title">WildFly Maven Pluginのdevゴールで、Jakarta EEアプリケーションの開発…</a> <div class="urllist-entry-body related-entries-entry-body">これは、なにをしたくて書いたもの? Jakarta EEアプリケーショ…</div> </div> </li> <li class="urllist-item related-entries-item"> <div class="urllist-item-inner related-entries-item-inner"> <a class="urllist-image-link related-entries-image-link" href="https://kazuhira-r.hatenablog.com/entry/2022/02/11/005344"> <img alt="OpenTelemetry/Jaeger/JAX-RS/MySQLで、Distributed Tracing" src="https://cdn.image.st-hatena.com/image/square/527325a97d6b96d45721cd3448fefb79a69865f0/backend=imagemagick;height=100;version=1;width=100/https%3A%2F%2Fcdn-ak.f.st-hatena.com%2Fimages%2Ffotolife%2FK%2FKazuhira%2F20220208%2F20220208002749.png" class="urllist-image related-entries-image" title="OpenTelemetry/Jaeger/JAX-RS/MySQLで、Distributed Tracing" width="100" height="100" loading="lazy"> </a> <div class="urllist-date-link related-entries-date-link"> <a href="https://kazuhira-r.hatenablog.com/archive/2022/02/11" rel="nofollow"> <time datetime="2022-02-10T15:53:44Z" title="2022年2月11日"> 2022-02-11 </time> </a> </div> <a href="https://kazuhira-r.hatenablog.com/entry/2022/02/11/005344" class="urllist-title-link related-entries-title-link urllist-title related-entries-title">OpenTelemetry/Jaeger/JAX-RS/MySQLで、Distributed Tra…</a> <div class="urllist-entry-body related-entries-entry-body">これは、なにをしたくて書いたもの? OpenTelemetryについて、1…</div> </div> </li> </ul> </div> </div> </div> </div> <div class="comment-box js-comment-box"> <ul class="comment js-comment"> <li class="read-more-comments" style="display: none;"><a>もっと読む</a></li> </ul> <a class="leave-comment-title js-leave-comment-title">コメントを書く</a> </div> </footer> </div> </article> <!-- rakuten_ad_target_end --> <!-- google_ad_section_end --> <div class="pager pager-permalink permalink"> <span class="pager-next"> <a href="https://kazuhira-r.hatenablog.com/entry/2024/11/24/201541" rel="next"> Apache Mavenでインテグレーションテスト… <span class="pager-arrow"> &raquo;</span> </a> </span> </div> </div> </div> <aside id="box1"> <div id="box1-inner"> </div> </aside> </div><!-- #wrapper --> <aside id="box2"> <div id="box2-inner"> <div class="hatena-module hatena-module-profile"> <div class="hatena-module-title"> プロフィール </div> <div class="hatena-module-body"> <a href="https://kazuhira-r.hatenablog.com/about" class="profile-icon-link"> <img src="https://cdn.profile-image.st-hatena.com/users/Kazuhira/profile.png?1356877606" alt="id:Kazuhira" class="profile-icon" /> </a> <span class="id"> <a href="https://kazuhira-r.hatenablog.com/about" class="hatena-id-link"><span data-load-nickname="1" data-user-name="Kazuhira">id:Kazuhira</span></a> </span> <div class="profile-description"> <p>Sunday Programmer's Diary</p> </div> <div class="hatena-follow-button-box btn-subscribe js-hatena-follow-button-box" > <a href="#" class="hatena-follow-button js-hatena-follow-button"> <span class="subscribing"> <span class="foreground">読者です</span> <span class="background">読者をやめる</span> </span> <span class="unsubscribing" data-track-name="profile-widget-subscribe-button" data-track-once> <span class="foreground">読者になる</span> <span class="background">読者になる</span> </span> </a> <div class="subscription-count-box js-subscription-count-box"> <i></i> <u></u> <span class="subscription-count js-subscription-count"> </span> </div> </div> <div class="profile-about"> <a href="https://kazuhira-r.hatenablog.com/about">このブログについて</a> </div> </div> </div> <div class="hatena-module hatena-module-search-box"> <div class="hatena-module-title"> 検索 </div> <div class="hatena-module-body"> <form class="search-form" role="search" action="https://kazuhira-r.hatenablog.com/search" method="get"> <input type="text" name="q" class="search-module-input" value="" placeholder="記事を検索" required> <input type="submit" value="検索" class="search-module-button" /> </form> </div> </div> <div class="hatena-module hatena-module-links"> <div class="hatena-module-title"> リンク </div> <div class="hatena-module-body"> <ul class="hatena-urllist"> <li> <a href="https://hatena.blog/">はてなブログ</a> </li> <li> <a href="https://hatena.blog/guide?via=200109">ブログをはじめる</a> </li> <li> <a href="http://blog.hatenablog.com">週刊はてなブログ</a> </li> <li> <a href="https://hatena.blog/guide/pro">はてなブログPro</a> </li> </ul> </div> </div> <div class="hatena-module hatena-module-archive" data-archive-type="default" data-archive-url="https://kazuhira-r.hatenablog.com/archive"> <div class="hatena-module-title"> <a href="https://kazuhira-r.hatenablog.com/archive">月別アーカイブ</a> </div> <div class="hatena-module-body"> <ul class="hatena-urllist"> <li class="archive-module-year archive-module-year-hidden" data-year="2024"> <div class="archive-module-button"> <span class="archive-module-hide-button">▼</span> <span class="archive-module-show-button">▶</span> </div> <a href="https://kazuhira-r.hatenablog.com/archive/2024" class="archive-module-year-title archive-module-year-2024"> 2024 </a> <ul class="archive-module-months"> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2024/11" class="archive-module-month-title archive-module-month-2024-11"> 2024 / 11 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2024/10" class="archive-module-month-title archive-module-month-2024-10"> 2024 / 10 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2024/09" class="archive-module-month-title archive-module-month-2024-9"> 2024 / 9 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2024/08" class="archive-module-month-title archive-module-month-2024-8"> 2024 / 8 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2024/07" class="archive-module-month-title archive-module-month-2024-7"> 2024 / 7 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2024/06" class="archive-module-month-title archive-module-month-2024-6"> 2024 / 6 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2024/05" class="archive-module-month-title archive-module-month-2024-5"> 2024 / 5 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2024/04" class="archive-module-month-title archive-module-month-2024-4"> 2024 / 4 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2024/03" class="archive-module-month-title archive-module-month-2024-3"> 2024 / 3 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2024/02" class="archive-module-month-title archive-module-month-2024-2"> 2024 / 2 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2024/01" class="archive-module-month-title archive-module-month-2024-1"> 2024 / 1 </a> </li> </ul> </li> <li class="archive-module-year archive-module-year-hidden" data-year="2023"> <div class="archive-module-button"> <span class="archive-module-hide-button">▼</span> <span class="archive-module-show-button">▶</span> </div> <a href="https://kazuhira-r.hatenablog.com/archive/2023" class="archive-module-year-title archive-module-year-2023"> 2023 </a> <ul class="archive-module-months"> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2023/12" class="archive-module-month-title archive-module-month-2023-12"> 2023 / 12 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2023/11" class="archive-module-month-title archive-module-month-2023-11"> 2023 / 11 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2023/10" class="archive-module-month-title archive-module-month-2023-10"> 2023 / 10 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2023/09" class="archive-module-month-title archive-module-month-2023-9"> 2023 / 9 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2023/08" class="archive-module-month-title archive-module-month-2023-8"> 2023 / 8 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2023/07" class="archive-module-month-title archive-module-month-2023-7"> 2023 / 7 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2023/06" class="archive-module-month-title archive-module-month-2023-6"> 2023 / 6 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2023/05" class="archive-module-month-title archive-module-month-2023-5"> 2023 / 5 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2023/04" class="archive-module-month-title archive-module-month-2023-4"> 2023 / 4 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2023/03" class="archive-module-month-title archive-module-month-2023-3"> 2023 / 3 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2023/02" class="archive-module-month-title archive-module-month-2023-2"> 2023 / 2 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2023/01" class="archive-module-month-title archive-module-month-2023-1"> 2023 / 1 </a> </li> </ul> </li> <li class="archive-module-year archive-module-year-hidden" data-year="2022"> <div class="archive-module-button"> <span class="archive-module-hide-button">▼</span> <span class="archive-module-show-button">▶</span> </div> <a href="https://kazuhira-r.hatenablog.com/archive/2022" class="archive-module-year-title archive-module-year-2022"> 2022 </a> <ul class="archive-module-months"> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2022/12" class="archive-module-month-title archive-module-month-2022-12"> 2022 / 12 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2022/11" class="archive-module-month-title archive-module-month-2022-11"> 2022 / 11 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2022/10" class="archive-module-month-title archive-module-month-2022-10"> 2022 / 10 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2022/09" class="archive-module-month-title archive-module-month-2022-9"> 2022 / 9 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2022/08" class="archive-module-month-title archive-module-month-2022-8"> 2022 / 8 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2022/07" class="archive-module-month-title archive-module-month-2022-7"> 2022 / 7 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2022/06" class="archive-module-month-title archive-module-month-2022-6"> 2022 / 6 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2022/05" class="archive-module-month-title archive-module-month-2022-5"> 2022 / 5 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2022/04" class="archive-module-month-title archive-module-month-2022-4"> 2022 / 4 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2022/03" class="archive-module-month-title archive-module-month-2022-3"> 2022 / 3 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2022/02" class="archive-module-month-title archive-module-month-2022-2"> 2022 / 2 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2022/01" class="archive-module-month-title archive-module-month-2022-1"> 2022 / 1 </a> </li> </ul> </li> <li class="archive-module-year archive-module-year-hidden" data-year="2021"> <div class="archive-module-button"> <span class="archive-module-hide-button">▼</span> <span class="archive-module-show-button">▶</span> </div> <a href="https://kazuhira-r.hatenablog.com/archive/2021" class="archive-module-year-title archive-module-year-2021"> 2021 </a> <ul class="archive-module-months"> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2021/12" class="archive-module-month-title archive-module-month-2021-12"> 2021 / 12 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2021/11" class="archive-module-month-title archive-module-month-2021-11"> 2021 / 11 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2021/10" class="archive-module-month-title archive-module-month-2021-10"> 2021 / 10 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2021/09" class="archive-module-month-title archive-module-month-2021-9"> 2021 / 9 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2021/08" class="archive-module-month-title archive-module-month-2021-8"> 2021 / 8 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2021/07" class="archive-module-month-title archive-module-month-2021-7"> 2021 / 7 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2021/06" class="archive-module-month-title archive-module-month-2021-6"> 2021 / 6 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2021/05" class="archive-module-month-title archive-module-month-2021-5"> 2021 / 5 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2021/04" class="archive-module-month-title archive-module-month-2021-4"> 2021 / 4 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2021/03" class="archive-module-month-title archive-module-month-2021-3"> 2021 / 3 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2021/02" class="archive-module-month-title archive-module-month-2021-2"> 2021 / 2 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2021/01" class="archive-module-month-title archive-module-month-2021-1"> 2021 / 1 </a> </li> </ul> </li> <li class="archive-module-year archive-module-year-hidden" data-year="2020"> <div class="archive-module-button"> <span class="archive-module-hide-button">▼</span> <span class="archive-module-show-button">▶</span> </div> <a href="https://kazuhira-r.hatenablog.com/archive/2020" class="archive-module-year-title archive-module-year-2020"> 2020 </a> <ul class="archive-module-months"> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2020/12" class="archive-module-month-title archive-module-month-2020-12"> 2020 / 12 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2020/11" class="archive-module-month-title archive-module-month-2020-11"> 2020 / 11 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2020/10" class="archive-module-month-title archive-module-month-2020-10"> 2020 / 10 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2020/09" class="archive-module-month-title archive-module-month-2020-9"> 2020 / 9 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2020/08" class="archive-module-month-title archive-module-month-2020-8"> 2020 / 8 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2020/07" class="archive-module-month-title archive-module-month-2020-7"> 2020 / 7 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2020/06" class="archive-module-month-title archive-module-month-2020-6"> 2020 / 6 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2020/05" class="archive-module-month-title archive-module-month-2020-5"> 2020 / 5 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2020/04" class="archive-module-month-title archive-module-month-2020-4"> 2020 / 4 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2020/03" class="archive-module-month-title archive-module-month-2020-3"> 2020 / 3 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2020/02" class="archive-module-month-title archive-module-month-2020-2"> 2020 / 2 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2020/01" class="archive-module-month-title archive-module-month-2020-1"> 2020 / 1 </a> </li> </ul> </li> <li class="archive-module-year archive-module-year-hidden" data-year="2019"> <div class="archive-module-button"> <span class="archive-module-hide-button">▼</span> <span class="archive-module-show-button">▶</span> </div> <a href="https://kazuhira-r.hatenablog.com/archive/2019" class="archive-module-year-title archive-module-year-2019"> 2019 </a> <ul class="archive-module-months"> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2019/12" class="archive-module-month-title archive-module-month-2019-12"> 2019 / 12 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2019/11" class="archive-module-month-title archive-module-month-2019-11"> 2019 / 11 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2019/10" class="archive-module-month-title archive-module-month-2019-10"> 2019 / 10 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2019/09" class="archive-module-month-title archive-module-month-2019-9"> 2019 / 9 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2019/08" class="archive-module-month-title archive-module-month-2019-8"> 2019 / 8 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2019/07" class="archive-module-month-title archive-module-month-2019-7"> 2019 / 7 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2019/06" class="archive-module-month-title archive-module-month-2019-6"> 2019 / 6 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2019/05" class="archive-module-month-title archive-module-month-2019-5"> 2019 / 5 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2019/04" class="archive-module-month-title archive-module-month-2019-4"> 2019 / 4 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2019/03" class="archive-module-month-title archive-module-month-2019-3"> 2019 / 3 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2019/02" class="archive-module-month-title archive-module-month-2019-2"> 2019 / 2 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2019/01" class="archive-module-month-title archive-module-month-2019-1"> 2019 / 1 </a> </li> </ul> </li> <li class="archive-module-year archive-module-year-hidden" data-year="2018"> <div class="archive-module-button"> <span class="archive-module-hide-button">▼</span> <span class="archive-module-show-button">▶</span> </div> <a href="https://kazuhira-r.hatenablog.com/archive/2018" class="archive-module-year-title archive-module-year-2018"> 2018 </a> <ul class="archive-module-months"> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2018/12" class="archive-module-month-title archive-module-month-2018-12"> 2018 / 12 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2018/11" class="archive-module-month-title archive-module-month-2018-11"> 2018 / 11 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2018/10" class="archive-module-month-title archive-module-month-2018-10"> 2018 / 10 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2018/09" class="archive-module-month-title archive-module-month-2018-9"> 2018 / 9 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2018/08" class="archive-module-month-title archive-module-month-2018-8"> 2018 / 8 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2018/07" class="archive-module-month-title archive-module-month-2018-7"> 2018 / 7 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2018/06" class="archive-module-month-title archive-module-month-2018-6"> 2018 / 6 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2018/05" class="archive-module-month-title archive-module-month-2018-5"> 2018 / 5 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2018/04" class="archive-module-month-title archive-module-month-2018-4"> 2018 / 4 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2018/03" class="archive-module-month-title archive-module-month-2018-3"> 2018 / 3 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2018/02" class="archive-module-month-title archive-module-month-2018-2"> 2018 / 2 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2018/01" class="archive-module-month-title archive-module-month-2018-1"> 2018 / 1 </a> </li> </ul> </li> <li class="archive-module-year archive-module-year-hidden" data-year="2017"> <div class="archive-module-button"> <span class="archive-module-hide-button">▼</span> <span class="archive-module-show-button">▶</span> </div> <a href="https://kazuhira-r.hatenablog.com/archive/2017" class="archive-module-year-title archive-module-year-2017"> 2017 </a> <ul class="archive-module-months"> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2017/12" class="archive-module-month-title archive-module-month-2017-12"> 2017 / 12 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2017/11" class="archive-module-month-title archive-module-month-2017-11"> 2017 / 11 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2017/10" class="archive-module-month-title archive-module-month-2017-10"> 2017 / 10 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2017/09" class="archive-module-month-title archive-module-month-2017-9"> 2017 / 9 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2017/08" class="archive-module-month-title archive-module-month-2017-8"> 2017 / 8 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2017/07" class="archive-module-month-title archive-module-month-2017-7"> 2017 / 7 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2017/06" class="archive-module-month-title archive-module-month-2017-6"> 2017 / 6 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2017/05" class="archive-module-month-title archive-module-month-2017-5"> 2017 / 5 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2017/04" class="archive-module-month-title archive-module-month-2017-4"> 2017 / 4 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2017/03" class="archive-module-month-title archive-module-month-2017-3"> 2017 / 3 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2017/02" class="archive-module-month-title archive-module-month-2017-2"> 2017 / 2 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2017/01" class="archive-module-month-title archive-module-month-2017-1"> 2017 / 1 </a> </li> </ul> </li> <li class="archive-module-year archive-module-year-hidden" data-year="2016"> <div class="archive-module-button"> <span class="archive-module-hide-button">▼</span> <span class="archive-module-show-button">▶</span> </div> <a href="https://kazuhira-r.hatenablog.com/archive/2016" class="archive-module-year-title archive-module-year-2016"> 2016 </a> <ul class="archive-module-months"> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2016/12" class="archive-module-month-title archive-module-month-2016-12"> 2016 / 12 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2016/11" class="archive-module-month-title archive-module-month-2016-11"> 2016 / 11 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2016/10" class="archive-module-month-title archive-module-month-2016-10"> 2016 / 10 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2016/09" class="archive-module-month-title archive-module-month-2016-9"> 2016 / 9 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2016/08" class="archive-module-month-title archive-module-month-2016-8"> 2016 / 8 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2016/07" class="archive-module-month-title archive-module-month-2016-7"> 2016 / 7 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2016/06" class="archive-module-month-title archive-module-month-2016-6"> 2016 / 6 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2016/05" class="archive-module-month-title archive-module-month-2016-5"> 2016 / 5 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2016/04" class="archive-module-month-title archive-module-month-2016-4"> 2016 / 4 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2016/03" class="archive-module-month-title archive-module-month-2016-3"> 2016 / 3 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2016/02" class="archive-module-month-title archive-module-month-2016-2"> 2016 / 2 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2016/01" class="archive-module-month-title archive-module-month-2016-1"> 2016 / 1 </a> </li> </ul> </li> <li class="archive-module-year archive-module-year-hidden" data-year="2015"> <div class="archive-module-button"> <span class="archive-module-hide-button">▼</span> <span class="archive-module-show-button">▶</span> </div> <a href="https://kazuhira-r.hatenablog.com/archive/2015" class="archive-module-year-title archive-module-year-2015"> 2015 </a> <ul class="archive-module-months"> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2015/12" class="archive-module-month-title archive-module-month-2015-12"> 2015 / 12 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2015/11" class="archive-module-month-title archive-module-month-2015-11"> 2015 / 11 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2015/10" class="archive-module-month-title archive-module-month-2015-10"> 2015 / 10 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2015/09" class="archive-module-month-title archive-module-month-2015-9"> 2015 / 9 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2015/08" class="archive-module-month-title archive-module-month-2015-8"> 2015 / 8 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2015/07" class="archive-module-month-title archive-module-month-2015-7"> 2015 / 7 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2015/06" class="archive-module-month-title archive-module-month-2015-6"> 2015 / 6 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2015/05" class="archive-module-month-title archive-module-month-2015-5"> 2015 / 5 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2015/04" class="archive-module-month-title archive-module-month-2015-4"> 2015 / 4 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2015/03" class="archive-module-month-title archive-module-month-2015-3"> 2015 / 3 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2015/02" class="archive-module-month-title archive-module-month-2015-2"> 2015 / 2 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2015/01" class="archive-module-month-title archive-module-month-2015-1"> 2015 / 1 </a> </li> </ul> </li> <li class="archive-module-year archive-module-year-hidden" data-year="2014"> <div class="archive-module-button"> <span class="archive-module-hide-button">▼</span> <span class="archive-module-show-button">▶</span> </div> <a href="https://kazuhira-r.hatenablog.com/archive/2014" class="archive-module-year-title archive-module-year-2014"> 2014 </a> <ul class="archive-module-months"> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2014/12" class="archive-module-month-title archive-module-month-2014-12"> 2014 / 12 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2014/11" class="archive-module-month-title archive-module-month-2014-11"> 2014 / 11 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2014/10" class="archive-module-month-title archive-module-month-2014-10"> 2014 / 10 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2014/09" class="archive-module-month-title archive-module-month-2014-9"> 2014 / 9 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2014/08" class="archive-module-month-title archive-module-month-2014-8"> 2014 / 8 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2014/07" class="archive-module-month-title archive-module-month-2014-7"> 2014 / 7 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2014/06" class="archive-module-month-title archive-module-month-2014-6"> 2014 / 6 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2014/05" class="archive-module-month-title archive-module-month-2014-5"> 2014 / 5 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2014/04" class="archive-module-month-title archive-module-month-2014-4"> 2014 / 4 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2014/03" class="archive-module-month-title archive-module-month-2014-3"> 2014 / 3 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2014/02" class="archive-module-month-title archive-module-month-2014-2"> 2014 / 2 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2014/01" class="archive-module-month-title archive-module-month-2014-1"> 2014 / 1 </a> </li> </ul> </li> <li class="archive-module-year archive-module-year-hidden" data-year="2013"> <div class="archive-module-button"> <span class="archive-module-hide-button">▼</span> <span class="archive-module-show-button">▶</span> </div> <a href="https://kazuhira-r.hatenablog.com/archive/2013" class="archive-module-year-title archive-module-year-2013"> 2013 </a> <ul class="archive-module-months"> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2013/12" class="archive-module-month-title archive-module-month-2013-12"> 2013 / 12 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2013/11" class="archive-module-month-title archive-module-month-2013-11"> 2013 / 11 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2013/10" class="archive-module-month-title archive-module-month-2013-10"> 2013 / 10 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2013/09" class="archive-module-month-title archive-module-month-2013-9"> 2013 / 9 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2013/08" class="archive-module-month-title archive-module-month-2013-8"> 2013 / 8 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2013/07" class="archive-module-month-title archive-module-month-2013-7"> 2013 / 7 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2013/06" class="archive-module-month-title archive-module-month-2013-6"> 2013 / 6 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2013/05" class="archive-module-month-title archive-module-month-2013-5"> 2013 / 5 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2013/04" class="archive-module-month-title archive-module-month-2013-4"> 2013 / 4 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2013/03" class="archive-module-month-title archive-module-month-2013-3"> 2013 / 3 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2013/02" class="archive-module-month-title archive-module-month-2013-2"> 2013 / 2 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2013/01" class="archive-module-month-title archive-module-month-2013-1"> 2013 / 1 </a> </li> </ul> </li> <li class="archive-module-year archive-module-year-hidden" data-year="2012"> <div class="archive-module-button"> <span class="archive-module-hide-button">▼</span> <span class="archive-module-show-button">▶</span> </div> <a href="https://kazuhira-r.hatenablog.com/archive/2012" class="archive-module-year-title archive-module-year-2012"> 2012 </a> <ul class="archive-module-months"> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2012/12" class="archive-module-month-title archive-module-month-2012-12"> 2012 / 12 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2012/11" class="archive-module-month-title archive-module-month-2012-11"> 2012 / 11 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2012/10" class="archive-module-month-title archive-module-month-2012-10"> 2012 / 10 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2012/09" class="archive-module-month-title archive-module-month-2012-9"> 2012 / 9 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2012/08" class="archive-module-month-title archive-module-month-2012-8"> 2012 / 8 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2012/07" class="archive-module-month-title archive-module-month-2012-7"> 2012 / 7 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2012/06" class="archive-module-month-title archive-module-month-2012-6"> 2012 / 6 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2012/05" class="archive-module-month-title archive-module-month-2012-5"> 2012 / 5 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2012/04" class="archive-module-month-title archive-module-month-2012-4"> 2012 / 4 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2012/03" class="archive-module-month-title archive-module-month-2012-3"> 2012 / 3 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2012/02" class="archive-module-month-title archive-module-month-2012-2"> 2012 / 2 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2012/01" class="archive-module-month-title archive-module-month-2012-1"> 2012 / 1 </a> </li> </ul> </li> <li class="archive-module-year archive-module-year-hidden" data-year="2011"> <div class="archive-module-button"> <span class="archive-module-hide-button">▼</span> <span class="archive-module-show-button">▶</span> </div> <a href="https://kazuhira-r.hatenablog.com/archive/2011" class="archive-module-year-title archive-module-year-2011"> 2011 </a> <ul class="archive-module-months"> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2011/12" class="archive-module-month-title archive-module-month-2011-12"> 2011 / 12 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2011/11" class="archive-module-month-title archive-module-month-2011-11"> 2011 / 11 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2011/10" class="archive-module-month-title archive-module-month-2011-10"> 2011 / 10 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2011/09" class="archive-module-month-title archive-module-month-2011-9"> 2011 / 9 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2011/08" class="archive-module-month-title archive-module-month-2011-8"> 2011 / 8 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2011/07" class="archive-module-month-title archive-module-month-2011-7"> 2011 / 7 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2011/06" class="archive-module-month-title archive-module-month-2011-6"> 2011 / 6 </a> </li> <li class="archive-module-month"> <a href="https://kazuhira-r.hatenablog.com/archive/2011/05" class="archive-module-month-title archive-module-month-2011-5"> 2011 / 5 </a> </li> </ul> </li> </ul> </div> </div> </div> </aside> </div> </div> </div> </div> <footer id="footer" data-brand="hatenablog"> <div id="footer-inner"> <div style="display:none !important" class="guest-footer js-guide-register test-blogs-register-guide" data-action="guide-register"> <div class="guest-footer-content"> <h3>はてなブログをはじめよう!</h3> <p>Kazuhiraさんは、はてなブログを使っています。あなたもはてなブログをはじめてみませんか?</p> <div class="guest-footer-btn-container"> <div class="guest-footer-btn"> <a class="btn btn-register js-inherit-ga" href="https://blog.hatena.ne.jp/register?via=200227" target="_blank">はてなブログをはじめる(無料)</a> </div> <div class="guest-footer-btn"> <a href="https://hatena.blog/guide" target="_blank">はてなブログとは</a> </div> </div> </div> </div> <address class="footer-address"> <a href="https://kazuhira-r.hatenablog.com/"> <img src="https://cdn.blog.st-hatena.com/images/admin/blog-icon-noimage.png" width="16" height="16" alt="CLOVER🍀"/> <span class="footer-address-name">CLOVER🍀</span> </a> </address> <p class="services"> Powered by <a href="https://hatena.blog/">Hatena Blog</a> | <a href="https://blog.hatena.ne.jp/-/abuse_report?target_url=https%3A%2F%2Fkazuhira-r.hatenablog.com%2Fentry%2F2024%2F11%2F25%2F001157" class="report-abuse-link test-report-abuse-link" target="_blank">ブログを報告する</a> </p> </div> </footer> <script async src="https://s.hatena.ne.jp/js/widget/star.js"></script> <script> if (typeof window.Hatena === 'undefined') { window.Hatena = {}; } if (!Hatena.hasOwnProperty('Star')) { Hatena.Star = { VERSION: 2, }; } </script> <div id="fb-root"></div> <script>(function(d, s, id) { var js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) return; js = d.createElement(s); js.id = id; js.src = "//connect.facebook.net/ja_JP/sdk.js#xfbml=1&appId=719729204785177&version=v17.0"; fjs.parentNode.insertBefore(js, fjs); }(document, 'script', 'facebook-jssdk'));</script> <div class="quote-box"> <div class="tooltip-quote tooltip-quote-stock"> <i class="blogicon-quote" title="引用をストック"></i> </div> <div class="tooltip-quote tooltip-quote-tweet js-tooltip-quote-tweet"> <a class="js-tweet-quote" target="_blank" data-track-name="quote-tweet" data-track-once> <img src="https://cdn.blog.st-hatena.com/images/admin/quote/quote-x-icon.svg?version=b06a9d4929119667e7027e25c25079" title="引用して投稿する" > </a> </div> </div> <div class="quote-stock-panel" id="quote-stock-message-box" style="position: absolute; z-index: 3000"> <div class="message-box" id="quote-stock-succeeded-message" style="display: none"> <p>引用をストックしました</p> <button class="btn btn-primary" id="quote-stock-show-editor-button" data-track-name="curation-quote-edit-button">ストック一覧を見る</button> <button class="btn quote-stock-close-message-button">閉じる</button> </div> <div class="message-box" id="quote-login-required-message" style="display: none"> <p>引用するにはまずログインしてください</p> <button class="btn btn-primary" id="quote-login-button">ログイン</button> <button class="btn quote-stock-close-message-button">閉じる</button> </div> <div class="error-box" id="quote-stock-failed-message" style="display: none"> <p>引用をストックできませんでした。再度お試しください</p> <button class="btn quote-stock-close-message-button">閉じる</button> </div> <div class="error-box" id="unstockable-quote-message-box" style="display: none; position: absolute; z-index: 3000;"> <p>限定公開記事のため引用できません。</p> </div> </div> <script type="x-underscore-template" id="js-requote-button-template"> <div class="requote-button js-requote-button"> <button class="requote-button-btn tipsy-top" title="引用する"><i class="blogicon-quote"></i></button> </div> </script> <div id="hidden-subscribe-button" style="display: none;"> <div class="hatena-follow-button-box btn-subscribe js-hatena-follow-button-box" > <a href="#" class="hatena-follow-button js-hatena-follow-button"> <span class="subscribing"> <span class="foreground">読者です</span> <span class="background">読者をやめる</span> </span> <span class="unsubscribing" data-track-name="profile-widget-subscribe-button" data-track-once> <span class="foreground">読者になる</span> <span class="background">読者になる</span> </span> </a> <div class="subscription-count-box js-subscription-count-box"> <i></i> <u></u> <span class="subscription-count js-subscription-count"> </span> </div> </div> </div> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> <script src="https://b.st-hatena.com/js/bookmark_button.js" charset="utf-8" async="async"></script> <script type="text/javascript" src="https://cdn.blog.st-hatena.com/js/external/jquery.min.js?v=1.12.4&amp;version=b06a9d4929119667e7027e25c25079"></script> <script src="https://cdn.blog.st-hatena.com/js/texts-ja.js?version=b06a9d4929119667e7027e25c25079"></script> <script id="vendors-js" data-env="production" src="https://cdn.blog.st-hatena.com/js/vendors.js?version=b06a9d4929119667e7027e25c25079" crossorigin="anonymous"></script> <script id="hatenablog-js" data-env="production" src="https://cdn.blog.st-hatena.com/js/hatenablog.js?version=b06a9d4929119667e7027e25c25079" crossorigin="anonymous" data-page-id="entry"></script> <script>Hatena.Diary.GlobalHeader.init()</script> <script id="valve-dmp" data-service="blog" src="https://cdn.pool.st-hatena.com/valve/dmp.js" data-test-id="dmpjs" async></script> </body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10