{"id":780,"date":"2025-03-24T08:53:06","date_gmt":"2025-03-23T23:53:06","guid":{"rendered":"https:\/\/dexall.co.jp\/articles\/?p=780"},"modified":"2025-03-24T08:53:06","modified_gmt":"2025-03-23T23:53:06","slug":"%e3%80%902024%e5%b9%b4%e4%bf%9d%e5%ad%98%e7%89%88%e3%80%91grails%e3%83%95%e3%83%ac%e3%83%bc%e3%83%a0%e3%83%af%e3%83%bc%e3%82%af%e5%ae%8c%e5%85%a8%e3%82%ac%e3%82%a4%e3%83%89%ef%bc%9a%e7%89%b9%e5%be%b4","status":"publish","type":"post","link":"https:\/\/dexall.co.jp\/articles\/?p=780","title":{"rendered":"\u30102024\u5e74\u4fdd\u5b58\u7248\u3011Grails\u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af\u5b8c\u5168\u30ac\u30a4\u30c9\uff1a\u7279\u5fb4\u30fb\u30e1\u30ea\u30c3\u30c8\u30fb\u5b9f\u88c5\u624b\u9806\u3092\u5fb9\u5e95\u89e3\u8aac"},"content":{"rendered":"\n<div class=\"toc\"><br \/>\n<b>Warning<\/b>:  Undefined array key \"is_admin\" in <b>\/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/sango-theme\/library\/gutenberg\/dist\/classes\/Toc.php<\/b> on line <b>116<\/b><br \/>\n<br \/>\n<b>Warning<\/b>:  Undefined array key \"is_category_top\" in <b>\/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/sango-theme\/library\/gutenberg\/dist\/classes\/Toc.php<\/b> on line <b>121<\/b><br \/>\n<br \/>\n<b>Warning<\/b>:  Undefined array key \"is_top\" in <b>\/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/sango-theme\/library\/gutenberg\/dist\/classes\/Toc.php<\/b> on line <b>128<\/b><br \/>\n    <div id=\"toc_container\" class=\"sgb-toc--bullets js-smooth-scroll\" data-dialog-title=\"\u76ee\u6b21\">\n      <p class=\"toc_title\">\u76ee\u6b21 <\/p>\n      <ul class=\"toc_list\">  <li class=\"first\">    <a href=\"#i-0\">Grails\u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af\u3068\u306f<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-1\">Groovy\u30d9\u30fc\u30b9\u306e\u9ad8\u751f\u7523\u6027Web\u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-2\">Convention over Configuration\u306e\u601d\u60f3<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-3\">Grails\u306e\u4e3b\u8981\u306a\u7279\u5fb4\u3068\u6a5f\u80fd<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-4\">\u5f37\u529b\u306aORM\u30b5\u30dd\u30fc\u30c8\uff08GORM\uff09<\/a>      <\/li>      <li>        <a href=\"#i-5\">\u67d4\u8edf\u306a\u30d3\u30e5\u30fc\u5c64\uff08GSP\uff09<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-6\">\u30d7\u30e9\u30b0\u30a4\u30f3\u30a8\u30b3\u30b7\u30b9\u30c6\u30e0<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-7\">Grails\u306e\u30e1\u30ea\u30c3\u30c8\u30fb\u30c7\u30e1\u30ea\u30c3\u30c8<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-8\">\u958b\u767a\u751f\u7523\u6027\u306e\u5927\u5e45\u306a\u5411\u4e0a<\/a>      <\/li>      <li>        <a href=\"#i-9\">Java\u3068\u306e\u89aa\u548c\u6027\u306e\u9ad8\u3055<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-10\">\u5b66\u7fd2\u66f2\u7dda\u3068\u5c0e\u5165\u30b3\u30b9\u30c8<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-11\">Spring Boot\u3068\u306e\u6bd4\u8f03<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-12\">\u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3\u306e\u9055\u3044<\/a>      <\/li>      <li>        <a href=\"#i-13\">\u958b\u767a\u751f\u7523\u6027\u306e\u6bd4\u8f03<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-14\">\u30e6\u30fc\u30b9\u30b1\u30fc\u30b9\u5225\u306e\u9078\u5b9a\u57fa\u6e96<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-15\">Grails\u306e\u74b0\u5883\u69cb\u7bc9\u624b\u9806<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-16\">SDKMAN\u3092\u4f7f\u7528\u3057\u305f\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb<\/a>      <\/li>      <li>        <a href=\"#i-17\">\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u4f5c\u6210\u3068\u8a2d\u5b9a<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-18\">\u958b\u767a\u74b0\u5883\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-19\">\u5b9f\u8df5\u7684\u306aGrails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u958b\u767a<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-20\">\u30c9\u30e1\u30a4\u30f3\u30af\u30e9\u30b9\u306e\u8a2d\u8a08\u3068\u5b9f\u88c5<\/a>      <\/li>      <li>        <a href=\"#i-21\">\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u3068\u30d3\u30e5\u30fc\u306e\u9023\u643a<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-22\">\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u7d71\u5408\u306e\u30dd\u30a4\u30f3\u30c8<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-23\">\u672c\u756a\u74b0\u5883\u3067\u306e\u904b\u7528\u3068\u30c7\u30d7\u30ed\u30a4<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-24\">\u6027\u80fd\u30c1\u30e5\u30fc\u30cb\u30f3\u30b0\u306e\u30dd\u30a4\u30f3\u30c8<\/a>      <\/li>      <li>        <a href=\"#i-25\">\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5bfe\u7b56\u306e\u5b9f\u88c5<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-26\">\u7d99\u7d9a\u7684\u30c7\u30ea\u30d0\u30ea\u30fc\u306e\u69cb\u7bc9<\/a>      <\/li>    <\/ul>  <\/li>  <li class=\"last\">    <a href=\"#i-27\">\u307e\u3068\u3081\uff1aGrails\u63a1\u7528\u306e\u5224\u65ad\u57fa\u6e96<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-28\">\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306b\u9069\u3057\u305f\u6280\u8853\u9078\u5b9a\u306e\u30dd\u30a4\u30f3\u30c8<\/a>      <\/li>      <li>        <a href=\"#i-29\">\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u898f\u6a21\u5225\u306e\u63a1\u7528\u5224\u65ad\u57fa\u6e96<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-30\">\u4eca\u5f8c\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u30a2\u30c3\u30d7\u3068\u5c55\u671b<\/a>      <\/li>    <\/ul>  <\/li><\/ul>\n      <a href=\"#\" class=\"sgb-toc-button js-toc-button\" rel=\"nofollow\" data-open-dialog=\"true\"><i class=\"fa fa-list\"><\/i><span class=\"sgb-toc-button__text\">\u76ee\u6b21\u3078<\/span><\/a>\n    <\/div><\/div><h2 class=\"wp-block-heading\" id=\"i-0\">Grails\u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af\u3068\u306f<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-1\">Groovy\u30d9\u30fc\u30b9\u306e\u9ad8\u751f\u7523\u6027Web\u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af<\/h3>\n\n\n\n<p>Grails\u306f\u3001JVM\uff08Java Virtual Machine\uff09\u4e0a\u3067\u52d5\u4f5c\u3059\u308b\u9ad8\u751f\u7523\u6027Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af\u3067\u3059\u30022006\u5e74\u306b\u767b\u5834\u3057\u3066\u4ee5\u6765\u3001Java\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0\u306b\u304a\u3051\u308bRuby on Rails\u30e9\u30a4\u30af\u306a\u958b\u767a\u4f53\u9a13\u3092\u63d0\u4f9b\u3059\u308b\u3053\u3068\u3092\u76ee\u6307\u3057\u3066\u767a\u5c55\u3057\u3066\u304d\u307e\u3057\u305f\u3002<\/p>\n\n\n\n<p>Grails\u306e\u6700\u5927\u306e\u7279\u5fb4\u306f\u3001\u52d5\u7684\u30d7\u30ed\u30b0\u30e9\u30df\u30f3\u30b0\u8a00\u8a9e\u3067\u3042\u308bGroovy\u3092\u30d9\u30fc\u30b9\u8a00\u8a9e\u3068\u3057\u3066\u63a1\u7528\u3057\u3066\u3044\u308b\u3053\u3068\u3067\u3059\u3002Groovy\u306f\u4ee5\u4e0b\u306e\u7279\u5fb4\u3092\u6301\u3064\u3053\u3068\u3067\u3001Java\u958b\u767a\u8005\u306e\u751f\u7523\u6027\u3092\u5927\u5e45\u306b\u5411\u4e0a\u3055\u305b\u307e\u3059\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Java\u3068\u306e\u5b8c\u5168\u306a\u4e92\u63db\u6027<\/strong>\n<ul class=\"wp-block-list\">\n<li>\u65e2\u5b58\u306eJava\u30e9\u30a4\u30d6\u30e9\u30ea\u3084\u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af\u3092\u76f4\u63a5\u5229\u7528\u53ef\u80fd<\/li>\n\n\n\n<li>Java\u3068Groovy\u306e\u30b3\u30fc\u30c9\u3092\u6df7\u5728\u3055\u305b\u3066\u958b\u767a\u53ef\u80fd<\/li>\n\n\n\n<li>\u65e2\u5b58\u306eJava\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3078\u306e\u6bb5\u968e\u7684\u306a\u5c0e\u5165\u304c\u5bb9\u6613<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>\u30b7\u30f3\u30d7\u30eb\u306a\u6587\u6cd5<\/strong><\/li>\n<\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  \/\/ Java\u306e\u5834\u5408\n  public class Person {\n      private String name;\n      private int age;\n\n      public String getName() { return name; }\n      public void setName(String name) { this.name = name; }\n      public int getAge() { return age; }\n      public void setAge(int age) { this.age = age; }\n  }\n\n  \/\/ Groovy\u306e\u5834\u5408\n  class Person {\n      String name\n      int age\n  }<\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u52d5\u7684\u8a00\u8a9e\u306e\u5229\u70b9<\/strong>\n<ul class=\"wp-block-list\">\n<li>\u578b\u63a8\u8ad6\u306b\u3088\u308b\u30b3\u30fc\u30c9\u91cf\u306e\u524a\u6e1b<\/li>\n\n\n\n<li>\u30e1\u30bf\u30d7\u30ed\u30b0\u30e9\u30df\u30f3\u30b0\u6a5f\u80fd<\/li>\n\n\n\n<li>\u30af\u30ed\u30fc\u30b8\u30e3\u306e\u30b5\u30dd\u30fc\u30c8<\/li>\n\n\n\n<li>DSL\uff08\u30c9\u30e1\u30a4\u30f3\u7279\u5316\u8a00\u8a9e\uff09\u306e\u4f5c\u6210\u304c\u5bb9\u6613<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-2\">Convention over Configuration\u306e\u601d\u60f3<\/h3>\n\n\n\n<p>Grails\u306f\u300c\u8a2d\u5b9a\u3088\u308a\u898f\u7d04\u300d\uff08Convention over Configuration\uff09\u3068\u3044\u3046\u8a2d\u8a08\u601d\u60f3\u3092\u63a1\u7528\u3057\u3066\u3044\u307e\u3059\u3002\u3053\u308c\u306b\u3088\u308a\u3001\u958b\u767a\u8005\u306f\u6700\u5c0f\u9650\u306e\u8a2d\u5b9a\u3067\u958b\u767a\u3092\u958b\u59cb\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<p class=\"is-style-sango-paragraph-memo-alt\">\u4e3b\u306a\u898f\u7d04\u306e\u4f8b\uff1a<\/p>\n\n\n<div id=\"id-3014baeb-927d-4076-ba07-6c44743a5f92\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u9805\u76ee<\/th><th>\u898f\u7d04<\/th><th>\u8aac\u660e<\/th><\/tr><\/thead><tbody><tr><td>\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc<\/td><td><code>grails-app\/controllers\/<\/code><\/td><td>\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u30af\u30e9\u30b9\u306f\u81ea\u52d5\u7684\u306bURL\u30de\u30c3\u30d4\u30f3\u30b0\u3055\u308c\u308b<\/td><\/tr><tr><td>\u30c9\u30e1\u30a4\u30f3\u30af\u30e9\u30b9<\/td><td><code>grails-app\/domain\/<\/code><\/td><td>\u81ea\u52d5\u7684\u306b\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30c6\u30fc\u30d6\u30eb\u306b\u30de\u30c3\u30d4\u30f3\u30b0<\/td><\/tr><tr><td>\u30d3\u30e5\u30fc<\/td><td><code>grails-app\/views\/<\/code><\/td><td>\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u306e\u30a2\u30af\u30b7\u30e7\u30f3\u306b\u5bfe\u5fdc\u3059\u308bGSP\u30d5\u30a1\u30a4\u30eb<\/td><\/tr><tr><td>\u30b5\u30fc\u30d3\u30b9<\/td><td><code>grails-app\/services\/<\/code><\/td><td>\u30c8\u30e9\u30f3\u30b6\u30af\u30b7\u30e7\u30f3\u7ba1\u7406\u306a\u3069\u304c\u81ea\u52d5\u7684\u306b\u63d0\u4f9b\u3055\u308c\u308b<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<p class=\"is-style-sango-paragraph-memo-alt\">\u898f\u7d04\u306b\u57fa\u3065\u3044\u305f\u958b\u767a\u306e\u5177\u4f53\u4f8b\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u30c9\u30e1\u30a4\u30f3\u30af\u30e9\u30b9\u306e\u5b9a\u7fa9\nclass Book {\n    String title\n    String author\n    Date publishDate\n\n    \/\/ \u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u30eb\u30fc\u30eb\u3082\u5ba3\u8a00\u7684\u306b\u8a18\u8ff0\u53ef\u80fd\n    static constraints = {\n        title blank: false\n        author blank: false\n        publishDate nullable: true\n    }\n}\n\n\/\/ \u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u306e\u5b9f\u88c5\nclass BookController {\n    def index() {\n        \/\/ \u81ea\u52d5\u751f\u6210\u3055\u308c\u305fDynamicFinder\u3092\u4f7f\u7528\n        [books: Book.findAllByAuthor(\"J.K. Rowling\")]\n    }\n    def show(Long id) {\n        \/\/ ID\u306b\u3088\u308b\u81ea\u52d5\u691c\u7d22\n        [book: Book.get(id)]\n    }\n}<\/pre>\n\n\n\n<p>\u3053\u306e\u3088\u3046\u306a\u898f\u7d04\u30d9\u30fc\u30b9\u306e\u958b\u767a\u306b\u3088\u308a\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30dc\u30a4\u30e9\u30fc\u30d7\u30ec\u30fc\u30c8\u30b3\u30fc\u30c9\u306e\u524a\u6e1b<\/li>\n\n\n\n<li>\u4e00\u8cab\u6027\u306e\u3042\u308b\u958b\u767a\u30b9\u30bf\u30a4\u30eb\u306e\u5b9f\u73fe<\/li>\n\n\n\n<li>\u30c1\u30fc\u30e0\u958b\u767a\u3067\u306e\u751f\u7523\u6027\u5411\u4e0a<\/li>\n\n\n\n<li>\u5b66\u7fd2\u30b3\u30b9\u30c8\u306e\u4f4e\u6e1b<\/li>\n<\/ol>\n\n\n\n<p>\u304c\u53ef\u80fd\u3068\u306a\u308a\u307e\u3059\u3002<\/p>\n\n\n\n<p>Grails\u306f\u3053\u308c\u3089\u306e\u7279\u5fb4\u3092\u6d3b\u304b\u3057\u3001\u7279\u306b\u4ee5\u4e0b\u306e\u3088\u3046\u306a\u958b\u767a\u30b7\u30fc\u30f3\u3067\u529b\u3092\u767a\u63ee\u3057\u307e\u3059\uff1a<\/p>\n\n\n\n<p class=\"is-style-sango-paragraph-idea-alt\">\u529b\u304c\u767a\u63ee\u3067\u304d\u308b\u958b\u767a\u30b7\u30fc\u30f3<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30d7\u30ed\u30c8\u30bf\u30a4\u30d7\u306e\u8fc5\u901f\u306a\u958b\u767a<\/li>\n\n\n\n<li>\u30c7\u30fc\u30bf\u99c6\u52d5\u578b\u306eWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3<\/li>\n\n\n\n<li>RESTful API\u306e\u69cb\u7bc9<\/li>\n\n\n\n<li>\u30a8\u30f3\u30bf\u30fc\u30d7\u30e9\u30a4\u30ba\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u958b\u767a<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-3\">Grails\u306e\u4e3b\u8981\u306a\u7279\u5fb4\u3068\u6a5f\u80fd<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-4\">\u5f37\u529b\u306aORM\u30b5\u30dd\u30fc\u30c8\uff08GORM\uff09<\/h3>\n\n\n\n<p>GORM\uff08Grails Object Relational Mapping\uff09\u306f\u3001Grails\u304c\u63d0\u4f9b\u3059\u308b\u5f37\u529b\u306aORM\u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af\u3067\u3059\u3002Hibernate\u3092\u30d9\u30fc\u30b9\u3068\u3057\u306a\u304c\u3089\u3001\u3088\u308a\u76f4\u611f\u7684\u306a\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u64cd\u4f5c\u3092\u53ef\u80fd\u306b\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<p>GORM\u306e\u4e3b\u8981\u6a5f\u80fd\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u52d5\u7684\u30d5\u30a1\u30a4\u30f3\u30c0\u30fc<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u52d5\u7684\u30d5\u30a1\u30a4\u30f3\u30c0\u30fc\u306e\u4f8b\ndef users = User.findAllByAgeGreaterThanAndLastNameLike(18, 'Smith%')\ndef book = Book.findByTitleAndAuthor(\"Grails\u30ac\u30a4\u30c9\", \"\u5c71\u7530\u592a\u90ce\")<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30af\u30a8\u30eaDSL<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">def results = Book.createCriteria().list {\n    eq('publisher', 'TechBooks')\n    between('publishDate', startDate, endDate)\n    order('title', 'asc')\n    maxResults(10)\n    firstResult(0)\n}<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u30c7\u30fc\u30bf\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class Product {\n    String name\n    BigDecimal price\n\n    static constraints = {\n        name size: 5..50, blank: false\n        price min: 0.0, scale: 2\n    }\n}<\/pre>\n\n\n\n<ol start=\"4\" class=\"wp-block-list\">\n<li><strong>\u30ea\u30ec\u30fc\u30b7\u30e7\u30f3\u30b7\u30c3\u30d7\u30de\u30c3\u30d4\u30f3\u30b0<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class Author {\n    String name\n    static hasMany = [books: Book]\n}\n\nclass Book {\n    String title\n    static belongsTo = [author: Author]\n    static hasMany = [categories: Category]\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-5\">\u67d4\u8edf\u306a\u30d3\u30e5\u30fc\u5c64\uff08GSP\uff09<\/h3>\n\n\n\n<p>GSP\uff08Groovy Server Pages\uff09\u306f\u3001Grails\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30a8\u30f3\u30b8\u30f3\u3067\u3001\u4ee5\u4e0b\u306e\u7279\u5fb4\u3092\u6301\u3061\u307e\u3059\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30bf\u30b0\u30e9\u30a4\u30d6\u30e9\u30ea<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;!-- \u30d5\u30a9\u30fc\u30e0\u4f5c\u6210\u306e\u4f8b --&gt;\n&lt;g:form controller=\"book\" action=\"save\"&gt;\n    &lt;g:textField name=\"title\" value=\"${book?.title}\"\/&gt;\n    &lt;g:select name=\"author.id\" \n              from=\"${Author.list()}\" \n              optionKey=\"id\" \n              optionValue=\"name\"\/&gt;\n    &lt;g:submitButton name=\"save\" value=\"\u4fdd\u5b58\"\/&gt;\n&lt;\/g:form&gt;<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30ec\u30a4\u30a2\u30a6\u30c8\u3068\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;!-- \u30ec\u30a4\u30a2\u30a6\u30c8\u5b9a\u7fa9\uff08layouts\/main.gsp\uff09 --&gt;\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n    &lt;head&gt;\n        &lt;title&gt;&lt;g:layoutTitle default=\"My App\"\/&gt;&lt;\/title&gt;\n        &lt;g:layoutHead\/&gt;\n    &lt;\/head&gt;\n    &lt;body&gt;\n        &lt;div class=\"header\"&gt;\n            &lt;!-- \u5171\u901a\u30d8\u30c3\u30c0\u30fc --&gt;\n        &lt;\/div&gt;\n        &lt;g:layoutBody\/&gt;\n        &lt;div class=\"footer\"&gt;\n            &lt;!-- \u5171\u901a\u30d5\u30c3\u30bf\u30fc --&gt;\n        &lt;\/div&gt;\n    &lt;\/body&gt;\n&lt;\/html&gt;\n\n&lt;!-- \u30d3\u30e5\u30fc\u3067\u306e\u4f7f\u7528 --&gt;\n&lt;meta name=\"layout\" content=\"main\"\/&gt;<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u30ab\u30b9\u30bf\u30e0\u30bf\u30b0\u306e\u4f5c\u6210<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ TagLib\u306e\u5b9a\u7fa9\nclass CustomTagLib {\n    static namespace = \"custom\"\n\n    def formatPrice = { attrs, body -&gt;\n        def price = attrs.value\n        out &lt;&lt; \"\uffe5${price.toString().padLeft(3, '0')}\"\n    }\n}\n\n\/\/ \u30d3\u30e5\u30fc\u3067\u306e\u4f7f\u7528\n&lt;custom:formatPrice value=\"${product.price}\"\/&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-6\">\u30d7\u30e9\u30b0\u30a4\u30f3\u30a8\u30b3\u30b7\u30b9\u30c6\u30e0<\/h3>\n\n\n\n<p>Grails\u306f\u8c4a\u5bcc\u306a\u30d7\u30e9\u30b0\u30a4\u30f3\u30a8\u30b3\u30b7\u30b9\u30c6\u30e0\u3092\u6301\u3061\u3001\u69d8\u3005\u306a\u6a5f\u80fd\u3092\u5bb9\u6613\u306b\u8ffd\u52a0\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<p class=\"is-style-sango-paragraph-idea-alt\">\u4e3b\u8981\u30d7\u30e9\u30b0\u30a4\u30f3\u30ab\u30c6\u30b4\u30ea\uff1a<\/p>\n\n\n<div id=\"id-0168baca-b3bc-47ec-b0e4-f76d16ebb0e1\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u30ab\u30c6\u30b4\u30ea<\/th><th>\u4ee3\u8868\u7684\u306a\u30d7\u30e9\u30b0\u30a4\u30f3<\/th><th>\u6a5f\u80fd<\/th><\/tr><\/thead><tbody><tr><td>\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3<\/td><td>Spring Security Core<\/td><td>\u8a8d\u8a3c\u30fb\u8a8d\u53ef\u6a5f\u80fd<\/td><\/tr><tr><td>API\u958b\u767a<\/td><td>REST API<\/td><td>RESTful\u30b5\u30fc\u30d3\u30b9\u306e\u69cb\u7bc9<\/td><\/tr><tr><td>\u30ad\u30e3\u30c3\u30b7\u30e5<\/td><td>Cache<\/td><td>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316<\/td><\/tr><tr><td>\u691c\u7d22<\/td><td>Elasticsearch<\/td><td>\u5168\u6587\u691c\u7d22\u6a5f\u80fd<\/td><\/tr><tr><td>\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0<\/td><td>Actuator<\/td><td>\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u76e3\u8996<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<p class=\"is-style-sango-paragraph-memo-alt\">\u30d7\u30e9\u30b0\u30a4\u30f3\u306e\u5c0e\u5165\u4f8b\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ build.gradle\u3067\u306e\u4f9d\u5b58\u95a2\u4fc2\u306e\u8ffd\u52a0\ndependencies {\n    \/\/ Spring Security\u30d7\u30e9\u30b0\u30a4\u30f3\n    compile 'org.grails.plugins:spring-security-core:5.0.0'\n\n    \/\/ REST API\u30d7\u30e9\u30b0\u30a4\u30f3\n    compile 'org.grails.plugins:rest:1.0.0'\n}<\/pre>\n\n\n\n<p class=\"is-style-sango-paragraph-memo-alt\">\u30d7\u30e9\u30b0\u30a4\u30f3\u306e\u8a2d\u5b9a\u4f8b\uff08Spring Security\uff09\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ application.groovy\u3067\u306e\u8a2d\u5b9a\ngrails.plugin.springsecurity.userLookup.userDomainClassName = 'com.example.User'\ngrails.plugin.springsecurity.userLookup.authorityJoinClassName = 'com.example.UserRole'\ngrails.plugin.springsecurity.authority.className = 'com.example.Role'\n\n\/\/ \u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30eb\u30fc\u30eb\u306e\u5b9a\u7fa9\ngrails.plugin.springsecurity.controllerAnnotations.staticRules = [\n    [pattern: '\/',               access: ['permitAll']],\n    [pattern: '\/error',          access: ['permitAll']],\n    [pattern: '\/admin\/**',       access: ['ROLE_ADMIN']],\n    [pattern: '\/api\/**',         access: ['ROLE_API']]\n]<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u6a5f\u80fd\u3092\u7d44\u307f\u5408\u308f\u305b\u308b\u3053\u3068\u3067\u3001Grails\u306f:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u9ad8\u901f\u306a\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u958b\u767a<\/li>\n\n\n\n<li>\u4fdd\u5b88\u6027\u306e\u9ad8\u3044\u30b3\u30fc\u30c9\u30d9\u30fc\u30b9<\/li>\n\n\n\n<li>\u30b9\u30b1\u30fc\u30e9\u30d6\u30eb\u306a\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3<\/li>\n\n\n\n<li>\u30bb\u30ad\u30e5\u30a2\u306a\u30b7\u30b9\u30c6\u30e0<\/li>\n<\/ul>\n\n\n\n<p>\u306e\u69cb\u7bc9\u3092\u53ef\u80fd\u306b\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-7\">Grails\u306e\u30e1\u30ea\u30c3\u30c8\u30fb\u30c7\u30e1\u30ea\u30c3\u30c8<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-8\">\u958b\u767a\u751f\u7523\u6027\u306e\u5927\u5e45\u306a\u5411\u4e0a<\/h3>\n\n\n\n<p>Grails\u3092\u63a1\u7528\u3059\u308b\u3053\u3068\u3067\u5f97\u3089\u308c\u308b\u958b\u767a\u751f\u7523\u6027\u306e\u5411\u4e0a\u306b\u3064\u3044\u3066\u3001\u5177\u4f53\u7684\u306a\u5074\u9762\u304b\u3089\u5206\u6790\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<p><strong>1. \u30b3\u30fc\u30c9\u91cf\u306e\u524a\u6e1b<\/strong><\/p>\n\n\n\n<p>\u5f93\u6765\u306eJava\u3067\u306e\u5b9f\u88c5\u3068Groovy\/Grails\u3067\u306e\u5b9f\u88c5\u3092\u6bd4\u8f03\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Java\u3067\u306e\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u30af\u30e9\u30b9\u5b9f\u88c5\npublic class Customer {\n    private Long id;\n    private String firstName;\n    private String lastName;\n    private List&lt;Order&gt; orders;\n\n    public Long getId() { return id; }\n    public void setId(Long id) { this.id = id; }\n    public String getFirstName() { return firstName; }\n    public void setFirstName(String firstName) { this.firstName = firstName; }\n    public String getLastName() { return lastName; }\n    public void setLastName(String lastName) { this.lastName = lastName; }\n    public List&lt;Order&gt; getOrders() { return orders; }\n    public void setOrders(List&lt;Order&gt; orders) { this.orders = orders; }\n}\n\n\/\/ Grails\u3067\u306e\u540c\u7b49\u306e\u5b9f\u88c5\nclass Customer {\n    String firstName\n    String lastName\n    static hasMany = [orders: Order]\n}<\/pre>\n\n\n\n<p>\u751f\u7523\u6027\u5411\u4e0a\u306e\u5177\u4f53\u7684\u306a\u6570\u5024\uff1a<\/p>\n\n\n<div id=\"id-d33c1733-3fe9-40a0-82b2-f09ef0d7408f\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u958b\u767a\u30bf\u30b9\u30af<\/th><th>\u5f93\u6765\u306e\u6240\u8981\u6642\u9593<\/th><th>Grails\u4f7f\u7528\u6642<\/th><th>\u524a\u6e1b\u7387<\/th><\/tr><\/thead><tbody><tr><td>CRUD\u64cd\u4f5c\u306e\u5b9f\u88c5<\/td><td>4\u6642\u9593<\/td><td>30\u5206<\/td><td>87.5%<\/td><\/tr><tr><td>\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u5b9f\u88c5<\/td><td>2\u6642\u9593<\/td><td>15\u5206<\/td><td>87.5%<\/td><\/tr><tr><td>REST API\u69cb\u7bc9<\/td><td>8\u6642\u9593<\/td><td>2\u6642\u9593<\/td><td>75%<\/td><\/tr><tr><td>\u30c6\u30b9\u30c8\u5b9f\u88c5<\/td><td>6\u6642\u9593<\/td><td>2\u6642\u9593<\/td><td>66.7%<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<p><strong>2. \u958b\u767a\u30b5\u30a4\u30af\u30eb\u306e\u77ed\u7e2e<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30ea\u30ed\u30fc\u30c9\u306a\u3057\u3067\u306e\u5909\u66f4\u53cd\u6620\uff08\u30ea\u30ed\u30fc\u30c7\u30a3\u30f3\u30b0\u6a5f\u80fd\uff09<\/li>\n\n\n\n<li>\u30b9\u30ad\u30e3\u30d5\u30a9\u30fc\u30eb\u30c7\u30a3\u30f3\u30b0\u306b\u3088\u308b\u96db\u5f62\u751f\u6210<\/li>\n\n\n\n<li>\u81ea\u52d5\u30c6\u30b9\u30c8\u7d71\u5408<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-9\">Java\u3068\u306e\u89aa\u548c\u6027\u306e\u9ad8\u3055<\/h3>\n\n\n\n<p><strong>1. \u65e2\u5b58\u8cc7\u7523\u306e\u6d3b\u7528<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Java\u30e9\u30a4\u30d6\u30e9\u30ea\u306e\u76f4\u63a5\u5229\u7528\nimport com.google.common.collect.Lists\nimport org.apache.commons.lang3.StringUtils\n\nclass UserService {\n    def processUserData(String userData) {\n        \/\/ Guava\u30e9\u30a4\u30d6\u30e9\u30ea\u306e\u5229\u7528\n        List&lt;String&gt; dataList = Lists.newArrayList()\n\n        \/\/ Apache Commons\u306e\u5229\u7528\n        if (StringUtils.isNotBlank(userData)) {\n            \/\/ \u51e6\u7406\u30ed\u30b8\u30c3\u30af\n        }\n    }\n}<\/pre>\n\n\n\n<p><strong>2. \u6bb5\u968e\u7684\u306a\u79fb\u884c\u304c\u53ef\u80fd<\/strong><\/p>\n\n\n\n<p class=\"is-style-sango-paragraph-memo-alt\">\u79fb\u884c\u6226\u7565\u306e\u4f8b\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u65b0\u898f\u6a5f\u80fd\u3092Grails\u3067\u958b\u767a<\/li>\n\n\n\n<li>\u65e2\u5b58\u306eJava\u30b3\u30fc\u30c9\u3092\u6bb5\u968e\u7684\u306bGroovy\u5316<\/li>\n\n\n\n<li>\u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af\u5c64\u3092\u5f90\u3005\u306bGrails\u306b\u79fb\u884c<\/li>\n<\/ol>\n\n\n<div id=\"id-ad38f5c2-c554-4689-af48-19eae536909f\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u30d5\u30a7\u30fc\u30ba<\/th><th>\u65e2\u5b58Java\u6bd4\u7387<\/th><th>Grails\u6bd4\u7387<\/th><th>\u671f\u9593<\/th><\/tr><\/thead><tbody><tr><td>\u521d\u671f<\/td><td>90%<\/td><td>10%<\/td><td>1-2\u30f6\u6708<\/td><\/tr><tr><td>\u4e2d\u671f<\/td><td>50%<\/td><td>50%<\/td><td>3-6\u30f6\u6708<\/td><\/tr><tr><td>\u6700\u7d42<\/td><td>20%<\/td><td>80%<\/td><td>6-12\u30f6\u6708<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"i-10\">\u5b66\u7fd2\u66f2\u7dda\u3068\u5c0e\u5165\u30b3\u30b9\u30c8<\/h3>\n\n\n\n<p><strong>1. \u4e3b\u306a\u5b66\u7fd2\u9805\u76ee\u3068\u7fd2\u5f97\u671f\u9593<\/strong><\/p>\n\n\n<div id=\"id-6899f250-ce77-48e9-a1a0-464d67ca5417\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u5b66\u7fd2\u9805\u76ee<\/th><th>\u7fd2\u5f97\u76ee\u5b89<\/th><th>\u91cd\u8981\u5ea6<\/th><\/tr><\/thead><tbody><tr><td>Groovy\u57fa\u790e<\/td><td>1\u9031\u9593<\/td><td>\u2605\u2605\u2605\u2605\u2605<\/td><\/tr><tr><td>GORM<\/td><td>2\u9031\u9593<\/td><td>\u2605\u2605\u2605\u2605\u2606<\/td><\/tr><tr><td>GSP<\/td><td>1\u9031\u9593<\/td><td>\u2605\u2605\u2605\u2606\u2606<\/td><\/tr><tr><td>Grails\u30d7\u30e9\u30b0\u30a4\u30f3<\/td><td>2\u9031\u9593<\/td><td>\u2605\u2605\u2605\u2605\u2606<\/td><\/tr><tr><td>\u30c6\u30b9\u30c8\u624b\u6cd5<\/td><td>1\u9031\u9593<\/td><td>\u2605\u2605\u2605\u2605\u2606<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<p><strong>2. \u5c0e\u5165\u6642\u306e\u8ab2\u984c\u3068\u5bfe\u7b56<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u5b66\u7fd2\u30b3\u30b9\u30c8\u306e\u554f\u984c\n<ul class=\"wp-block-list\">\n<li>\u5bfe\u7b56\uff1a\u6bb5\u968e\u7684\u306a\u5c0e\u5165\u3068\u96c6\u4e2d\u7684\u306a\u30cf\u30f3\u30ba\u30aa\u30f3\u30c8\u30ec\u30fc\u30cb\u30f3\u30b0<\/li>\n\n\n\n<li>\u30c4\u30fc\u30eb\uff1aInteractive Groovy Console\u3001Grails Console<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30c1\u30e5\u30fc\u30cb\u30f3\u30b0\n<ul class=\"wp-block-list\">\n<li>\u8ab2\u984c\uff1aN+1\u554f\u984c\u3001\u30e1\u30e2\u30ea\u4f7f\u7528\u91cf<\/li>\n\n\n\n<li>\u5bfe\u7b56\uff1a<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ N+1\u554f\u984c\u306e\u56de\u907f\u4f8b\ndef books = Book.createCriteria().list {\n    createAlias('author', 'a')\n    createAlias('publisher', 'p')\n    fetchMode('author', FetchMode.JOIN)\n    fetchMode('publisher', FetchMode.JOIN)\n}<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li>\u30c7\u30d0\u30c3\u30b0\u306e\u8907\u96d1\u3055\n<ul class=\"wp-block-list\">\n<li>\u8ab2\u984c\uff1a\u52d5\u7684\u8a00\u8a9e\u7279\u6709\u306e\u30c7\u30d0\u30c3\u30b0\u306e\u96e3\u3057\u3055<\/li>\n\n\n\n<li>\u5bfe\u7b56\uff1a\n<ul class=\"wp-block-list\">\n<li>IDE\u7d71\u5408\u30c7\u30d0\u30c3\u30ac\u30fc\u306e\u6d3b\u7528<\/li>\n\n\n\n<li>\u30ed\u30b0\u51fa\u529b\u306e\u5f37\u5316<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u30ed\u30b0\u8a2d\u5b9a\u4f8b\nlog4j = {\n    debug 'grails.app.controllers',\n         'grails.app.services',\n         'grails.app.domain'\n}<\/pre>\n\n\n\n<ol start=\"4\" class=\"wp-block-list\">\n<li>\u30c1\u30fc\u30e0\u958b\u767a\u3067\u306e\u7d71\u4e00\u6027\n<ul class=\"wp-block-list\">\n<li>\u8ab2\u984c\uff1a\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0\u898f\u7d04\u306e\u7d71\u4e00<\/li>\n\n\n\n<li>\u5bfe\u7b56\uff1a\n<ul class=\"wp-block-list\">\n<li>CodeNarc\u7b49\u306e\u9759\u7684\u89e3\u6790\u30c4\u30fc\u30eb\u306e\u5c0e\u5165<\/li>\n\n\n\n<li>\u30ec\u30d3\u30e5\u30fc\u30d7\u30ed\u30bb\u30b9\u306e\u78ba\u7acb<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p class=\"is-style-sango-paragraph-memo-alt\">\u5c0e\u5165\u5224\u65ad\u306e\u305f\u3081\u306e\u30c1\u30a7\u30c3\u30af\u30ea\u30b9\u30c8\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[ ] \u30c1\u30fc\u30e0\u306eJava\/Groovy\u7fd2\u719f\u5ea6<\/li>\n\n\n\n<li>[ ] \u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u898f\u6a21\u3068\u671f\u9593<\/li>\n\n\n\n<li>[ ] \u65e2\u5b58\u30b7\u30b9\u30c6\u30e0\u3068\u306e\u7d71\u5408\u8981\u4ef6<\/li>\n\n\n\n<li>[ ] \u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u8981\u4ef6<\/li>\n\n\n\n<li>[ ] \u4fdd\u5b88\u30fb\u904b\u7528\u4f53\u5236<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-11\">Spring Boot\u3068\u306e\u6bd4\u8f03<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-12\">\u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3\u306e\u9055\u3044<\/h3>\n\n\n\n<p>Grails\u3068 Spring Boot\u306f\u3001\u3069\u3061\u3089\u3082JVM\u30d9\u30fc\u30b9\u306e\u5f37\u529b\u306aWeb\u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af\u3067\u3059\u304c\u3001\u305d\u306e\u30a2\u30d7\u30ed\u30fc\u30c1\u306b\u5927\u304d\u306a\u9055\u3044\u304c\u3042\u308a\u307e\u3059\u3002<\/p>\n\n\n\n<p><strong>\u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3\u6bd4\u8f03\u8868<\/strong><\/p>\n\n\n<div id=\"id-e719b7af-fb0b-4a37-b318-72a13463f523\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u89b3\u70b9<\/th><th>Grails<\/th><th>Spring Boot<\/th><\/tr><\/thead><tbody><tr><td>\u57fa\u672c\u8a00\u8a9e<\/td><td>Groovy\uff08Java\u3082\u53ef\uff09<\/td><td>Java\uff08Kotlin\u3082\u53ef\uff09<\/td><\/tr><tr><td>\u8a2d\u5b9a\u65b9\u5f0f<\/td><td>Convention over Configuration<\/td><td>Auto-configuration<\/td><\/tr><tr><td>DI \u30b3\u30f3\u30c6\u30ca<\/td><td>Spring<\/td><td>Spring<\/td><\/tr><tr><td>ORM<\/td><td>GORM\uff08Hibernate\uff09<\/td><td>JPA\/Hibernate<\/td><\/tr><tr><td>\u30d3\u30e5\u30fc\u5c64<\/td><td>GSP<\/td><td>Thymeleaf\/JSP<\/td><\/tr><tr><td>\u30d3\u30eb\u30c9\u30c4\u30fc\u30eb<\/td><td>Gradle<\/td><td>Maven\/Gradle<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<p><strong>1. \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u69cb\u9020\u306e\u6bd4\u8f03<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Grails\u306e\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u5b9f\u88c5\nclass BookController {\n    def bookService\n\n    def index() {\n        [books: bookService.listAll()]\n    }\n\n    def save() {\n        def book = new Book(params)\n        book.save()\n        redirect action: 'index'\n    }\n}<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Spring Boot\u306e\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u5b9f\u88c5\n@Controller\n@RequestMapping(\"\/books\")\npublic class BookController {\n    @Autowired\n    private BookService bookService;\n\n    @GetMapping\n    public String index(Model model) {\n        model.addAttribute(\"books\", bookService.listAll());\n        return \"books\/index\";\n    }\n\n    @PostMapping\n    public String save(@ModelAttribute Book book) {\n        bookService.save(book);\n        return \"redirect:\/books\";\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-13\">\u958b\u767a\u751f\u7523\u6027\u306e\u6bd4\u8f03<\/h3>\n\n\n\n<p><strong>1. \u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u521d\u671f\u5316\u3068\u57fa\u672c\u8a2d\u5b9a<\/strong><\/p>\n\n\n<div id=\"id-058bba50-8375-46ec-83e2-fad140e54c86\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u6a5f\u80fd<\/th><th>Grails<\/th><th>Spring Boot<\/th><th>\u52dd\u8005<\/th><\/tr><\/thead><tbody><tr><td>\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u4f5c\u6210<\/td><td><code>grails create-app<\/code><\/td><td>Spring Initializr<\/td><td>\u540c\u7b49<\/td><\/tr><tr><td>\u4f9d\u5b58\u95a2\u4fc2\u7ba1\u7406<\/td><td>\u30d7\u30e9\u30b0\u30a4\u30f3\u30b7\u30b9\u30c6\u30e0<\/td><td>Spring Starter<\/td><td>Spring Boot<\/td><\/tr><tr><td>\u74b0\u5883\u8a2d\u5b9a<\/td><td><code>application.yml<\/code><\/td><td><code>application.properties<\/code>\/<code>yml<\/code><\/td><td>\u540c\u7b49<\/td><\/tr><tr><td>\u30db\u30c3\u30c8\u30ea\u30ed\u30fc\u30c9<\/td><td>\u7d44\u307f\u8fbc\u307f<\/td><td>DevTools<\/td><td>Grails<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<p><strong>2. \u30b3\u30fc\u30c9\u8a18\u8ff0\u91cf\u306e\u6bd4\u8f03<\/strong><\/p>\n\n\n\n<p class=\"is-style-sango-paragraph-memo-alt\">\u5178\u578b\u7684\u306aCRUD\u6a5f\u80fd\u306e\u5b9f\u88c5\u4f8b\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Grails\u3067\u306e\u30c9\u30e1\u30a4\u30f3\u30af\u30e9\u30b9\u3068\u30ea\u30dd\u30b8\u30c8\u30ea\u5b9f\u88c5\nclass Book {\n    String title\n    String author\n    Date publishDate\n\n    static constraints = {\n        title blank: false\n        author blank: false\n    }\n}\n\/\/ \u30ea\u30dd\u30b8\u30c8\u30ea\u306f\u81ea\u52d5\u751f\u6210<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Spring Boot\u3067\u306e\u540c\u7b49\u5b9f\u88c5\n@Entity\npublic class Book {\n    @Id\n    @GeneratedValue(strategy = GenerationType.IDENTITY)\n    private Long id;\n\n    @NotBlank\n    private String title;\n\n    @NotBlank\n    private String author;\n\n    @Temporal(TemporalType.DATE)\n    private Date publishDate;\n\n    \/\/ getters and setters\n}\n\n@Repository\npublic interface BookRepository extends JpaRepository&lt;Book, Long&gt; {\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-14\">\u30e6\u30fc\u30b9\u30b1\u30fc\u30b9\u5225\u306e\u9078\u5b9a\u57fa\u6e96<\/h3>\n\n\n\n<p><strong>1. \u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u7279\u6027\u306b\u3088\u308b\u9078\u5b9a\u30de\u30c8\u30ea\u30c3\u30af\u30b9<\/strong><\/p>\n\n\n<div id=\"id-82362e68-e331-4cf3-ba7e-c710f631337d\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u7279\u6027<\/th><th>Grails\u63a8\u5968<\/th><th>Spring Boot\u63a8\u5968<\/th><\/tr><\/thead><tbody><tr><td>\u5c0f\u898f\u6a21\u301c\u4e2d\u898f\u6a21Web<\/td><td>\u25ce<\/td><td>\u3007<\/td><\/tr><tr><td>\u5927\u898f\u6a21\u30a8\u30f3\u30bf\u30fc\u30d7\u30e9\u30a4\u30ba<\/td><td>\u25b3<\/td><td>\u25ce<\/td><\/tr><tr><td>\u30de\u30a4\u30af\u30ed\u30b5\u30fc\u30d3\u30b9<\/td><td>\u3007<\/td><td>\u25ce<\/td><\/tr><tr><td>RESTful API<\/td><td>\u25ce<\/td><td>\u25ce<\/td><\/tr><tr><td>\u30d7\u30ed\u30c8\u30bf\u30a4\u30d4\u30f3\u30b0<\/td><td>\u25ce<\/td><td>\u3007<\/td><\/tr><tr><td>\u30ec\u30ac\u30b7\u30fcJava\u79fb\u884c<\/td><td>\u3007<\/td><td>\u25ce<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<p><strong>2. \u6280\u8853\u9078\u5b9a\u306e\u305f\u3081\u306e\u5224\u65ad\u57fa\u6e96<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30c1\u30fc\u30e0\u8981\u56e0\n<ul class=\"wp-block-list\">\n<li>Java\/Groovy\u7d4c\u9a13<\/li>\n\n\n\n<li>\u5b66\u7fd2\u610f\u6b32<\/li>\n\n\n\n<li>\u958b\u767a\u8005\u5e02\u5834<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u8981\u56e0\n<ul class=\"wp-block-list\">\n<li>\u958b\u767a\u671f\u9593<\/li>\n\n\n\n<li>\u30b9\u30b1\u30fc\u30e9\u30d3\u30ea\u30c6\u30a3\u8981\u4ef6<\/li>\n\n\n\n<li>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u8981\u4ef6<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u30d3\u30b8\u30cd\u30b9\u8981\u56e0\n<ul class=\"wp-block-list\">\n<li>\u4e88\u7b97<\/li>\n\n\n\n<li>\u4fdd\u5b88\u6027<\/li>\n\n\n\n<li>\u30b5\u30dd\u30fc\u30c8\u4f53\u5236<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p><strong>\u9078\u5b9a\u30d5\u30ed\u30fc\u30c1\u30e3\u30fc\u30c8<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">graph TD\n    A[\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u958b\u59cb] --&gt; B{\u30c1\u30fc\u30e0\u306fGroovy\u7d4c\u9a13\u3042\u308a?}\n    B --&gt;|Yes| C[Grails\u691c\u8a0e]\n    B --&gt;|No| D{\u958b\u767a\u671f\u9593\u306f\u77ed\u3044?}\n    D --&gt;|Yes| E{\u30d7\u30ed\u30c8\u30bf\u30a4\u30d7\u91cd\u8996?}\n    D --&gt;|No| F[Spring Boot\u691c\u8a0e]\n    E --&gt;|Yes| C\n    E --&gt;|No| F\n    C --&gt; G{\u30b9\u30b1\u30fc\u30e9\u30d3\u30ea\u30c6\u30a3\u91cd\u8996?}\n    G --&gt;|Yes| F\n    G --&gt;|No| H[Grails\u63a1\u7528]\n    F --&gt; I[Spring Boot\u63a1\u7528]<\/pre>\n\n\n\n<p><strong>\u5b9f\u88c5\u4f8b\u3067\u306e\u6bd4\u8f03\uff08REST API\uff09<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Grails\u3067\u306e\u30ec\u30b9\u30c8 API\u5b9f\u88c5\n@RestController\nclass BookApiController {\n    def index() {\n        respond Book.list()\n    }\n\n    def save(Book book) {\n        book.save()\n        respond book\n    }\n}<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Spring Boot\u3067\u306eREST API\u5b9f\u88c5\n@RestController\n@RequestMapping(\"\/api\/books\")\npublic class BookApiController {\n    @Autowired\n    private BookRepository bookRepository;\n\n    @GetMapping\n    public List&lt;Book&gt; index() {\n        return bookRepository.findAll();\n    }\n\n    @PostMapping\n    public ResponseEntity&lt;Book&gt; save(@RequestBody Book book) {\n        Book savedBook = bookRepository.save(book);\n        return ResponseEntity.ok(savedBook);\n    }\n}<\/pre>\n\n\n\n<p>\u3053\u306e\u6bd4\u8f03\u3092\u901a\u3058\u3066\u3001\u4ee5\u4e0b\u306e\u30dd\u30a4\u30f3\u30c8\u304c\u660e\u78ba\u306b\u306a\u308a\u307e\u3059\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Grails\u306e\u512a\u4f4d\u70b9\n<ul class=\"wp-block-list\">\n<li>\u3088\u308a\u5c11\u306a\u3044\u30b3\u30fc\u30c9\u91cf<\/li>\n\n\n\n<li>\u9ad8\u901f\u306a\u30d7\u30ed\u30c8\u30bf\u30a4\u30d4\u30f3\u30b0<\/li>\n\n\n\n<li>\u67d4\u8edf\u306a\u958b\u767a\u30a2\u30d7\u30ed\u30fc\u30c1<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Spring Boot\u306e\u512a\u4f4d\u70b9\n<ul class=\"wp-block-list\">\n<li>\u8c4a\u5bcc\u306a\u30a8\u30b3\u30b7\u30b9\u30c6\u30e0<\/li>\n\n\n\n<li>\u5f37\u529b\u306a\u30a8\u30f3\u30bf\u30fc\u30d7\u30e9\u30a4\u30ba\u30b5\u30dd\u30fc\u30c8<\/li>\n\n\n\n<li>\u5e83\u3044\u958b\u767a\u8005\u30b3\u30df\u30e5\u30cb\u30c6\u30a3<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-15\">Grails\u306e\u74b0\u5883\u69cb\u7bc9\u624b\u9806<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-16\">SDKMAN\u3092\u4f7f\u7528\u3057\u305f\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb<\/h3>\n\n\n\n<p>SDKMAN\u306f\u3001\u69d8\u3005\u306aJVM\u8a00\u8a9e\u3084\u30c4\u30fc\u30eb\u306e\u7ba1\u7406\u3092\u5bb9\u6613\u306b\u3059\u308b\u30d0\u30fc\u30b8\u30e7\u30f3\u30de\u30cd\u30fc\u30b8\u30e3\u30fc\u3067\u3059\u3002Grails\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u306b\u63a8\u5968\u3055\u308c\u308b\u65b9\u6cd5\u3067\u3059\u3002<\/p>\n\n\n\n<p><strong>1. SDKMAN\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb<\/strong><\/p>\n\n\n\n<p>Unix\u7cfbOS\uff08Linux\/macOS\uff09\u3067\u306e\u624b\u9806\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># SDKMAN\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\ncurl -s \"https:\/\/get.sdkman.io\" | bash\n\n# \u74b0\u5883\u5909\u6570\u306e\u8a2d\u5b9a\nsource \"$HOME\/.sdkman\/bin\/sdkman-init.sh\"\n\n# \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u306e\u78ba\u8a8d\nsdk version<\/pre>\n\n\n\n<p>Windows\u74b0\u5883\u3067\u306e\u624b\u9806\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># GitBash\u307e\u305f\u306fWSL2\u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u3092\u63a8\u5968\n# GitBash\u3067\u306e\u5b9f\u884c\ncurl -s \"https:\/\/get.sdkman.io\" | bash<\/pre>\n\n\n\n<p><strong>2. Grails\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u5229\u7528\u53ef\u80fd\u306a\u30d0\u30fc\u30b8\u30e7\u30f3\u306e\u78ba\u8a8d\nsdk list grails\n\n# \u6700\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\nsdk install grails\n\n# \u7279\u5b9a\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\nsdk install grails 6.0.0\n\n# \u30c7\u30d5\u30a9\u30eb\u30c8\u30d0\u30fc\u30b8\u30e7\u30f3\u306e\u8a2d\u5b9a\nsdk default grails 6.0.0\n\n# \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u306e\u78ba\u8a8d\ngrails --version<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-17\">\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u4f5c\u6210\u3068\u8a2d\u5b9a<\/h3>\n\n\n\n<p><strong>1. \u65b0\u898f\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u4f5c\u6210<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u57fa\u672c\u7684\u306aWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u4f5c\u6210\ngrails create-app my-grails-app\n\n# REST API\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u4f5c\u6210\ngrails create-app my-api --profile=rest-api\n\n# \u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u3078\u306e\u79fb\u52d5\ncd my-grails-app<\/pre>\n\n\n\n<p><strong>2. \u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u69cb\u9020\u306e\u78ba\u8a8d<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">my-grails-app\/\n\u251c\u2500\u2500 grails-app\/\n\u2502   \u251c\u2500\u2500 conf\/            # \u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\n\u2502   \u251c\u2500\u2500 controllers\/     # \u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\n\u2502   \u251c\u2500\u2500 domain\/         # \u30c9\u30e1\u30a4\u30f3\u30af\u30e9\u30b9\n\u2502   \u251c\u2500\u2500 services\/       # \u30b5\u30fc\u30d3\u30b9\n\u2502   \u251c\u2500\u2500 views\/          # \u30d3\u30e5\u30fc\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\n\u2502   \u2514\u2500\u2500 i18n\/           # \u56fd\u969b\u5316\u30ea\u30bd\u30fc\u30b9\n\u251c\u2500\u2500 src\/\n\u2502   \u251c\u2500\u2500 main\/           # \u30e1\u30a4\u30f3\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9\n\u2502   \u2514\u2500\u2500 test\/           # \u30c6\u30b9\u30c8\u30b3\u30fc\u30c9\n\u251c\u2500\u2500 build.gradle        # \u30d3\u30eb\u30c9\u8a2d\u5b9a\n\u2514\u2500\u2500 application.yml     # \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u8a2d\u5b9a<\/pre>\n\n\n\n<p><strong>3. \u57fa\u672c\u8a2d\u5b9a\u306e\u8abf\u6574<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># application.yml\u306e\u8a2d\u5b9a\u4f8b\nenvironments:\n    development:\n        dataSource:\n            dbCreate: update\n            url: jdbc:h2:mem:devDb\n\n    test:\n        dataSource:\n            dbCreate: update\n            url: jdbc:h2:mem:testDb\n\n    production:\n        dataSource:\n            dbCreate: none\n            url: jdbc:postgresql:\/\/localhost:5432\/prodDb\n            username: ${JDBC_USERNAME}\n            password: ${JDBC_PASSWORD}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-18\">\u958b\u767a\u74b0\u5883\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/h3>\n\n\n\n<p><strong>1. IDE\u306e\u8a2d\u5b9a<\/strong><\/p>\n\n\n\n<p>\u63a8\u5968IDE\u8a2d\u5b9a\uff1a<\/p>\n\n\n<div id=\"id-f88061b9-74d6-4ca2-b80c-a0170d1b8d2c\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>IDE<\/th><th>\u30d7\u30e9\u30b0\u30a4\u30f3<\/th><th>\u4e3b\u306a\u6a5f\u80fd<\/th><\/tr><\/thead><tbody><tr><td>IntelliJ IDEA<\/td><td>Groovy<\/td><td>\u30b3\u30fc\u30c9\u88dc\u5b8c\u3001\u30ea\u30d5\u30a1\u30af\u30bf\u30ea\u30f3\u30b0<\/td><\/tr><tr><td>VS Code<\/td><td>Groovy Extension Pack<\/td><td>\u30b7\u30f3\u30bf\u30c3\u30af\u30b9\u30cf\u30a4\u30e9\u30a4\u30c8\u3001\u30c7\u30d0\u30c3\u30b0<\/td><\/tr><tr><td>Eclipse<\/td><td>Groovy-Eclipse<\/td><td>Groovy\/Grails\u30b5\u30dd\u30fc\u30c8<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<p>IntelliJ IDEA\u3067\u306e\u63a8\u5968\u8a2d\u5b9a\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Groovy\u30b3\u30f3\u30d1\u30a4\u30e9\u8a2d\u5b9a\ngroovyc.static.compilation.enabled=true\ngroovyc.parameters.enabled=true\n\n# \u30b3\u30fc\u30c9\u88dc\u5b8c\u306e\u5f37\u5316\neditor.suggest.snippets.enabled=true<\/pre>\n\n\n\n<p><strong>2. \u958b\u767a\u30d5\u30ed\u30fc\u81ea\u52d5\u5316<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ build.gradle\u3067\u306e\u958b\u767a\u652f\u63f4\u30bf\u30b9\u30af\u5b9a\u7fa9\ntasks.register('devSetup') {\n    doLast {\n        \/\/ \u958b\u767a\u7528\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\n        exec {\n            commandLine 'grails', 'dev-db-setup'\n        }\n\n        \/\/ \u958b\u767a\u7528\u30c7\u30fc\u30bf\u306e\u6295\u5165\n        exec {\n            commandLine 'grails', 'db-seed'\n        }\n    }\n}\n\n\/\/ \u30c6\u30b9\u30c8\u81ea\u52d5\u5316\u306e\u8a2d\u5b9a\ntest {\n    maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1\n    forkEvery = 100\n    reports.html.enabled = true\n}<\/pre>\n\n\n\n<p><strong>3. \u958b\u767a\u74b0\u5883\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30d0\u30fc\u30b8\u30e7\u30f3\u7ba1\u7406\u306e\u63a8\u5968\u8a2d\u5b9a<\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># .gitignore\u306e\u63a8\u5968\u8a2d\u5b9a\n\/build\/\n.gradle\/\n*.iml\n.idea\/\n*.log\n\/target\/<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li>\u30c7\u30d0\u30c3\u30b0\u8a2d\u5b9a<\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># application.yml\u3067\u306e\u30c7\u30d0\u30c3\u30b0\u8a2d\u5b9a\nenvironments:\n    development:\n        grails:\n            logging:\n                debug: true\n            gsp:\n                reload: true\n            plugin:\n                console:\n                    enabled: true<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li>\u958b\u767a\u751f\u7523\u6027\u3092\u5411\u4e0a\u3055\u305b\u308bTips<\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Command Object for validation\nclass BookCommand {\n    String title\n    String author\n\n    static constraints = {\n        title blank: false\n        author blank: false\n    }\n}\n\n\/\/ Controller\u3067\u306e\u5229\u7528\ndef save(BookCommand cmd) {\n    if (cmd.hasErrors()) {\n        respond cmd.errors\n        return\n    }\n    \/\/ \u51e6\u7406\u7d9a\u884c\n}<\/pre>\n\n\n\n<ol start=\"4\" class=\"wp-block-list\">\n<li>\u52b9\u7387\u7684\u306a\u958b\u767a\u306e\u305f\u3081\u306e\u30c1\u30a7\u30c3\u30af\u30ea\u30b9\u30c8\n<ul class=\"wp-block-list\">\n<li>[ ] SDKMAN\u306b\u3088\u308b\u74b0\u5883\u7ba1\u7406<\/li>\n\n\n\n<li>[ ] IDE\u8a2d\u5b9a\u306e\u6700\u9069\u5316<\/li>\n\n\n\n<li>[ ] \u30c7\u30d0\u30c3\u30b0\u30c4\u30fc\u30eb\u306e\u8a2d\u5b9a<\/li>\n\n\n\n<li>[ ] \u30db\u30c3\u30c8\u30ea\u30ed\u30fc\u30c9\u306e\u6709\u52b9\u5316<\/li>\n\n\n\n<li>[ ] \u30c6\u30b9\u30c8\u81ea\u52d5\u5316\u306e\u8a2d\u5b9a<\/li>\n\n\n\n<li>[ ] \u30b3\u30fc\u30c9\u54c1\u8cea\u30c4\u30fc\u30eb\u306e\u5c0e\u5165<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-19\">\u5b9f\u8df5\u7684\u306aGrails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u958b\u767a<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-20\">\u30c9\u30e1\u30a4\u30f3\u30af\u30e9\u30b9\u306e\u8a2d\u8a08\u3068\u5b9f\u88c5<\/h3>\n\n\n\n<p>\u30c9\u30e1\u30a4\u30f3\u99c6\u52d5\u8a2d\u8a08\uff08DDD\uff09\u306e\u539f\u5247\u306b\u57fa\u3065\u3044\u305f\u3001\u5b9f\u8df5\u7684\u306a\u30c9\u30e1\u30a4\u30f3\u30e2\u30c7\u30eb\u306e\u5b9f\u88c5\u65b9\u6cd5\u3092\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<p><strong>1. \u30c9\u30e1\u30a4\u30f3\u30af\u30e9\u30b9\u306e\u57fa\u672c\u5b9f\u88c5<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u57fa\u672c\u7684\u306a\u30c9\u30e1\u30a4\u30f3\u30af\u30e9\u30b9\u306e\u5b9f\u88c5\u4f8b\nclass Order {\n    String orderNumber\n    Date orderDate\n    BigDecimal totalAmount\n    OrderStatus status\n    Customer customer\n\n    static hasMany = [items: OrderItem]\n\n    \/\/ \u5217\u6319\u578b\u306e\u5b9a\u7fa9\n    enum OrderStatus {\n        PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED\n    }\n\n    \/\/ \u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u5b9a\u7fa9\n    static constraints = {\n        orderNumber unique: true, blank: false\n        orderDate nullable: false\n        totalAmount min: 0.0\n        status nullable: false\n        customer nullable: false\n    }\n\n    \/\/ \u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30de\u30c3\u30d4\u30f3\u30b0\n    static mapping = {\n        table 'orders'\n        orderNumber index: true\n        items cascade: 'all-delete-orphan'\n    }\n\n    \/\/ \u30d3\u30b8\u30cd\u30b9\u30ed\u30b8\u30c3\u30af\n    def calculateTotal() {\n        totalAmount = items.sum { it.quantity * it.unitPrice }\n    }\n\n    \/\/ \u30e9\u30a4\u30d5\u30b5\u30a4\u30af\u30eb\u30a4\u30d9\u30f3\u30c8\n    def beforeInsert() {\n        if (!orderNumber) {\n            orderNumber = generateOrderNumber()\n        }\n    }\n\n    private String generateOrderNumber() {\n        \"ORD-${new Date().format('yyyyMMdd')}-${UUID.randomUUID().toString()[0..7]}\"\n    }\n}\n\n\/\/ \u95a2\u9023\u30c9\u30e1\u30a4\u30f3\u30af\u30e9\u30b9\nclass OrderItem {\n    Product product\n    Integer quantity\n    BigDecimal unitPrice\n\n    static belongsTo = [order: Order]\n\n    static constraints = {\n        product nullable: false\n        quantity min: 1\n        unitPrice min: 0.0\n    }\n}<\/pre>\n\n\n\n<p><strong>2. \u30c9\u30e1\u30a4\u30f3\u30e2\u30c7\u30ea\u30f3\u30b0\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/strong><\/p>\n\n\n<div id=\"id-4b971d0c-4d3a-493c-a8e0-3620f2b2ab86\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u30d1\u30bf\u30fc\u30f3<\/th><th>\u5b9f\u88c5\u4f8b<\/th><th>\u7528\u9014<\/th><\/tr><\/thead><tbody><tr><td>Value Object<\/td><td><code>Money<\/code>, <code>EmailAddress<\/code><\/td><td>\u5024\u306e\u4e0d\u5909\u6027\u4fdd\u8a3c<\/td><\/tr><tr><td>Entity<\/td><td><code>Order<\/code>, <code>Customer<\/code><\/td><td>\u30d3\u30b8\u30cd\u30b9\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3<\/td><\/tr><tr><td>Aggregate Root<\/td><td><code>Order<\/code>\uff08items\u542b\u3080\uff09<\/td><td>\u30c8\u30e9\u30f3\u30b6\u30af\u30b7\u30e7\u30f3\u5883\u754c<\/td><\/tr><tr><td>Repository<\/td><td><code>OrderRepository<\/code><\/td><td>\u30c7\u30fc\u30bf\u30a2\u30af\u30bb\u30b9\u62bd\u8c61\u5316<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"i-21\">\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u3068\u30d3\u30e5\u30fc\u306e\u9023\u643a<\/h3>\n\n\n\n<p><strong>1. RESTful\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u306e\u5b9f\u88c5<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ RESTful\u306a\u30aa\u30fc\u30c0\u30fc\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\n@RestController\nclass OrderController {\n    OrderService orderService\n\n    \/\/ \u6ce8\u6587\u4e00\u89a7\u53d6\u5f97\n    def index() {\n        respond orderService.listOrders(params)\n    }\n\n    \/\/ \u6ce8\u6587\u8a73\u7d30\u53d6\u5f97\n    def show(Long id) {\n        respond orderService.getOrder(id)\n    }\n\n    \/\/ \u6ce8\u6587\u4f5c\u6210\n    @Transactional\n    def save(OrderCommand cmd) {\n        if (cmd.hasErrors()) {\n            respond cmd.errors, status: 422\n            return\n        }\n\n        def order = orderService.createOrder(cmd)\n        respond order, status: 201\n    }\n\n    \/\/ \u6ce8\u6587\u66f4\u65b0\n    @Transactional\n    def update(Long id, OrderCommand cmd) {\n        if (cmd.hasErrors()) {\n            respond cmd.errors, status: 422\n            return\n        }\n\n        def order = orderService.updateOrder(id, cmd)\n        respond order\n    }\n}\n\n\/\/ \u30b3\u30de\u30f3\u30c9\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\nclass OrderCommand {\n    Long customerId\n    List&lt;OrderItemCommand&gt; items\n\n    static constraints = {\n        customerId nullable: false\n        items minSize: 1\n    }\n}<\/pre>\n\n\n\n<p><strong>2. GSP\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306e\u5b9f\u88c5<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;%-- orders\/index.gsp --%&gt;\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta name=\"layout\" content=\"main\"\/&gt;\n    &lt;title&gt;\u6ce8\u6587\u4e00\u89a7&lt;\/title&gt;\n    &lt;asset:stylesheet src=\"orders.css\"\/&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div class=\"container\"&gt;\n        &lt;g:if test=\"${flash.message}\"&gt;\n            &lt;div class=\"alert alert-info\"&gt;${flash.message}&lt;\/div&gt;\n        &lt;\/g:if&gt;\n\n        &lt;table class=\"table\"&gt;\n            &lt;thead&gt;\n                &lt;tr&gt;\n                    &lt;g:sortableColumn property=\"orderNumber\" title=\"\u6ce8\u6587\u756a\u53f7\"\/&gt;\n                    &lt;g:sortableColumn property=\"orderDate\" title=\"\u6ce8\u6587\u65e5\"\/&gt;\n                    &lt;g:sortableColumn property=\"status\" title=\"\u30b9\u30c6\u30fc\u30bf\u30b9\"\/&gt;\n                    &lt;th&gt;\u64cd\u4f5c&lt;\/th&gt;\n                &lt;\/tr&gt;\n            &lt;\/thead&gt;\n            &lt;tbody&gt;\n                &lt;g:each in=\"${orderList}\" var=\"order\"&gt;\n                    &lt;tr&gt;\n                        &lt;td&gt;&lt;g:link action=\"show\" id=\"${order.id}\"&gt;${order.orderNumber}&lt;\/g:link&gt;&lt;\/td&gt;\n                        &lt;td&gt;&lt;g:formatDate date=\"${order.orderDate}\" format=\"yyyy\/MM\/dd\"\/&gt;&lt;\/td&gt;\n                        &lt;td&gt;${order.status}&lt;\/td&gt;\n                        &lt;td&gt;\n                            &lt;g:link action=\"edit\" id=\"${order.id}\" class=\"btn btn-primary\"&gt;\u7de8\u96c6&lt;\/g:link&gt;\n                        &lt;\/td&gt;\n                    &lt;\/tr&gt;\n                &lt;\/g:each&gt;\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n\n        &lt;div class=\"pagination\"&gt;\n            &lt;g:paginate total=\"${orderCount}\" max=\"10\"\/&gt;\n        &lt;\/div&gt;\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-22\">\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u7d71\u5408\u306e\u30dd\u30a4\u30f3\u30c8<\/h3>\n\n\n\n<p><strong>1. GORM\u3092\u6d3b\u7528\u3057\u305f\u30c7\u30fc\u30bf\u30a2\u30af\u30bb\u30b9<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u30b5\u30fc\u30d3\u30b9\u30af\u30e9\u30b9\u3067\u306eGORM\u6d3b\u7528\u4f8b\n@Service\n@Transactional\nclass OrderService {\n    def listOrders(Map params) {\n        Order.createCriteria().list(params) {\n            if (params.status) {\n                eq('status', params.status)\n            }\n            if (params.dateFrom) {\n                ge('orderDate', params.dateFrom)\n            }\n            if (params.dateTo) {\n                le('orderDate', params.dateTo)\n            }\n            order('orderDate', 'desc')\n        }\n    }\n\n    def getOrder(Long id) {\n        Order.get(id)\n    }\n\n    @Transactional\n    def createOrder(OrderCommand cmd) {\n        def order = new Order(\n            customer: Customer.get(cmd.customerId),\n            orderDate: new Date(),\n            status: OrderStatus.PENDING\n        )\n\n        cmd.items.each { itemCmd -&gt;\n            order.addToItems(\n                product: Product.get(itemCmd.productId),\n                quantity: itemCmd.quantity,\n                unitPrice: itemCmd.unitPrice\n            )\n        }\n\n        order.calculateTotal()\n        order.save(flush: true)\n        order\n    }\n}<\/pre>\n\n\n\n<p><strong>2. \u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u30c6\u30af\u30cb\u30c3\u30af<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ N+1\u554f\u984c\u306e\u89e3\u6c7a\ndef getOrdersWithDetails() {\n    Order.createCriteria().list {\n        createAlias('customer', 'c')\n        createAlias('items', 'i')\n        createAlias('i.product', 'p')\n\n        fetchMode('customer', FetchMode.JOIN)\n        fetchMode('items', FetchMode.JOIN)\n        fetchMode('i.product', FetchMode.JOIN)\n\n        resultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY)\n    }\n}\n\n\/\/ \u30d0\u30c3\u30c1\u51e6\u7406\u306e\u6700\u9069\u5316\n@Transactional\ndef processBulkOrders() {\n    def batchSize = 50\n    Order.withSession { session -&gt;\n        Order.findAllByStatus(OrderStatus.PENDING).eachWithIndex { order, index -&gt;\n            order.process()\n            if (index &gt; 0 &amp;&amp; index % batchSize == 0) {\n                session.flush()\n                session.clear()\n            }\n        }\n    }\n}<\/pre>\n\n\n\n<p><strong>3. \u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u8a2d\u8a08\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u6226\u7565<\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">static mapping = {\n    \/\/ \u8907\u5408\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\n    table 'orders'\n    orderNumber index: true\n    customer index: true\n    orderDate index: true\n\n    \/\/ \u8907\u5408\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\n    index([orderNumber: 'asc', orderDate: 'desc'])\n}<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li>\u30ad\u30e3\u30c3\u30b7\u30e5\u6226\u7565<\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ 2\u6b21\u30ad\u30e3\u30c3\u30b7\u30e5\u306e\u8a2d\u5b9a\nstatic mapping = {\n    cache usage: 'read-write', include: 'non-lazy'\n}\n\n\/\/ \u30b5\u30fc\u30d3\u30b9\u3067\u306e\u30ad\u30e3\u30c3\u30b7\u30e5\u5229\u7528\n@Cacheable('orders')\ndef getOrderSummary(Long orderId) {\n    \/\/ \u30ad\u30e3\u30c3\u30b7\u30e5\u3055\u308c\u308b\u51e6\u7406\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-23\">\u672c\u756a\u74b0\u5883\u3067\u306e\u904b\u7528\u3068\u30c7\u30d7\u30ed\u30a4<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-24\">\u6027\u80fd\u30c1\u30e5\u30fc\u30cb\u30f3\u30b0\u306e\u30dd\u30a4\u30f3\u30c8<\/h3>\n\n\n\n<p><strong>1. \u30e1\u30e2\u30ea\u6700\u9069\u5316<\/strong><\/p>\n\n\n\n<p>JVM\u306e\u8a2d\u5b9a\u4f8b\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u30d7\u30ed\u30c0\u30af\u30b7\u30e7\u30f3\u74b0\u5883\u7528\u306eJVM\u8a2d\u5b9a\nJAVA_OPTS=\"-Xms2g -Xmx4g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m\"\n\n# GC\u8a2d\u5b9a\nJAVA_OPTS=\"$JAVA_OPTS -XX:+UseG1GC -XX:MaxGCPauseMillis=200\"\n\n# GC\u30ed\u30b0\u8a2d\u5b9a\nJAVA_OPTS=\"$JAVA_OPTS -Xlog:gc*:file=gc.log:time,uptime:filecount=5,filesize=100m\"<\/pre>\n\n\n\n<p><strong>2. \u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ DataSource\u306e\u8a2d\u5b9a\ndataSource {\n    pooled = true\n    jmxExport = true\n    driverClassName = \"org.postgresql.Driver\"\n\n    properties {\n        \/\/ \u30b3\u30cd\u30af\u30b7\u30e7\u30f3\u30d7\u30fc\u30eb\u8a2d\u5b9a\n        maxActive = 50\n        maxIdle = 25\n        minIdle = 5\n        initialSize = 5\n        minEvictableIdleTimeMillis = 60000\n        timeBetweenEvictionRunsMillis = 60000\n        maxWait = 10000\n\n        \/\/ \u30d7\u30ea\u30da\u30a2\u30c9\u30b9\u30c6\u30fc\u30c8\u30e1\u30f3\u30c8\u30ad\u30e3\u30c3\u30b7\u30e5\n        maxOpenPreparedStatements = 100\n\n        \/\/ \u30d0\u30c3\u30c1\u51e6\u7406\u306e\u6700\u9069\u5316\n        reWriteBatchedInserts = true\n    }\n}\n\n\/\/ \u30af\u30a8\u30ea\u30ad\u30e3\u30c3\u30b7\u30e5\u306e\u8a2d\u5b9a\nhibernate {\n    cache {\n        queries = true\n        use_second_level_cache = true\n        use_query_cache = true\n        region.factory_class = 'org.hibernate.cache.ehcache.EhCacheRegionFactory'\n    }\n}<\/pre>\n\n\n\n<p><strong>3. \u30ad\u30e3\u30c3\u30b7\u30e5\u6226\u7565\u306e\u5b9f\u88c5<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u30b5\u30fc\u30d3\u30b9\u30ec\u30d9\u30eb\u306e\u30ad\u30e3\u30c3\u30b7\u30e5\u5b9f\u88c5\n@Service\nclass ProductService {\n    @Cacheable(\"products\")\n    List&lt;Product&gt; listActiveProducts() {\n        Product.findAllByActive(true)\n    }\n\n    @CacheEvict(value = \"products\", allEntries = true)\n    void updateProduct(Product product) {\n        product.save(flush: true)\n    }\n}\n\n\/\/ \u30d3\u30e5\u30fc\u30ad\u30e3\u30c3\u30b7\u30e5\u306e\u5b9f\u88c5\n&lt;%-- _product.gsp --%&gt;\n&lt;cache:block key=\"product_${product.id}\"&gt;\n    &lt;div class=\"product-card\"&gt;\n        &lt;h3&gt;${product.name}&lt;\/h3&gt;\n        &lt;p&gt;${product.description}&lt;\/p&gt;\n        &lt;span class=\"price\"&gt;\uffe5${product.price}&lt;\/span&gt;\n    &lt;\/div&gt;\n&lt;\/cache:block&gt;<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-25\">\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5bfe\u7b56\u306e\u5b9f\u88c5<\/h3>\n\n\n\n<p><strong>1. Spring Security\u306e\u7d71\u5408<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ build.gradle\u306e\u4f9d\u5b58\u95a2\u4fc2\ndependencies {\n    compile 'org.grails.plugins:spring-security-core:5.0.0'\n    compile 'org.grails.plugins:spring-security-jwt:2.0.0'\n}\n\n\/\/ \u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u8a2d\u5b9a\ngrails {\n    plugin {\n        springsecurity {\n            userLookup {\n                userDomainClassName = 'com.example.User'\n                authorityJoinClassName = 'com.example.UserRole'\n            }\n            authority {\n                className = 'com.example.Role'\n            }\n            controllerAnnotations {\n                staticRules = [\n                    [pattern: '\/',               access: ['permitAll']],\n                    [pattern: '\/error',          access: ['permitAll']],\n                    [pattern: '\/api\/**',         access: ['isFullyAuthenticated()']],\n                    [pattern: '\/admin\/**',       access: ['ROLE_ADMIN']],\n                    [pattern: '\/swagger-ui\/**',  access: ['permitAll']]\n                ]\n            }\n            password {\n                algorithm = 'bcrypt'\n                encodeHashAsBase64 = true\n                bcrypt {\n                    logrounds = 10\n                }\n            }\n        }\n    }\n}<\/pre>\n\n\n\n<p><strong>2. XSS\u5bfe\u7b56\u3068CSRF\u4fdd\u8b77<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ XSS\u5bfe\u7b56\u306e\u30d5\u30a3\u30eb\u30bf\u30fc\u5b9f\u88c5\n@Component\nclass XssFilter extends OncePerRequestFilter {\n    @Override\n    protected void doFilterInternal(HttpServletRequest request, \n                                  HttpServletResponse response,\n                                  FilterChain filterChain) {\n        filterChain.doFilter(new XssRequestWrapper(request), response)\n    }\n}\n\n\/\/ CSRF\u30c8\u30fc\u30af\u30f3\u306e\u5b9f\u88c5\n&lt;g:form action=\"save\"&gt;\n    &lt;g:hiddenField name=\"${request._csrf.parameterName}\" \n                   value=\"${request._csrf.token}\"\/&gt;\n    &lt;!-- \u30d5\u30a9\u30fc\u30e0\u8981\u7d20 --&gt;\n&lt;\/g:form&gt;<\/pre>\n\n\n\n<p><strong>3. \u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30d8\u30c3\u30c0\u30fc\u306e\u8a2d\u5b9a<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ application.yml\u3067\u306e\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30d8\u30c3\u30c0\u30fc\u8a2d\u5b9a\ngrails:\n    plugin:\n        springsecurity:\n            headers:\n                frameOptions: DENY\n                contentSecurityPolicy: \"default-src 'self'\"\n                xssProtection: '1; mode=block'\n                contentTypeOptions: NOSNIFF\n                referrerPolicy: 'strict-origin-when-cross-origin'<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-26\">\u7d99\u7d9a\u7684\u30c7\u30ea\u30d0\u30ea\u30fc\u306e\u69cb\u7bc9<\/h3>\n\n\n\n<p><strong>1. Jenkins\u30d1\u30a4\u30d7\u30e9\u30a4\u30f3\u306e\u5b9f\u88c5<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Jenkinsfile\npipeline {\n    agent any\n\n    environment {\n        GRAILS_ENV = 'production'\n    }\n\n    stages {\n        stage('Build') {\n            steps {\n                sh '.\/gradlew clean assemble'\n            }\n        }\n\n        stage('Test') {\n            steps {\n                sh '.\/gradlew test integrationTest'\n            }\n            post {\n                always {\n                    junit '**\/build\/test-results\/**\/*.xml'\n                }\n            }\n        }\n\n        stage('Code Quality') {\n            steps {\n                sh '.\/gradlew codenarc'\n                recordIssues(tools: [\n                    codenarcParser(pattern: '**\/build\/reports\/codenarc\/*.xml')\n                ])\n            }\n        }\n\n        stage('Deploy') {\n            when {\n                branch 'main'\n            }\n            steps {\n                sh '''\n                    .\/gradlew war\n                    scp build\/libs\/myapp.war user@production:\/apps\/\n                    ssh user@production \"deploy-script.sh\"\n                '''\n            }\n        }\n    }\n}<\/pre>\n\n\n\n<p><strong>2. Docker\u5316\u3068Kubernetes\u5bfe\u5fdc<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Dockerfile\nFROM adoptopenjdk\/openjdk11:alpine-jre\n\nENV GRAILS_ENV=production\nENV JAVA_OPTS=\"-Xms2g -Xmx4g -XX:MetaspaceSize=256m\"\n\nCOPY build\/libs\/myapp.war \/app\/myapp.war\nWORKDIR \/app\n\nEXPOSE 8080\nENTRYPOINT [\"java\", \"-jar\", \"myapp.war\"]<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># kubernetes\/deployment.yaml\napiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: grails-app\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: grails-app\n  template:\n    metadata:\n      labels:\n        app: grails-app\n    spec:\n      containers:\n      - name: grails-app\n        image: myapp:latest\n        ports:\n        - containerPort: 8080\n        resources:\n          requests:\n            memory: \"2Gi\"\n            cpu: \"500m\"\n          limits:\n            memory: \"4Gi\"\n            cpu: \"1000m\"\n        readinessProbe:\n          httpGet:\n            path: \/health\n            port: 8080\n        livenessProbe:\n          httpGet:\n            path: \/health\n            port: 8080<\/pre>\n\n\n\n<p><strong>3. \u30e2\u30cb\u30bf\u30ea\u30f3\u30b0\u3068\u30ed\u30b0\u7ba1\u7406<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30e1\u30c8\u30ea\u30af\u30b9\u306e\u5b9f\u88c5\n@Component\nclass MetricsService {\n    private final MeterRegistry registry\n\n    MetricsService(MeterRegistry registry) {\n        this.registry = registry\n    }\n\n    void recordApiCall(String endpoint, long duration) {\n        registry.timer(\"api.calls\", \n            \"endpoint\", endpoint).record(duration, TimeUnit.MILLISECONDS)\n    }\n}\n\n\/\/ \u30ed\u30b0\u8a2d\u5b9a\nlog4j = {\n    appenders {\n        console name: 'stdout', layout: pattern(\n            conversionPattern: '%d [%t] %-5p %c{1} - %m%n'\n        )\n\n        rollingFile name: 'applicationLog',\n            maxFileSize: '100MB',\n            file: '\/var\/log\/myapp\/application.log',\n            layout: pattern(\n                conversionPattern: '%d [%t] %-5p %c{1} - %m%n'\n            )\n    }\n\n    root {\n        error 'stdout', 'applicationLog'\n    }\n\n    error 'org.codehaus.groovy.grails.web.servlet',\n          'org.codehaus.groovy.grails.web.pages'\n\n    debug 'grails.app.controllers',\n          'grails.app.services',\n          'grails.app.domain'\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-27\">\u307e\u3068\u3081\uff1aGrails\u63a1\u7528\u306e\u5224\u65ad\u57fa\u6e96<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-28\">\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306b\u9069\u3057\u305f\u6280\u8853\u9078\u5b9a\u306e\u30dd\u30a4\u30f3\u30c8<\/h3>\n\n\n\n<p><strong>1. \u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u7279\u6027\u306b\u3088\u308b\u8a55\u4fa1\u30de\u30c8\u30ea\u30c3\u30af\u30b9<\/strong><\/p>\n\n\n<div id=\"id-7c2d5415-b121-47e3-a416-f6421389c00a\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u8a55\u4fa1\u9805\u76ee<\/th><th>Grails\u9069\u5408\u5ea6<\/th><th>\u5224\u65ad\u306e\u30dd\u30a4\u30f3\u30c8<\/th><\/tr><\/thead><tbody><tr><td>\u958b\u767a\u901f\u5ea6\u91cd\u8996<\/td><td>\u25ce<\/td><td>\u2022 \u898f\u7d04\u30d9\u30fc\u30b9\u306e\u958b\u767a<br>\u2022 \u30b9\u30ad\u30e3\u30d5\u30a9\u30fc\u30eb\u30c7\u30a3\u30f3\u30b0\u6a5f\u80fd<br>\u2022 \u30d7\u30e9\u30b0\u30a4\u30f3\u30a8\u30b3\u30b7\u30b9\u30c6\u30e0<\/td><\/tr><tr><td>\u30b9\u30b1\u30fc\u30e9\u30d3\u30ea\u30c6\u30a3<\/td><td>\u3007<\/td><td>\u2022 \u30de\u30a4\u30af\u30ed\u30b5\u30fc\u30d3\u30b9\u5bfe\u5fdc<br>\u2022 \u30af\u30e9\u30a6\u30c9\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0\u5bfe\u5fdc<br>\u2022 \u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30c1\u30e5\u30fc\u30cb\u30f3\u30b0\u53ef\u80fd<\/td><\/tr><tr><td>\u4fdd\u5b88\u6027<\/td><td>\u25ce<\/td><td>\u2022 \u30b3\u30fc\u30c9\u91cf\u306e\u5c11\u306a\u3055<br>\u2022 \u660e\u78ba\u306a\u898f\u7d04<br>\u2022 \u30c6\u30b9\u30c8\u5bb9\u6613\u6027<\/td><\/tr><tr><td>\u5b66\u7fd2\u30b3\u30b9\u30c8<\/td><td>\u25b3<\/td><td>\u2022 Groovy\u7fd2\u5f97\u5fc5\u8981<br>\u2022 \u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af\u72ec\u81ea\u306e\u898f\u7d04\u7406\u89e3\u5fc5\u8981<br>\u2022 Java\u77e5\u8b58\u306e\u6d41\u7528\u53ef\u80fd<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<p><strong>2. \u30e6\u30fc\u30b9\u30b1\u30fc\u30b9\u5225\u9069\u5408\u6027\u8a55\u4fa1<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">graph TD\n    A[\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u958b\u59cb] --&gt; B{\u958b\u767a\u671f\u9593}\n    B --&gt;|3\u30f6\u6708\u4ee5\u5185| C[Grails\u63a8\u5968]\n    B --&gt;|6\u30f6\u6708\u4ee5\u4e0a| D{\u30c1\u30fc\u30e0\u898f\u6a21}\n    D --&gt;|10\u4eba\u4ee5\u4e0b| E[Grails\u691c\u8a0e\u53ef]\n    D --&gt;|10\u4eba\u4ee5\u4e0a| F{\u65e2\u5b58\u8cc7\u7523}\n    F --&gt;|Java\u8cc7\u7523\u591a| G[Spring Boot\u63a8\u5968]\n    F --&gt;|\u65b0\u898f\u958b\u767a| H[Grails\/Spring Boot\u9078\u629e\u53ef]\n\n    C --&gt; I{\u8981\u4ef6}\n    I --&gt;|\u30d7\u30ed\u30c8\u30bf\u30a4\u30d7| J[Grails\u6700\u9069]\n    I --&gt;|\u672c\u756a\u7a3c\u50cd| K[\u8981\u4ef6\u7cbe\u67fb]<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-29\">\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u898f\u6a21\u5225\u306e\u63a1\u7528\u5224\u65ad\u57fa\u6e96<\/h3>\n\n\n\n<p><strong>1. \u5c0f\u898f\u6a21\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\uff08\u958b\u767a\u671f\u95933\u30f6\u6708\u4ee5\u5185\u3001\u30c1\u30fc\u30e05\u4eba\u4ee5\u4e0b\uff09<\/strong><\/p>\n\n\n\n<p>\u63a8\u5968\u5ea6\uff1a\u25ce\uff08\u5f37\u304f\u63a8\u5968\uff09<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30e1\u30ea\u30c3\u30c8<\/li>\n\n\n\n<li>\u7d20\u65e9\u3044\u30d7\u30ed\u30c8\u30bf\u30a4\u30d4\u30f3\u30b0<\/li>\n\n\n\n<li>\u5c11\u306a\u3044\u30b3\u30fc\u30c9\u91cf<\/li>\n\n\n\n<li>\u7d71\u5408\u6e08\u307f\u306e\u6a5f\u80fd\u30bb\u30c3\u30c8<\/li>\n<\/ul>\n\n\n\n<p>\u63a1\u7528\u306e\u305f\u3081\u306e\u524d\u63d0\u6761\u4ef6\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\u25a1 \u30c1\u30fc\u30e0\u304cGroovy\u7fd2\u5f97\u306b\u524d\u5411\u304d\n\u25a1 \u30d7\u30ed\u30c8\u30bf\u30a4\u30d7\u304b\u3089\u672c\u756a\u307e\u3067\u306e\u671f\u9593\u304c\u77ed\u3044\n\u25a1 \u30b7\u30f3\u30d7\u30eb\u306aCRUD\u64cd\u4f5c\u304c\u4e2d\u5fc3<\/pre>\n\n\n\n<p><strong>2. \u4e2d\u898f\u6a21\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\uff08\u958b\u767a\u671f\u95933-6\u30f6\u6708\u3001\u30c1\u30fc\u30e05-10\u4eba\uff09<\/strong><\/p>\n\n\n\n<p>\u63a8\u5968\u5ea6\uff1a\u3007\uff08\u6761\u4ef6\u4ed8\u304d\u63a8\u5968\uff09<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u691c\u8a0e\u30dd\u30a4\u30f3\u30c8<\/li>\n\n\n\n<li>\u30c1\u30fc\u30e0\u306eGroovy\/Grails\u7d4c\u9a13<\/li>\n\n\n\n<li>\u30de\u30a4\u30af\u30ed\u30b5\u30fc\u30d3\u30b9\u306e\u8981\u5426<\/li>\n\n\n\n<li>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u8981\u4ef6<\/li>\n<\/ul>\n\n\n\n<p>\u63a1\u7528\u5224\u65ad\u30c1\u30a7\u30c3\u30af\u30ea\u30b9\u30c8\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\u25a1 \u30c1\u30fc\u30e0\u5185\u306bGrails\u7d4c\u9a13\u8005\u304c\u3044\u308b\n\u25a1 \u30b9\u30b1\u30fc\u30e9\u30d3\u30ea\u30c6\u30a3\u8981\u4ef6\u304c\u660e\u78ba\n\u25a1 \u30c7\u30fc\u30bf\u30e2\u30c7\u30eb\u306e\u8907\u96d1\u6027\u304c\u4e2d\u7a0b\u5ea6\n\u25a1 CI\/CD\u74b0\u5883\u306e\u6574\u5099\u304c\u53ef\u80fd<\/pre>\n\n\n\n<p><strong>3. \u5927\u898f\u6a21\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\uff08\u958b\u767a\u671f\u95936\u30f6\u6708\u4ee5\u4e0a\u3001\u30c1\u30fc\u30e010\u4eba\u4ee5\u4e0a\uff09<\/strong><\/p>\n\n\n\n<p>\u63a8\u5968\u5ea6\uff1a\u25b3\uff08\u614e\u91cd\u306b\u5224\u65ad\uff09<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u8003\u616e\u4e8b\u9805<\/li>\n\n\n\n<li>\u30c1\u30fc\u30e0\u9593\u306e\u9023\u643a\u65b9\u6cd5<\/li>\n\n\n\n<li>\u958b\u767a\u6a19\u6e96\u306e\u78ba\u7acb<\/li>\n\n\n\n<li>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30c1\u30e5\u30fc\u30cb\u30f3\u30b0<\/li>\n<\/ul>\n\n\n\n<p>\u8a55\u4fa1\u9805\u76ee\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\u25a1 \u30de\u30a4\u30af\u30ed\u30b5\u30fc\u30d3\u30b9\u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3\u306e\u63a1\u7528\n\u25a1 \u30c1\u30fc\u30e0\u9593\u306e\u6280\u8853\u6a19\u6e96\u5316\n\u25a1 \u30e2\u30cb\u30bf\u30ea\u30f3\u30b0\/\u30ed\u30b0\u53ce\u96c6\u306e\u6574\u5099\n\u25a1 \u30b9\u30b1\u30fc\u30e9\u30d3\u30ea\u30c6\u30a3\u8981\u4ef6\u306e\u660e\u78ba\u5316<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-30\">\u4eca\u5f8c\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u30a2\u30c3\u30d7\u3068\u5c55\u671b<\/h3>\n\n\n\n<p><strong>1. Grails 6.0\u306e\u4e3b\u8981\u306a\u6539\u5584\u70b9<\/strong><\/p>\n\n\n<div id=\"id-18bbaae3-2b3c-4e72-abff-8ba35fabd98b\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u5206\u91ce<\/th><th>\u6539\u5584\u5185\u5bb9<\/th><th>\u5f71\u97ff\u5ea6<\/th><\/tr><\/thead><tbody><tr><td>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9<\/td><td>\u2022 JIT\u6700\u9069\u5316\u306e\u5f37\u5316<br>\u2022 \u30e1\u30e2\u30ea\u4f7f\u7528\u91cf\u306e\u524a\u6e1b<\/td><td>\u5927<\/td><\/tr><tr><td>\u958b\u767a\u751f\u7523\u6027<\/td><td>\u2022 DevTools\u306e\u5f37\u5316<br>\u2022 IDE\u30b5\u30dd\u30fc\u30c8\u306e\u6539\u5584<\/td><td>\u4e2d<\/td><\/tr><tr><td>\u30af\u30e9\u30a6\u30c9\u5bfe\u5fdc<\/td><td>\u2022 Kubernetes\u7d71\u5408\u306e\u5f37\u5316<br>\u2022 \u30af\u30e9\u30a6\u30c9\u30cd\u30a4\u30c6\u30a3\u30d6\u6a5f\u80fd<\/td><td>\u5927<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<p><strong>2. \u6280\u8853\u30c8\u30ec\u30f3\u30c9\u3068\u306e\u6574\u5408\u6027<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u5c06\u6765\u7684\u306a\u958b\u767a\u30e2\u30c7\u30eb\u306e\u4f8b\n@MicroserviceApi\nclass ProductService {\n    @CircuitBreaker(fallbackMethod = \"getDefaultProduct\")\n    @Cacheable(\"products\")\n    Product getProduct(String id) {\n        \/\/ \u30de\u30a4\u30af\u30ed\u30b5\u30fc\u30d3\u30b9\u5b9f\u88c5\n    }\n\n    @GraalVMNative\n    void processOrder(Order order) {\n        \/\/ \u30cd\u30a4\u30c6\u30a3\u30d6\u30b3\u30f3\u30d1\u30a4\u30eb\u5bfe\u5fdc\u51e6\u7406\n    }\n}<\/pre>\n\n\n\n<p><strong>3. \u63a1\u7528\u6642\u306e\u63a8\u5968\u30a2\u30d7\u30ed\u30fc\u30c1<\/strong><\/p>\n\n\n\n<p>\u6bb5\u968e\u7684\u306a\u5c0e\u5165\u6226\u7565\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u8a55\u4fa1\u30d5\u30a7\u30fc\u30ba\n<ul class=\"wp-block-list\">\n<li>\u30d7\u30ed\u30c8\u30bf\u30a4\u30d7\u958b\u767a<\/li>\n\n\n\n<li>\u30c1\u30fc\u30e0\u30c8\u30ec\u30fc\u30cb\u30f3\u30b0<\/li>\n\n\n\n<li>\u6280\u8853\u691c\u8a3c<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u521d\u671f\u5c0e\u5165\u30d5\u30a7\u30fc\u30ba\n<ul class=\"wp-block-list\">\n<li>\u5c0f\u898f\u6a21\u6a5f\u80fd\u304b\u3089\u958b\u59cb<\/li>\n\n\n\n<li>CI\/CD\u6574\u5099<\/li>\n\n\n\n<li>\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0\u69cb\u7bc9<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u672c\u683c\u5c55\u958b\u30d5\u30a7\u30fc\u30ba\n<ul class=\"wp-block-list\">\n<li>\u6bb5\u968e\u7684\u306a\u6a5f\u80fd\u79fb\u884c<\/li>\n\n\n\n<li>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316<\/li>\n\n\n\n<li>\u904b\u7528\u4f53\u5236\u306e\u78ba\u7acb<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p>\u6700\u7d42\u5224\u65ad\u306e\u305f\u3081\u306e\u30c1\u30a7\u30c3\u30af\u30ea\u30b9\u30c8\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u8981\u4ef6\n\u25a1 \u958b\u767a\u671f\u9593\u3068\u898f\u6a21\u306e\u9069\u5408\u6027\n\u25a1 \u30b9\u30b1\u30fc\u30e9\u30d3\u30ea\u30c6\u30a3\u8981\u4ef6\u306e\u660e\u78ba\u5316\n\u25a1 \u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u8981\u4ef6\u306e\u78ba\u8a8d\n\n\u6280\u8853\u8981\u4ef6\n\u25a1 Groovy\/Grails\u7fd2\u5f97\u8a08\u753b\n\u25a1 \u65e2\u5b58\u8cc7\u7523\u3068\u306e\u7d71\u5408\u65b9\u91dd\n\u25a1 \u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u8981\u4ef6\u306e\u5b9a\u7fa9\n\n\u30c1\u30fc\u30e0\u4f53\u5236\n\u25a1 \u958b\u767a\u8005\u306e\u30b9\u30ad\u30eb\u30bb\u30c3\u30c8\n\u25a1 \u30c8\u30ec\u30fc\u30cb\u30f3\u30b0\u8a08\u753b\n\u25a1 \u6280\u8853\u30b5\u30dd\u30fc\u30c8\u4f53\u5236\n\n\u904b\u7528\u4f53\u5236\n\u25a1 \u76e3\u8996\u30fb\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0\u8a08\u753b\n\u25a1 \u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u30fb\u707d\u5bb3\u5fa9\u65e7\u8a08\u753b\n\u25a1 \u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5bfe\u7b56<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u5224\u65ad\u57fa\u6e96\u306b\u57fa\u3065\u304d\u3001\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u7279\u6027\u3068\u30c1\u30fc\u30e0\u306e\u72b6\u6cc1\u3092\u7dcf\u5408\u7684\u306b\u8a55\u4fa1\u3059\u308b\u3053\u3068\u3067\u3001Grails\u63a1\u7528\u306e\u662f\u975e\u3092\u9069\u5207\u306b\u5224\u65ad\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Warning: Undefined array key &#8220;is_admin&#8221; in \/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/ &#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":{"0":"post-780","1":"post","2":"type-post","3":"status-publish","4":"format-standard","6":"category-uncategorized","7":"nothumb"},"_links":{"self":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts\/780","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=780"}],"version-history":[{"count":2,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts\/780\/revisions"}],"predecessor-version":[{"id":788,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts\/780\/revisions\/788"}],"wp:attachment":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=780"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=780"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=780"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}