{"id":50930,"date":"2025-03-07T00:00:00","date_gmt":"2025-03-07T08:00:00","guid":{"rendered":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/"},"modified":"2025-11-14T07:57:04","modified_gmt":"2025-11-14T15:57:04","slug":"building-a-fitness-tracker-web-using-spring-boot","status":"publish","type":"post","link":"https:\/\/www.griddb.net\/ja\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/","title":{"rendered":"Spring Boot \u3092\u4f7f\u7528\u3057\u3066\u30d5\u30a3\u30c3\u30c8\u30cd\u30b9\u30c8\u30e9\u30c3\u30ab\u30fc\u30a6\u30a7\u30d6\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u69cb\u7bc9\u3059\u308b"},"content":{"rendered":"<p>\u5065\u5eb7\u3067\u30d5\u30a3\u30c3\u30c8\u306a\u72b6\u614b\u306f\u3001\u79c1\u305f\u3061\u306e\u30e9\u30a4\u30d5\u30b9\u30bf\u30a4\u30eb\u306e\u4e00\u90e8\u3067\u3042\u308b\u3079\u304d\u3067\u3059\u3002\u5065\u5eb7\u7684\u306a\u30e9\u30a4\u30d5\u30b9\u30bf\u30a4\u30eb\u3092\u9001\u308b\u3053\u3068\u306f\u3001\u9577\u671f\u7684\u306a\u75c5\u6c17\u306e\u4e88\u9632\u306b\u5f79\u7acb\u3061\u307e\u3059\u3002\u305d\u306e\u305f\u3081\u306e\u65b9\u6cd5\u306e\u4e00\u3064\u306f\u3001\u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u306e\u8a18\u9332\u3092\u4ed8\u3051\u308b\u3053\u3068\u3067\u3059\u3002\u5177\u4f53\u7684\u306b\u306f\u3001\u904b\u52d5\u306e\u7a2e\u985e\u3001\u6642\u9593\u3001\u5fc3\u62cd\u6570\u306a\u3069\u3092\u8a18\u9332\u3057\u307e\u3059\u3002<\/p>\n<p>\u3053\u306e\u6280\u8853\u8a18\u4e8b\u3067\u306f\u3001Spring Boot\u3092\u4f7f\u7528\u3057\u3066\u30b7\u30f3\u30d7\u30eb\u306a\u30d5\u30a3\u30c3\u30c8\u30cd\u30b9\u8ffd\u8de1\u30a6\u30a7\u30d6\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u4f5c\u6210\u3059\u308b\u30d7\u30ed\u30bb\u30b9\u3092\u89e3\u8aac\u3057\u307e\u3059\u3002\u307e\u305a\u3001\u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u306e\u8a18\u9332\uff08\u3044\u3064\u3001\u4f55\u3092\u3057\u305f\u304b\u3001\u3069\u306e\u304f\u3089\u3044\u306e\u6642\u9593\u304b\uff09\u3092\u4fdd\u5b58\u3067\u304d\u308b\u30a6\u30a7\u30d6\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u4f5c\u6210\u3057\u307e\u3059\u3002\u307e\u305f\u3001\u5fc3\u62cd\u6570\u30c7\u30fc\u30bf\u3092\u6d3b\u7528\u3057\u3001\u6642\u9593\u7d4c\u904e\u306b\u4f34\u3046\u5fc3\u62cd\u6570\u3092\u793a\u3059\u30b9\u30bf\u30a4\u30ea\u30c3\u30b7\u30e5\u306a\u6298\u308c\u7dda\u30b0\u30e9\u30d5\u306b\u5909\u63db\u3057\u307e\u3059\u3002\u3053\u308c\u306f\u3001\u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u306b\u8996\u899a\u7684\u306a\u5fc3\u62cd\u6570\u3092\u4e0e\u3048\u308b\u3088\u3046\u306a\u3082\u306e\u3067\u3059\uff01\u3059\u3079\u3066\u306e\u30d7\u30ed\u30bb\u30b9\u3092\u5c0f\u3055\u306a\u30b9\u30c6\u30c3\u30d7\u306b\u5206\u89e3\u3057\u3001\u5404\u30b9\u30c6\u30c3\u30d7\u3092\u8a73\u7d30\u306b\u8aac\u660e\u3057\u3066\u3044\u304d\u307e\u3059\u3002<\/p>\n<h2>\u6a5f\u80fd\u3068\u6a5f\u80fd\u6027<\/h2>\n<p>\u3053\u306e\u30a6\u30a7\u30d6\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u304c\u30b5\u30dd\u30fc\u30c8\u3059\u308b\u6a5f\u80fd\u306f\u4ee5\u4e0b\u306e\u901a\u308a\u3067\u3059\uff1a<\/p>\n<ul>\n<li>\u30e6\u30fc\u30b6\u30fc\u306f\u3001\u8ddd\u96e2\u3001\u30ab\u30ed\u30ea\u30fc\u3001\u958b\u59cb\u6642\u9593\u3001\u7d42\u4e86\u6642\u9593\u306a\u3069\u306e\u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u60c5\u5831\u3092\u8a18\u9332\u3067\u304d\u307e\u3059\u3002<\/li>\n<li>\u30e6\u30fc\u30b6\u30fc\u306f\u5fc3\u62cd\u6570\u30c7\u30fc\u30bf\u3092\u7dda\u30b0\u30e9\u30d5\u3067\u78ba\u8a8d\u3067\u304d\u307e\u3059\u3002<\/li>\n<li>\u30e6\u30fc\u30b6\u30fc\u306f\u5fc3\u62cd\u6570\u30be\u30fc\u30f3\u306e\u5206\u6790\u3092\u78ba\u8a8d\u3067\u304d\u307e\u3059\u3002<\/li>\n<\/ul>\n<p>\u3053\u306e\u8a18\u4e8b\u3067\u306f\u4ee5\u4e0b\u306e\u5185\u5bb9\u306f\u63d0\u4f9b\u3057\u307e\u305b\u3093\uff1a* \u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3067\u5229\u7528\u53ef\u80fd\u306aRESTful\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u3002* \u30e6\u30fc\u30b6\u30fc\u767b\u9332\u3002\u30c7\u30e2\u76ee\u7684\u306e\u305f\u3081\u3001\u30c7\u30e2\u30e6\u30fc\u30b6\u30fc\u3092\u63d0\u4f9b\u3057\u307e\u3059\u3002* \u4eba\u6c17\u306e\u30d5\u30a3\u30c3\u30c8\u30cd\u30b9API\uff08Fitbit\u3001Google Fit\uff09\u3068\u306e\u7d71\u5408\u3002* \u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u306e\u7a2e\u985e\u3054\u3068\u306e\u5177\u4f53\u7684\u306a\u30e1\u30c8\u30ea\u30af\u30b9\u3002<\/p>\n<h2>\u30c7\u30fc\u30bf\u30e2\u30c7\u30eb<\/h2>\n<p>\u5b8c\u5168\u306a\u30c7\u30fc\u30bf\u30e2\u30c7\u30eb\u3002 <a href=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/03\/dbschema-fitness-tracking.png\"><img fetchpriority=\"high\" decoding=\"async\" src=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/03\/dbschema-fitness-tracking.png\" alt=\"\" width=\"965\" height=\"472\" class=\"aligncenter size-full wp-image-31394\" srcset=\"\/wp-content\/uploads\/2025\/03\/dbschema-fitness-tracking.png 965w, \/wp-content\/uploads\/2025\/03\/dbschema-fitness-tracking-300x147.png 300w, \/wp-content\/uploads\/2025\/03\/dbschema-fitness-tracking-768x376.png 768w, \/wp-content\/uploads\/2025\/03\/dbschema-fitness-tracking-600x293.png 600w\" sizes=\"(max-width: 965px) 100vw, 965px\" \/><\/a><\/p>\n<p>\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u5229\u7528\u3059\u308b\u30e6\u30fc\u30b6\u30fc\u306e\u60c5\u5831\u3092\u683c\u7d0d\u3059\u308b\u305f\u3081\u306e\u30c6\u30fc\u30d6\u30eb <code>users<\/code> \u3092\u4f5c\u6210\u3057\u307e\u3059\u3002\u73fe\u5728\u306f\u30e6\u30fc\u30b6\u30fc\u767b\u9332\u6a5f\u80fd\u304c\u306a\u3044\u305f\u3081\u3001\u30e6\u30fc\u30b6\u30fc\u306e\u30c6\u30fc\u30d6\u30eb\u306b\u306f\u540d\u524d\u3068\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u306e\u307f\u304c\u683c\u7d0d\u3055\u308c\u307e\u3059\u3002<\/p>\n<p>\u6b21\u306b\u3001\u30d5\u30a3\u30c3\u30c8\u30cd\u30b9\u30c7\u30fc\u30bf\u306e\u683c\u7d0d\u306b\u3064\u3044\u3066\u8aac\u660e\u3057\u307e\u3059\u3002\u30e1\u30a4\u30f3\u306e\u30c6\u30fc\u30d6\u30eb\u306f <code>workouts<\/code> \u3067\u3001\u30e6\u30fc\u30b6\u30fc\u304c\u5b9f\u65bd\u3057\u305f\u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u306e\u8a73\u7d30\u3092\u683c\u7d0d\u3057\u307e\u3059\u3002<\/p>\n<ul>\n<li><code>id<\/code> &#8211; \u5404\u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u306b\u4e00\u610f\u306eID\u3092\u5272\u308a\u5f53\u3066\u307e\u3059\u3002<\/li>\n<li><code>startTime<\/code> \u3068 <code>endTime<\/code> &#8211; \u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u306e\u958b\u59cb\u6642\u9593\u3068\u7d42\u4e86\u6642\u9593\u3092\u683c\u7d0d\u3057\u307e\u3059\u3002<\/li>\n<li><code>distance<\/code> &#8211; \u30e9\u30f3\u30cb\u30f3\u30b0\u3084\u30b5\u30a4\u30af\u30ea\u30f3\u30b0\u306a\u3069\u306e\u30b9\u30dd\u30fc\u30c4\u306f\u3001\u79fb\u52d5\u8ddd\u96e2\u3067\u6e2c\u5b9a\u3055\u308c\u307e\u3059\u3002<\/li>\n<li><code>calories<\/code> &#8211; \u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u4e2d\u306b\u6d88\u8cbb\u3057\u305f\u30ab\u30ed\u30ea\u30fc\u3092\u683c\u7d0d\u3057\u307e\u3059\u3002\u30ab\u30ed\u30ea\u30fc\u8a08\u7b97\u306e\u5f0f\u306f\u4f7f\u7528\u305b\u305a\u3001\u30e6\u30fc\u30b6\u30fc\u304c\u4efb\u610f\u306e\u5024\u3092\u5165\u529b\u3067\u304d\u307e\u3059\u3002<\/li>\n<li><code>duration<\/code> &#8211; \u30b7\u30b9\u30c6\u30e0\u304c\u81ea\u52d5\u7684\u306b\u5165\u529b\u3057\u307e\u3059\u3002<\/li>\n<\/ul>\n<p>\u5fc3\u62cd\u6570\u3092\u8a18\u9332\u3059\u308b\u305f\u3081\u306b\u3001<code>heartrate<\/code> \u3068\u3044\u3046\u540d\u524d\u306e\u30c6\u30fc\u30d6\u30eb\u3092\u4f5c\u6210\u3057\u307e\u3059\u3002\u3053\u306e\u30c6\u30fc\u30d6\u30eb\u306e\u5217\u306f\u6b21\u306e\u3068\u304a\u308a\u3067\u3059\uff1a<\/p>\n<ul>\n<li><code>id<\/code> &#8211; \u5404\u5fc3\u62cd\u6570\u30c7\u30fc\u30bf\u306b\u4e00\u610f\u306eID\u3092\u5272\u308a\u5f53\u3066\u307e\u3059\u3002<\/li>\n<li><code>workout_id<\/code> &#8211; \u3053\u306e\u30c7\u30fc\u30bf\u304c\u5c5e\u3059\u308b\u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u3092\u8b58\u5225\u3057\u307e\u3059\u3002<\/li>\n<li><code>value<\/code> &#8211; \u5fc3\u62cd\u6570\u306e\u5024\u3092\u683c\u7d0d\u3057\u307e\u3059\u3002<\/li>\n<li><code>time<\/code> &#8211; \u5fc3\u62cd\u6570\u306e\u5024\u304c\u5909\u66f4\u3055\u308c\u305f\u65e5\u6642\u3092\u683c\u7d0d\u3057\u307e\u3059\u3002<\/li>\n<\/ul>\n<p>\u30c7\u30e2\u3092\u7c21\u7d20\u5316\u3059\u308b\u305f\u3081\u3001\u5fc3\u62cd\u6570\u5024\u3092\u624b\u52d5\u3067\u5165\u529b\u3059\u308b\u30aa\u30d7\u30b7\u30e7\u30f3\u306f\u7528\u610f\u3057\u307e\u305b\u3093\u3002\u4ee3\u308f\u308a\u306b\u3001\u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u4e2d\u306b3\u5206\u3054\u3068\u306b\u30e9\u30f3\u30c0\u30e0\u306a\u5fc3\u62cd\u6570\u5024\u3092\u81ea\u52d5\u7684\u306b\u751f\u6210\u3059\u308b\u95a2\u6570\u3092\u4f5c\u6210\u3057\u307e\u3059\u3002<\/p>\n<h2>\u6280\u8853\u30b9\u30bf\u30c3\u30af<\/h2>\n<ul>\n<li>\n<p>Spring Boot\uff1a \u30b9\u30bf\u30f3\u30c9\u30a2\u30ed\u30f3\u3067\u751f\u7523\u74b0\u5883\u5411\u3051\u306eSpring\u30d9\u30fc\u30b9\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u6700\u5c0f\u9650\u306e effort \u3067\u958b\u767a\u3059\u308b\u305f\u3081\u306e\u30aa\u30fc\u30d7\u30f3\u30bd\u30fc\u30b9Java\u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af\u3002Spring Boot\u306f\u3001Spring Java\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0\u306e\u300c\u8a2d\u5b9a\u3088\u308a\u898f\u7d04\u300d\u62e1\u5f35\u6a5f\u80fd\u3067\u3001Spring\u30d9\u30fc\u30b9\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u958b\u767a\u6642\u306e\u8a2d\u5b9a\u306b\u95a2\u3059\u308b\u61f8\u5ff5\u3092\u6700\u5c0f\u9650\u306b\u6291\u3048\u308b\u3053\u3068\u3092\u76ee\u7684\u3068\u3057\u3066\u3044\u307e\u3059\u3002<\/p>\n<\/li>\n<li>\n<p>GridDB\uff1a \u30bf\u30a4\u30e0\u30b7\u30ea\u30fc\u30baIoT\u3068\u30d3\u30c3\u30b0\u30c7\u30fc\u30bf\u3092\u9ad8\u901f\u304b\u3064\u7c21\u5358\u306b\u51e6\u7406\u3067\u304d\u308b\u6b21\u4e16\u4ee3\u30aa\u30fc\u30d7\u30f3\u30bd\u30fc\u30b9\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3002<\/p>\n<\/li>\n<li>\n<p>Thymeleaf\uff1a Java XML\/XHTML\/HTML5\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u30a8\u30f3\u30b8\u30f3\u3067\u3001\u30a6\u30a7\u30d6\u74b0\u5883\u3068\u975e\u30a6\u30a7\u30d6\u74b0\u5883\u306e\u4e21\u65b9\u3067\u52d5\u4f5c\u3057\u307e\u3059\u3002MVC\u30d9\u30fc\u30b9\u306e\u30a6\u30a7\u30d6\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30d3\u30e5\u30fc\u5c64\u3067XHTML\/HTML5\u3092\u914d\u4fe1\u3059\u308b\u306e\u306b\u9069\u3057\u3066\u3044\u307e\u3059\u304c\u3001\u30aa\u30d5\u30e9\u30a4\u30f3\u74b0\u5883\u3067\u3082XML\u30d5\u30a1\u30a4\u30eb\u3092\u51e6\u7406\u3067\u304d\u307e\u3059\u3002Spring Framework\u3068\u306e\u5b8c\u5168\u306a\u7d71\u5408\u3092\u63d0\u4f9b\u3057\u307e\u3059\u3002<\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/echarts.apache.org\/en\/index.html\">Apache EChart<\/a>: Web\u30d9\u30fc\u30b9\u306e\u53ef\u8996\u5316\u3092\u8fc5\u901f\u306b\u69cb\u7bc9\u3059\u308b\u305f\u3081\u306e\u5ba3\u8a00\u578b\u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af\u3002Apache ECharts\u306f\u300120\u7a2e\u985e\u4ee5\u4e0a\u306e\u30c1\u30e3\u30fc\u30c8\u30bf\u30a4\u30d7\u3092\u6a19\u6e96\u3067\u63d0\u4f9b\u3057\u3001\u6570\u5341\u306e\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u3092\u7d44\u307f\u5408\u308f\u305b\u3066\u81ea\u7531\u306b\u4f7f\u7528\u3067\u304d\u307e\u3059\u3002<\/p>\n<\/li>\n<li>\n<p>Maven\uff1a Java\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u5411\u3051\u306b\u4e3b\u306b\u4f7f\u7528\u3055\u308c\u308b\u30d3\u30eb\u30c9\u81ea\u52d5\u5316\u30c4\u30fc\u30eb\u3002<\/p>\n<\/li>\n<li>\n<p>Docker\uff1a \u958b\u767a\u8005\u304c\u30b3\u30f3\u30c6\u30ca\u5316\u3055\u308c\u305f\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u69cb\u7bc9\u3001\u5c55\u958b\u3001\u5b9f\u884c\u3001\u66f4\u65b0\u3001\u7ba1\u7406\u3092\u53ef\u80fd\u306b\u3059\u308b\u30aa\u30fc\u30d7\u30f3\u30bd\u30fc\u30b9\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0\u3002<\/p>\n<\/li>\n<\/ul>\n<h2>\u958b\u767a\u74b0\u5883\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7<\/h2>\n<p>\u307e\u305a\u3001\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002<a href=\"https:\/\/jdk.java.net\/21\/\">Java 17 \u4ee5\u964d<\/a>, <a href=\"https:\/\/maven.apache.org\/download.cgi\">Maven 3.5+<\/a>, <a href=\"https:\/\/docs.docker.com\/engine\/install\/\">Docker \u30a8\u30f3\u30b8\u30f3<\/a>, \u30c6\u30ad\u30b9\u30c8\u30a8\u30c7\u30a3\u30bf\u30fc(<a href=\"https:\/\/spring.io\/guides\/gs\/intellij-idea\/\">\u30a4\u30f3\u30c6\u30ea\u30b8 IDEA<\/a>, or <a href=\"https:\/\/spring.io\/guides\/gs\/guides-with-vscode\/\">VSCode<\/a>).<\/p>\n<p>\u4e00\u304b\u3089\u59cb\u3081\u308b\u306b\u306f\u3001\u4ee5\u4e0b\u306e\u30ea\u30f3\u30af\u3092\u30af\u30ea\u30c3\u30af\u3057\u3066\u304f\u3060\u3055\u3044\u3002 <a href=\"https:\/\/start.spring.io\/\">Spring Initializr<\/a> Spring Web\u3001Thymeleaf\u3001Lombok\u3001\u304a\u3088\u3073Spring Boot DevTools\u306e\u4f9d\u5b58\u95a2\u4fc2\u3092\u8ffd\u52a0\u3057\u307e\u3059\u3002\u305d\u306e\u5f8c\u3001\u751f\u6210\u30dc\u30bf\u30f3\u3092\u30af\u30ea\u30c3\u30af\u3057\u3001ZIP\u30d5\u30a1\u30a4\u30eb\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3057\u307e\u3059\u3002\u30d5\u30a1\u30a4\u30eb\u3092\u89e3\u51cd\u3057\u3001\u304a\u597d\u307f\u306eIDE\u3067\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3092\u958b\u304d\u307e\u3059\u3002<\/p>\n<p>\u6b21\u306b\u3001\u5fc5\u8981\u306a\u4f9d\u5b58\u95a2\u4fc2\u3092\u8ffd\u52a0\u3057\u307e\u3059\u3002GridDB\u3068\u3084\u308a\u53d6\u308a\u3059\u308b\u305f\u3081\u3001\u6b21\u306e\u3088\u3046\u306bGridDB Java Client\u306e\u4f9d\u5b58\u95a2\u4fc2\u3092\u8ffd\u52a0\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\uff1a<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-html\">&lt;dependency&gt;\n  &lt;groupId&gt;com.github.griddb&lt;\/groupId&gt;\n  &lt;artifactId&gt;gridstore&lt;\/artifactId&gt;\n  &lt;version&gt;5.5.0&lt;\/version&gt;\n&lt;\/dependency&gt;<\/code><\/pre>\n<\/div>\n<p>\u5b8c\u5168\u306a <code>pom.xml<\/code> \u30d5\u30a1\u30a4\u30eb\u306f <a href=\"https:\/\/github.com\/alifruliarso\/springboot-fitness-tracking\/blob\/235ff7d514def6a11806ac880f528990d7bcda04\/pom.xml\">\u3053\u3061\u3089<\/a> \u304b\u3089\u78ba\u8a8d\u3067\u304d\u307e\u3059\u3002<\/p>\n<h2>\u4e3b\u8981\u6a5f\u80fd\u306e\u5b9f\u88c5<\/h2>\n<p>\u3053\u306e\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3067\u306f\u3001Controller-Service-Repository \uff08CSR\uff09 \u30d1\u30bf\u30fc\u30f3\u3092\u4f7f\u7528\u3057\u307e\u3059\u3002\u3053\u306e\u30d1\u30bf\u30fc\u30f3\u306f\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30ed\u30b8\u30c3\u30af\u3092\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u3001\u30b5\u30fc\u30d3\u30b9\u3001\u30ea\u30dd\u30b8\u30c8\u30ea\u306e3\u3064\u306e\u72ec\u7acb\u3057\u305f\u5c64\u306b\u5206\u5272\u3059\u308b\u3053\u3068\u3067\u3001\u95a2\u5fc3\u4e8b\u306e\u5206\u96e2\u3092\u4fc3\u9032\u3057\u307e\u3059\u3002\u5404\u5c64\u306f\u7279\u5b9a\u306e\u8cac\u4efb\u3092\u6301\u3061\u3001\u30b3\u30fc\u30c9\u306e\u7ba1\u7406\u304c\u5bb9\u6613\u306b\u306a\u308a\u307e\u3059\u3002<\/p>\n<p>Maven \u4f9d\u5b58\u95a2\u4fc2\u3092\u5b8c\u4e86\u3057\u305f\u5f8c\u3001\u4ee5\u4e0b\u306e\u30d5\u30a9\u30eb\u30c0\u30fc\u3092\u4f5c\u6210\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\uff1a<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-sh\">src\/main\/java\/com\/galapea\/techblog\/fitnesstracking\/\n\u251c\u2500\u2500 config\n\u251c\u2500\u2500 controller\n\u251c\u2500\u2500 entity\n\u251c\u2500\u2500 model\n\u2514\u2500\u2500 service<\/code><\/pre>\n<\/div>\n<p>GridDB\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u30d5\u30a3\u30c3\u30c8\u30cd\u30b9\u60c5\u5831\u3092\u4fdd\u5b58\u3059\u308b\u305f\u3081\u306b\u306f\u3001\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u30d5\u30a9\u30eb\u30c0\u30fc\u5185\u306b\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u30af\u30e9\u30b9\u3092\u5b9a\u7fa9\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002<\/p>\n<ul>\n<li>\u30e6\u30fc\u30b6\u30fc\u3092\u8868\u3059\u30c9\u30e1\u30a4\u30f3\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3092\u4ee5\u4e0b\u306e\u4f8b\u306e\u3088\u3046\u306b\u4f5c\u6210\u3057\u307e\u3059\uff1a<\/li>\n<\/ul>\n<div class=\"clipboard\">\n<pre><code class=\"language-java\">@Data\npublic class User {\n\n    @RowKey\n    String id;\n    String email;\n    String fullName;\n    Date createdAt;\n\n}<\/code><\/pre>\n<\/div>\n<ul>\n<li>\u30ef\u30fc\u30af\u30a2\u30a6\u30c8<\/li>\n<\/ul>\n<div class=\"clipboard\">\n<pre><code class=\"language-java\">@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class Workout {\n\n    @RowKey\n    public String id;\n    public String title;\n    public String type;\n    public String userId;\n    public Date startTime;\n    public Date endTime;\n    public Double distance;\n    public Double duration;\n    public Double calories;\n    public Date createdAt;\n\n}<\/code><\/pre>\n<\/div>\n<ul>\n<li>\u5fc3\u62cd\u6570<\/li>\n<\/ul>\n<div class=\"clipboard\">\n<pre><code class=\"language-java\">@Data\npublic class HeartRate {\n\n    @RowKey\n    String id;\n    Date timestamp;\n    double value;\n    String workoutId;\n\n}<\/code><\/pre>\n<\/div>\n<p>\u305d\u306e\u5f8c\u3001\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u63a5\u7d9a\u3092\u8a2d\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002Spring Boot\u306eGridDB\u81ea\u52d5\u8a2d\u5b9a\u304c\u5229\u7528\u3067\u304d\u306a\u3044\u305f\u3081\u3001<code>GridDBConfig<\/code>\u3092\u4f5c\u6210\u3057\u3066\u8a2d\u5b9a\u3092\u884c\u3044\u307e\u3059\u3002\u3053\u306e\u8a2d\u5b9a\u3067\u306f\u3001\u4ee5\u524d\u306b\u4f5c\u6210\u3057\u305f\u30a8\u30f3\u30c6\u30a3\u30c6\u30a3\u30af\u30e9\u30b9\u3092\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u5185\u306e\u30b3\u30f3\u30c6\u30ca\uff08\u30c6\u30fc\u30d6\u30eb\uff09\u3092\u8868\u3059\u305f\u3081\u306b\u4f7f\u7528\u3057\u3066\u3044\u307e\u3059\u3002<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-java\">@Configuration\npublic class GridDBConfig {\n\n    @Value(\"${GRIDDB_NOTIFICATION_MEMBER}\")\n    private String notificationMember;\n\n    @Value(\"${GRIDDB_CLUSTER_NAME}\")\n    private String clusterName;\n\n    @Value(\"${GRIDDB_USER}\")\n    private String user;\n\n    @Value(\"${GRIDDB_PASSWORD}\")\n    private String password;\n\n    @Bean\n    public GridStore gridStore() throws GSException {\n        Properties properties = new Properties();\n        properties.setProperty(\"notificationMember\", notificationMember);\n        properties.setProperty(\"clusterName\", clusterName);\n        properties.setProperty(\"user\", user);\n        properties.setProperty(\"password\", password);\n        GridStore store = GridStoreFactory.getInstance().getGridStore(properties);\n        return store;\n    }\n\n    @Bean\n    public Collection&lt;String, User> userCollection(GridStore gridStore) throws GSException {\n        Collection&lt;String, User> collection = gridStore.putCollection(AppConstant.USERS_CONTAINER, User.class);\n        collection.createIndex(\"email\");\n        return collection;\n    }\n\n    @Bean\n    public Collection&lt;String, HeartRate> heartRateCollection(GridStore gridStore) throws GSException {\n        Collection&lt;String, HeartRate> heartRateCollection = gridStore.putCollection(AppConstant.HEARTRATE_CONTAINER,\n                HeartRate.class);\n        heartRateCollection.createIndex(\"workoutId\");\n        return heartRatTo access GridDB, we need to get a GridStore instance using the GridStoreFactory. Connecting to GridDB cluster using the fixed list method, we need to specify the following required properties:\n\n1.  `notificationMember` A list of address and port pairs in cluster. It is used to connect to cluster which is configured with FIXED_LIST mode, and specified as follows. (Address1):(Port1),(Address2):(Port2),... This property cannot be specified with neither notificationAddress nor notificationProvider properties at the same time. This property is supported on version 2.9 or later.\n2.  `clusterName` A cluster name. It is used to verify whether it matches the cluster name assigned to the destination cluster. If it is omitted or an empty string is specified, cluster name verification is not performed.\n\n### Listing workout\neCollection;\n    }\n\n    @Bean\n    public Collection&lt;String, Workout> workoutCollection(GridStore gridStore) throws GSException {\n        Collection&lt;String, Workout> workoutCollection = gridStore.putCollection(AppConstant.WORKOUT_CONTAINER,\n                Workout.class);\n        workoutCollection.createIndex(\"userId\");\n        workoutCollection.createIndex(\"title\");\n        workoutCollection.createIndex(\"type\");\n        return workoutCollection;\n    }\n\n}<\/code><\/pre>\n<\/div>\n<p><a href=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/03\/list-workouts.png\"><img decoding=\"async\" src=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/03\/list-workouts.png\" alt=\"\" width=\"1297\" height=\"552\" class=\"aligncenter size-full wp-image-31396\" srcset=\"\/wp-content\/uploads\/2025\/03\/list-workouts.png 1297w, \/wp-content\/uploads\/2025\/03\/list-workouts-300x128.png 300w, \/wp-content\/uploads\/2025\/03\/list-workouts-1024x436.png 1024w, \/wp-content\/uploads\/2025\/03\/list-workouts-768x327.png 768w, \/wp-content\/uploads\/2025\/03\/list-workouts-600x255.png 600w\" sizes=\"(max-width: 1297px) 100vw, 1297px\" \/><\/a><\/p>\n<p>HTML\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8 <code>workouts.html<\/code> \u3092\u4f5c\u6210\u3057\u3001<code>th\uff1aeach<\/code> \u30c7\u30a3\u30ec\u30af\u30c6\u30a3\u30d6\u3092\u4f7f\u7528\u3057\u3066\u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u306e\u30ea\u30b9\u30c8\u3092\u8868\u793a\u3057\u307e\u3059\uff1a<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-html\">                    &lt;tbody class=\"table-group-divider\"&gt;\n                        &lt;tr th_each=\"workout : ${workouts}\"&gt;\n                            &lt;td id=\"user\" scope=\"row\" th_text=\"${workout.user} ? ${workout.user.fullName} : ''\"&gt;&lt;\/td&gt;\n                            &lt;th id=\"type\" scope=\"row\"&gt;[[${workout.type}]]&lt;\/th&gt;\n                            &lt;td&gt;\n                                &lt;a class=\"small\" th_href=\"@{\/heartrate\/{id}(id=${workout.id})}\"\n                                    th_text=\"${workout.title}\"&gt;&lt;\/a&gt;\n                            &lt;\/td&gt;\n                            &lt;th id=\"distance\" scope=\"row\"&gt;[[${workout.distance}]]&lt;\/th&gt;\n                            &lt;td id=\"startTime\" scope=\"row\"&gt;[[${workout.startTime}]]&lt;\/td&gt;\n                            &lt;th id=\"endTime\" scope=\"row\"&gt;[[${workout.endTime}]]&lt;\/th&gt;\n                            &lt;th id=\"duration\" scope=\"row\"&gt;[[${workout.durationText}]]&lt;\/th&gt;\n                        &lt;\/tr&gt;\n                    &lt;\/tbody&gt;<\/code><\/pre>\n<\/div>\n<p>\u305d\u306e\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306f\u3001\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u30af\u30e9\u30b9 <code>WorkoutController<\/code> \u306b\u3088\u3063\u3066\u547c\u3073\u51fa\u3055\u308c\u307e\u3059\uff1a<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-java\">@Controller\n@RequestMapping(\"\/workouts\")\n@RequiredArgsConstructor\npublic class WorkoutController {\n\n    private final WorkoutService workoutService;\n\n    private final UserService userService;\n\n    java.text.SimpleDateFormat formatter = new java.text.SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm\");\n\n    @GetMapping\n    String workouts(Model model) {\n        List&lt;workoutdto> workouts = workoutService.fetchAll();\n        workouts.forEach(workout -> {\n            try {\n                workout.setUser(userService.fetchOneById(workout.getUserId()));\n            }\n            catch (GSException e) {\n                e.printStackTrace();\n            }\n            String durationText = Helper.getDurationText(workout.getStartTime(), workout.getEndTime());\n            workout.setDurationText(durationText);\n        });\n        model.addAttribute(\"workouts\", workouts);\n        model.addAttribute(\"createWorkout\", new CreateWorkout());\n        return \"workouts\";\n    }&lt;\/workoutdto><\/code><\/pre>\n<\/div>\n<p>\u30e1\u30bd\u30c3\u30c9 <code>workouts\uff08\uff09<\/code> \u3067\u306f\u3001<code>WorkoutService<\/code> \u30af\u30e9\u30b9\u304b\u3089 <code>fetchAll\uff08\uff09<\/code> \u30e1\u30bd\u30c3\u30c9\u3092\u547c\u3073\u51fa\u3057\u307e\u3059\u3002\u305d\u306e\u5f8c\u3001\u30e6\u30fc\u30b6\u30fc\u30a4\u30f3\u30bf\u30fc\u30d5\u30a7\u30fc\u30b9\u306b\u623b\u3059\u524d\u306b\u3001\u30e6\u30fc\u30b6\u30fc\u306e\u30d5\u30eb\u30cd\u30fc\u30e0\u3092\u53d6\u5f97\u3057\u3001\u671f\u9593\u3092\u4eba\u9593\u304c\u8aad\u307f\u3084\u3059\u3044\u5f62\u5f0f\uff08\u4f8b\uff1a <code>1h 15m<\/code>\uff09\u306b\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u30c7\u30d5\u30a9\u30eb\u30c8\u306e <code>java.util.Date<\/code> \u5f62\u5f0f\u3067\u306f\u306a\u304f\u3001\u3053\u306e\u5f62\u5f0f\u3067\u8fd4\u3059\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u65b0\u3057\u3044\u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u3092\u4f5c\u6210\u3059\u308b\u305f\u3081\u306e\u6e96\u5099\u3068\u3057\u3066\u3001\u30d3\u30e5\u30fc\u306b\u65b0\u3057\u3044\u30e2\u30c7\u30eb\u3092\u8fd4\u3057\u307e\u3059\u3002n\u6b21\u306b\u3001\u30b5\u30fc\u30d3\u30b9\u30af\u30e9\u30b9\u3067GridDB\u304b\u3089\u30c7\u30fc\u30bf\u3092\u53d6\u5f97\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u3053\u3053\u3067\u306f\u3001<code>workout<\/code>\u30b3\u30ec\u30af\u30b7\u30e7\u30f3\u304b\u3089\u3059\u3079\u3066\u306e\u30ec\u30b3\u30fc\u30c9\u3092\u9078\u629e\u3059\u308bTQL\u30b9\u30c6\u30fc\u30c8\u30e1\u30f3\u30c8\u3092\u5b9f\u884c\u3059\u308b\u305f\u3081\u306e\u30af\u30a8\u30ea\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3092\u4f5c\u6210\u3057\u3001\u305d\u306e\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3092<code>WorkoutDto<\/code>\u306b\u30de\u30c3\u30d4\u30f3\u30b0\u3057\u307e\u3059\u3002<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-java\">public List&lt;workoutdto> fetchAll() {\n        List&lt;\/workoutdto>&lt;workoutdto> dtos = new ArrayList&lt;>(0);\n        try {\n            String tql = \"SELECT * FROM \" + AppConstant.WORKOUT_CONTAINER + \" ORDER BY createdAt DESC\";\n            Query&lt;workout> query = workoutCollection.query(tql);\n            RowSet&lt;\/workout>&lt;workout> rowSet = query.fetch();\n            while (rowSet.hasNext()) {\n                Workout row = rowSet.next();\n                WorkoutDto dto = WorkoutDto.builder()\n                    .id(row.id)\n                    .title(row.title)\n                    .type(WorkoutType.valueOf(row.type))\n                    .userId(row.userId)\n                    .startTime(row.startTime)\n                    .endTime(row.endTime)\n                    .distance(row.distance)\n                    .duration(row.duration)\n                    .calories(row.calories)\n                    .createdAt(row.createdAt)\n                    .build();\n                dtos.add(dto);\n            }\n        }\n        catch (GSException e) {\n            log.error(\"Error fetch all workouts\", e);\n        }\n        return dtos;\n    }&lt;\/workout>&lt;\/workoutdto><\/code><\/pre>\n<\/div>\n<h3>\u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u3092\u8a18\u9332\u3059\u308b<\/h3>\n<p><a href=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/03\/save-new-workout-scaled-1.png\"><img decoding=\"async\" src=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/03\/save-new-workout-scaled-1.png\" alt=\"\" width=\"441\" height=\"631\" class=\"aligncenter size-full wp-image-31397\" srcset=\"\/wp-content\/uploads\/2025\/03\/save-new-workout-scaled-1.png 441w, \/wp-content\/uploads\/2025\/03\/save-new-workout-scaled-1-210x300.png 210w\" sizes=\"(max-width: 441px) 100vw, 441px\" \/><\/a><\/p>\n<p>\u30e6\u30fc\u30b6\u30fc\u304c\u65b0\u3057\u3044\u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u3092\u8a18\u9332\u3057\u300c\u9001\u4fe1\u300d\u3092\u30af\u30ea\u30c3\u30af\u3059\u308b\u3068\u3001<code>WorkoutController<\/code> \u3067\u30d5\u30a9\u30fc\u30e0\u306e\u9001\u4fe1\u51e6\u7406\u3092\u4ee5\u4e0b\u306e\u30e1\u30bd\u30c3\u30c9\u3067\u51e6\u7406\u3057\u307e\u3059\uff1a<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-java\">@PostMapping(\"\/record\")\n    String recordWorkout(@ModelAttribute(\"createWorkout\") CreateWorkout createWorkout,\n            final BindingResult bindingResult, RedirectAttributes attributes, Model model) throws ParseException {\n        List&lt;user> users = userService.fetchAll();\n        java.util.Collections.shuffle(users);\n        java.util.Date startDate = formatter.parse(createWorkout.getStartTime());\n        java.util.Date endDate = formatter.parse(createWorkout.getEndTime());\n        LocalDateTime start = Helper.convertToLocalDateTime(startDate);\n        LocalDateTime end = Helper.convertToLocalDateTime(endDate);\n        if (end.isBefore(start)) {\n            bindingResult.rejectValue(\"endTime\", \"error.endTime\", \"End-date must be after Start-date!\");\n            attributes.addAttribute(\"hasErrors\", true);\n        }\n\n        if (bindingResult.hasErrors()) {\n            return workouts(model);\n        }\n\n        WorkoutDto workoutDto = WorkoutDto.builder()\n            .title(createWorkout.getTitle())\n            .type(WorkoutType.valueOf(createWorkout.getType()))\n            .userId(users.get(0).getId())\n            .distance(Double.parseDouble(createWorkout.getDistance()))\n            .startTime(startDate)\n            .endTime(endDate)\n            .calories(Double.parseDouble(createWorkout.getCalories()))\n            .build();\n        workoutService.create(workoutDto);\n        attributes.addFlashAttribute(\"message\", \"Workout recorded!\");\n        return \"redirect:\/workouts\";\n    }&lt;\/user><\/code><\/pre>\n<\/div>\n<p>\u30c7\u30e2\u76ee\u7684\u3067\u3001\u65e2\u5b58\u306e\u30e6\u30fc\u30b6\u30fc\u304b\u3089\u30e9\u30f3\u30c0\u30e0\u306b\u30e6\u30fc\u30b6\u30fc\u3092\u9078\u629e\u3057\u307e\u3059\u3002\u305d\u306e\u5f8c\u3001\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u306f<code>WorkoutService<\/code>\u306e<code>create<\/code>\u30e1\u30bd\u30c3\u30c9\u3092\u547c\u3073\u51fa\u3057\u307e\u3059\u3002\u3053\u306e\u30e1\u30bd\u30c3\u30c9\u5185\u3067\u3001\u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u306e\u6301\u7d9a\u6642\u9593\u3092\u8a08\u7b97\u3057\u307e\u3059\u3002\u884c\u3092\u633f\u5165\u3057\u305f\u5f8c\u3001<code>ApplicationEventPublisher<\/code>\u3092\u4f7f\u7528\u3057\u3066<code>WorkoutCreated<\/code>\u30a4\u30d9\u30f3\u30c8\u3092\u767a\u884c\u3057\u3001<code>HeartRateService<\/code>\u3067\u6d88\u8cbb\u3055\u308c\u307e\u3059\u3002<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-java\">public void create(WorkoutDto workoutDto) {\n        try {\n            Double duration = workoutDto.getDuration() == null ? 0.0 : workoutDto.getDuration();\n            if (workoutDto.getId() == null) {\n                workoutDto.setId(KeyGenerator.next(\"wk_\"));\n            }\n            Workout workout = Workout.builder()\n                .id(workoutDto.getId())\n                .title(workoutDto.getTitle())\n                .type(workoutDto.getType().name())\n                .userId(workoutDto.getUserId())\n                .startTime(workoutDto.getStartTime())\n                .endTime(workoutDto.getEndTime())\n                .distance(workoutDto.getDistance())\n                .duration(duration)\n                .calories(workoutDto.getCalories())\n                .createdAt(new java.util.Date())\n                .build();\n\n            if (workoutCollection.get(workoutDto.getId()) == null) {\n                workoutCollection.put(workoutDto.getId(), workout);\n                applicationEventPublisher.publishEvent(mapStructMapper.workoutToWorkoutCreated(workout));\n            }\n        }\n        catch (GSException e) {\n            log.error(\"Error create a workout\", e);\n        }\n    }<\/code><\/pre>\n<\/div>\n<p>\u3054\u5b58\u77e5\u306e\u901a\u308a\u3001\u6700\u521d\u306b\u5fc3\u62cd\u6570\u30c7\u30fc\u30bf\u3092\u8a18\u9332\u3059\u308b\u3068\u304a\u4f1d\u3048\u3057\u307e\u3057\u305f\u304c\u3001\u5165\u529b\u3059\u308b\u305f\u3081\u306e\u30a4\u30f3\u30bf\u30fc\u30d5\u30a7\u30fc\u30b9\u304c\u898b\u5f53\u305f\u308a\u307e\u305b\u3093\u3002\u3053\u306e\u30c1\u30e5\u30fc\u30c8\u30ea\u30a2\u30eb\u3067\u306f\u30a6\u30a7\u30a2\u30e9\u30d6\u30eb\u30c7\u30d0\u30a4\u30b9\u3068\u306e\u63a5\u7d9a\u3092\u884c\u308f\u306a\u3044\u305f\u3081\u3001\u5404\u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u3054\u3068\u306b\u30c0\u30df\u30fc\u306e\u5fc3\u62cd\u6570\u30c7\u30fc\u30bf\u3092\u4f5c\u6210\u3059\u308b\u3053\u3068\u306b\u3057\u307e\u3057\u305f\u3002\u30ed\u30b8\u30c3\u30af\u306f\u3001\u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u306e\u6301\u7d9a\u6642\u9593\u3054\u3068\u306b3\u5206\u3054\u3068\u306b\u30e9\u30f3\u30c0\u30e0\u306a\u5fc3\u62cd\u6570\u5024\u3092\u751f\u6210\u3059\u308b\u3082\u306e\u3067\u3001\u4ee5\u4e0b\u306e<code>HeartRateService<\/code>\u30af\u30e9\u30b9\u306b\u5b9f\u88c5\u3055\u308c\u3066\u3044\u307e\u3059\u3002<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-java\">public void generateHeartRate(WorkoutCreated workout) {\n        Faker faker = new Faker();\n        LocalDateTime start = Helper.convertToLocalDateTime(workout.getStartTime());\n        LocalDateTime end = Helper.convertToLocalDateTime(workout.getEndTime());\n        for (LocalDateTime date = start; date.isBefore(end); date = date.plusMinutes(3)) {\n            HeartRate heartRate = new HeartRate();\n            heartRate.setWorkoutId(workout.getId());\n            heartRate.setValue(faker.number().randomDouble(0, 50, 190));\n            heartRate.setTimestamp(Date.from(date.toInstant(ZoneOffset.UTC)));\n            heartRate.setId(KeyGenerator.next(\"hr_\"));\n            try {\n                heartRateCollection.put(heartRate);\n                log.debug(\"Append: {}\", heartRate);\n            }\n            catch (GSException e) {\n                log.error(\"Error generateHeartRate\", e);\n            }\n        }\n    }<\/code><\/pre>\n<\/div>\n<h3>\u30c0\u30df\u30fc\u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u306e\u751f\u6210<\/h3>\n<p>\u53ef\u8996\u5316\u306b\u79fb\u308b\u524d\u306b\u3001\u30c7\u30e2\u76ee\u7684\u3067\u30c0\u30df\u30fc\u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u3092\u751f\u6210\u3059\u308b\u65b9\u6cd5\u3092\u8aac\u660e\u3057\u307e\u3059\u3002\u30c0\u30df\u30fc\u30c7\u30fc\u30bf\u3092\u751f\u6210\u3059\u308b\u969b\u306b\u306f\u3001<a href=\"https:\/\/github.com\/DiUS\/java-faker\">Java Faker<\/a>\u3068\u3044\u3046\u30e9\u30a4\u30d6\u30e9\u30ea\u3092\u4f7f\u7528\u3057\u307e\u3059\u3002\u3053\u306e\u30e9\u30a4\u30d6\u30e9\u30ea\u306f\u3001\u4f4f\u6240\u304b\u3089\u30dd\u30c3\u30d7\u30ab\u30eb\u30c1\u30e3\u30fc\u306e\u53c2\u7167\u307e\u3067\u3001\u591a\u69d8\u306a\u73fe\u5b9f\u7684\u306a\u30c7\u30fc\u30bf\u3092\u751f\u6210\u3059\u308b\u306e\u306b\u5f79\u7acb\u3061\u307e\u3059\u3002<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-java\">@PostMapping(\"\/generate-dummy\")\n    String generateDummyWorkout(RedirectAttributes attributes) {\n        List&lt;user> users = userService.fetchAll();\n        List&lt;workouttype> types = Arrays.asList(WorkoutType.values());\n        Faker faker = new Faker();\n        for (int i = 0; i &lt; 2; i++) {\n            LocalDateTime startDateTime = LocalDateTime.now().minusMinutes(faker.number().randomNumber(2, true));\n            LocalDateTime endDateTime = LocalDateTime.now().minusMinutes(faker.number().numberBetween(1, 3));\n            if (endDateTime.isBefore(startDateTime)) {\n                continue;\n            }\n\n            Collections.shuffle(users);\n            Collections.shuffle(types);\n            WorkoutDto workoutDto = WorkoutDto.builder()\n                .id(KeyGenerator.next(\"wk_\"))\n                .title(faker.weather().description() + \" - \" + faker.address().streetAddress() + \", \"\n                        + faker.address().cityName())\n                .type(types.get(0))\n                .userId(users.get(0).getId())\n                .distance(Helper.calculateAverageDistanceInKm(startDateTime, endDateTime))\n                .calories(faker.number().randomDouble(0, 50, 500))\n                .startTime(Date.from(startDateTime.toInstant(ZoneOffset.UTC)))\n                .endTime(Date.from(endDateTime.toInstant(ZoneOffset.UTC)))\n                .build();\n            workoutService.create(workoutDto);\n        }\n        attributes.addFlashAttribute(\"message\", \"Generated dummy workout!\");\n        return \"redirect:\/workouts\";\n    }&lt;\/workouttype>&lt;\/user><\/code><\/pre>\n<\/div>\n<h3>\u5fc3\u62cd\u6570\u53ef\u8996\u5316\u3068\u5206\u6790<\/h3>\n<p>\u6b21\u306b\u3001\u53ef\u8996\u5316\u306e\u90e8\u5206\u306b\u9032\u307f\u307e\u3059\u3002\u6700\u521d\u306e\u30b9\u30c6\u30c3\u30d7\u306f\u3001\u9078\u629e\u3057\u305f\u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u306e\u5fc3\u62cd\u6570\u3092\u6642\u9593\u8ef8\u306b\u6cbf\u3063\u3066\u53ef\u8996\u5316\u3059\u308b\u3053\u3068\u3067\u3059\u3002\u7dda\u30b0\u30e9\u30d5\u306e\u4f5c\u6210\u306b\u306fApache EChart\u3092\u4f7f\u7528\u3057\u307e\u3059\u3002<\/p>\n<ol>\n<li>CDN\u304b\u3089EChart\u30e9\u30a4\u30d6\u30e9\u30ea\u3092\u30a4\u30f3\u30af\u30eb\u30fc\u30c9\u3057\u307e\u3059<\/li>\n<\/ol>\n<div class=\"clipboard\">\n<pre><code class=\"language-html\">&lt;script src=\"https:\/\/cdn.jsdelivr.net\/npm\/echarts@5.5.1\/dist\/echarts.min.js\"\n        integrity=\"sha256-6EJwvQzVvfYP78JtAMKjkcsugfTSanqe4WGFpUdzo88=\" crossorigin=\"anonymous\"&gt;&lt;\/script&gt;<\/code><\/pre>\n<\/div>\n<ol>\n<li>\u30c1\u30e3\u30fc\u30c8\u3092\u683c\u7d0d\u3059\u308b\u305f\u3081\u306eDOM\u8981\u7d20\u3092\u4f5c\u6210\u3059\u308b<\/li>\n<\/ol>\n<div class=\"clipboard\">\n<pre><code class=\"language-html\">&lt;div class=\"chart-area\"&gt;\n  &lt;div id=\"heartrateChart\" style=\"height:600px;\"&gt;&lt;\/div&gt;\n&lt;\/div&gt;<\/code><\/pre>\n<\/div>\n<ol>\n<li>EChart\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u3092\u4f5c\u6210\u3057\u3001DOM\u30b3\u30f3\u30c6\u30ca\u306b\u30d0\u30a4\u30f3\u30c9\u3057\u307e\u3059\u3002\u30bf\u30a4\u30c8\u30eb\u3001x\u8ef8\u306e\u30bf\u30a4\u30d7\uff08\u30ab\u30c6\u30b4\u30ea\uff09\u3001y\u8ef8\u306e\u6570\u5024\u30c7\u30fc\u30bf\u7528\u306e\u5024\u3092\u5b9a\u7fa9\u3057\u3001\u30c7\u30fc\u30bf\u30bb\u30c3\u30c8\u3092\u8868\u3059\u30b7\u30ea\u30fc\u30ba\u3092\u5b9a\u7fa9\u3057\u307e\u3059\u3002<\/li>\n<\/ol>\n<div class=\"clipboard\">\n<pre><code class=\"language-js\">var myChart = echarts.init(document.getElementById('heartrateChart'));\nconst heartRateZoneSummaries = \/*[[${heartRateZoneSummaries}]]*\/[]\nconst _data = \/*[[${heartRates}]]*\/[\n    { timestampLabel: \"2024\/01\/01 04:18\", value: 10 },\n    { timestampLabel: \"2024\/01\/02 04:23\", value: 5 }\n];\nvar option = {\n    title: {\n        text: 'Heart Rate',\n        left: '1%'\n    },\n    tooltip: {\n        trigger: 'axis'\n    },\n    grid: {\n        left: '10%',\n        right: '15%',\n        bottom: '10%'\n    },\n    xAxis: {\n        type: 'category',\n        boundaryGap: false,\n        data: _data.map(x => x.timestampLabel)\n    },\n    yAxis: {\n        type: 'value',\n        boundaryGap: [0, '100%']\n    },\n    toolbox: {\n        right: 10,\n        feature: {\n            dataZoom: {\n                yAxisIndex: 'none'\n            },\n            restore: {},\n            saveAsImage: {}\n        }\n    },\n    series: [\n        {\n            name: 'Heart Rate',\n            type: 'line',                    \n            data: _data.map(x => x.value),\n            markLine: {\n                silent: true,\n                lineStyle: {\n                    color: '#333'\n                },\n                data: [\n                    {\n                        yAxis: 50\n                    },\n                    {\n                        yAxis: 100\n                    },\n                    {\n                        yAxis: 150\n                    },\n                    {\n                        yAxis: 200\n                    },\n                    {\n                        yAxis: 250\n                    }\n                ]\n            }\n        }\n    ],\n};\n\/\/ Display the chart using the configuration items and data just specified.\nmyChart.setOption(option);<\/code><\/pre>\n<\/div>\n<p>HTML\u3067\u30c1\u30e3\u30fc\u30c8\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3092\u5b9a\u7fa9\u3057\u305f\u5f8c\u3001\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u30af\u30e9\u30b9\u304b\u3089\u5fc3\u62cd\u6570\u30c7\u30fc\u30bf\u3092\u6e21\u3059\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002 <code>HeartRateController<\/code>\uff1a<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-java\">@GetMapping(\"\/{id}\")\n    String heartRate(Model model, @PathVariable(\"id\") String id) {\n        log.info(\"Fetch heart rate of workout: {}\", id);\n        HeartRateDashboard heartRateDashboard = heartRateService.fetchForDashboardByWorkoutId(id);\n        log.info(\"heart rate size: {}\", heartRateDashboard.heartRates().size());\n        model.addAttribute(\"heartRates\", heartRateDashboard.heartRates());\n        model.addAttribute(\"workout\", workoutService.fetchById(id));\n        model.addAttribute(\"heartRateZoneSummaries\", heartRateDashboard.heartRateZoneSummaries());\n        return \"heartrate\";\n    }<\/code><\/pre>\n<\/div>\n<p>\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u306f\u3001\u5fc3\u62cd\u6570\u306e\u4e00\u89a7\u3068\u30be\u30fc\u30f3\u306e\u8981\u7d04\u3092\u30d3\u30e5\u30fc\u306b\u9001\u4fe1\u3057\u307e\u3059\u3002\u4ee5\u4e0b\u306bDTO\u30af\u30e9\u30b9\u3092\u793a\u3057\u307e\u3059\uff1a<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-java\">public record HeartRateDashboard(List&lt;heartratedto> heartRates, List&lt;heartratezonesummary> heartRateZoneSummaries) {}\n\n@Data\npublic class HeartRateDto {\n    private String timestampLabel;\n    private double value;\n}\n\n@Data\n@Builder\npublic class HeartRateZoneSummary {\n    String zoneValue;\n    long durationInMinuets;\n}&lt;\/heartratezonesummary>&lt;\/heartratedto><\/code><\/pre>\n<\/div>\n<p>In the service class, we query the heart rate container by workout ID and then format the timestamp into our desired format &#8220;2024\/01\/01 04:18&#8221;. Besides that, we categorize the heart rate data into 3 zones which are the Peak Zone, Cardio Zone, and Fat Burn Zone.<\/p>\n<p><code>HeartRateService<\/code>:<\/p>\n<div class=\"clipboard\">\n<pre><code class=\"language-java\">public HeartRateDashboard fetchForDashboardByWorkoutId(String workoutId) {\n        List&lt;heartratedto> heartRates = new ArrayList&lt;>(0);\n        List&lt;heartrate> heartRatesList = getHeartRatesList(workoutId);\n        heartRates = heartRatesList.stream()\n            .map(hr -> HeartRateDto.builder()\n                .timestampLabel(getTimestampLabel(hr.getTimestamp()))\n                .value(hr.getValue())\n                .build())\n            .collect(Collectors.toList());\n\n        List&lt;heartratezonesummary> heartRateZoneSummaries = HeartRateAnalyzer.analyze(heartRatesList);\n\n        return new HeartRateDashboard(heartRates, heartRateZoneSummaries);\n    }\n\n    private List&lt;heartrate> getHeartRatesList(String workoutId) {\n        List&lt;\/heartrate>&lt;heartrate> heartRatesList = new ArrayList&lt;>(0);\n        try {\n            Query&lt;\/heartrate>&lt;heartrate> query = heartRateCollection.query(\"SELECT * WHERE workoutId='\" + workoutId + \"'\",\n                    HeartRate.class);\n            RowSet&lt;\/heartrate>&lt;heartrate> rowSet = query.fetch();\n            while (rowSet.hasNext()) {\n                HeartRate row = rowSet.next();\n                log.debug(\"Fetched: {}\", row);\n                heartRatesList.add(row);\n            }\n        }\n        catch (GSException e) {\n            log.error(\"Error query heart rate\", e);\n        }\n        return heartRatesList;\n    }&lt;\/heartrate>&lt;\/heartratezonesummary>&lt;\/heartrate>&lt;\/heartratedto><\/code><\/pre>\n<\/div>\n<p>\u3064\u3044\u306b\u30c0\u30c3\u30b7\u30e5\u30dc\u30fc\u30c9\u304c\u5b8c\u6210\u3057\u307e\u3057\u305f: <a href=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/03\/heartrate-dashboard.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/griddb.net\/wp-content\/uploads\/2025\/03\/heartrate-dashboard.png\" alt=\"\" width=\"1311\" height=\"838\" class=\"aligncenter size-full wp-image-31395\" srcset=\"\/wp-content\/uploads\/2025\/03\/heartrate-dashboard.png 1311w, \/wp-content\/uploads\/2025\/03\/heartrate-dashboard-300x192.png 300w, \/wp-content\/uploads\/2025\/03\/heartrate-dashboard-1024x655.png 1024w, \/wp-content\/uploads\/2025\/03\/heartrate-dashboard-768x491.png 768w, \/wp-content\/uploads\/2025\/03\/heartrate-dashboard-600x384.png 600w\" sizes=\"(max-width: 1311px) 100vw, 1311px\" \/><\/a><\/p>\n<p>\u79c1\u305f\u3061\u306f\u3001\u3053\u306e\u30de\u30eb\u30c1\u30b3\u30f3\u30c6\u30caDocker\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\uff08Spring Boot\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3068GridDB\uff09\u306e\u5b9a\u7fa9\u3068\u5b9f\u884c\u306bDocker Compose\u3092\u4f7f\u7528\u3057\u3066\u3044\u307e\u3059\u3002\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3092\u5b9f\u884c\u3059\u308b\u306b\u306f\u3001<a href=\"https:\/\/github.com\/alifruliarso\/springboot-fitness-tracking\">Github<\/a>\u306e\u624b\u9806\u306b\u5f93\u3063\u3066\u304f\u3060\u3055\u3044\u3002<\/p>\n<h2>\u7d50\u8ad6<\/h2>\n<p>\u3053\u306e\u30c1\u30e5\u30fc\u30c8\u30ea\u30a2\u30eb\u3067\u306f\u3001Spring Boot\u3092\u4f7f\u7528\u3057\u3066\u5805\u7262\u306aMVC\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u4f5c\u6210\u3059\u308b\u529b\u3092\u63a2\u6c42\u3057\u307e\u3057\u305f\u3002GridDB\u3092\u30c7\u30fc\u30bf\u6c38\u7d9a\u5316\u3068\u3057\u3066\u7d71\u5408\u3059\u308b\u65b9\u6cd5\u3092\u793a\u3057\u307e\u3057\u305f\u3002Thymeleaf\u306e\u3088\u3046\u306a\u30d5\u30ed\u30f3\u30c8\u30a8\u30f3\u30c9\u6280\u8853\u306e\u7d71\u5408\u306f\u3001Spring Boot\u304c\u3055\u307e\u3056\u307e\u306aUI\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306b\u5bfe\u5fdc\u3067\u304d\u308b\u67d4\u8edf\u6027\u3092\u793a\u3057\u307e\u3057\u305f\u3002raw\u30d5\u30a3\u30c3\u30c8\u30cd\u30b9\u30c7\u30fc\u30bf\u3092\u610f\u5473\u306e\u3042\u308b\u53ef\u8996\u5316\u306b\u5909\u63db\u3059\u308b\u65b9\u6cd5\u3092\u5b66\u3073\u307e\u3057\u305f\u3002<\/p>\n<p>\u3053\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u4ee5\u4e0b\u306e\u6a5f\u80fd\u3067\u62e1\u5f35\u3067\u304d\u307e\u3059\uff1a* \u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u4e00\u89a7\u306b\u30da\u30fc\u30b8\u30cd\u30fc\u30b7\u30e7\u30f3\u3092\u5b9f\u88c5\u3059\u308b* \u30a6\u30a7\u30a2\u30e9\u30d6\u30eb\u30c7\u30d0\u30a4\u30b9\u3067\u4f7f\u7528\u3067\u304d\u308bAPI\u3092\u63d0\u4f9b\u3059\u308b\u304b\u3001\u30d5\u30a3\u30c3\u30c8\u30cd\u30b9API\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u304b\u3089\u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u306e\u8a73\u7d30\u3092\u53d6\u5f97\u3059\u308b<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u5065\u5eb7\u3067\u30d5\u30a3\u30c3\u30c8\u306a\u72b6\u614b\u306f\u3001\u79c1\u305f\u3061\u306e\u30e9\u30a4\u30d5\u30b9\u30bf\u30a4\u30eb\u306e\u4e00\u90e8\u3067\u3042\u308b\u3079\u304d\u3067\u3059\u3002\u5065\u5eb7\u7684\u306a\u30e9\u30a4\u30d5\u30b9\u30bf\u30a4\u30eb\u3092\u9001\u308b\u3053\u3068\u306f\u3001\u9577\u671f\u7684\u306a\u75c5\u6c17\u306e\u4e88\u9632\u306b\u5f79\u7acb\u3061\u307e\u3059\u3002\u305d\u306e\u305f\u3081\u306e\u65b9\u6cd5\u306e\u4e00\u3064\u306f\u3001\u30ef\u30fc\u30af\u30a2\u30a6\u30c8\u306e\u8a18\u9332\u3092\u4ed8\u3051\u308b\u3053\u3068\u3067\u3059\u3002\u5177\u4f53\u7684\u306b\u306f\u3001\u904b\u52d5\u306e\u7a2e\u985e\u3001 [&hellip;]<\/p>\n","protected":false},"author":41,"featured_media":49781,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1005],"tags":[],"class_list":["post-50930","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-1005"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Spring Boot \u3092\u4f7f\u7528\u3057\u3066\u30d5\u30a3\u30c3\u30c8\u30cd\u30b9\u30c8\u30e9\u30c3\u30ab\u30fc\u30a6\u30a7\u30d6\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u69cb\u7bc9\u3059\u308b | GridDB: Open Source Time Series Database for IoT<\/title>\n<meta name=\"description\" content=\"\u5065\u5eb7\u3067\u30d5\u30a3\u30c3\u30c8\u306a\u72b6\u614b\u306f\u3001\u79c1\u305f\u3061\u306e\u30e9\u30a4\u30d5\u30b9\u30bf\u30a4\u30eb\u306e\u4e00\u90e8\u3067\u3042\u308b\u3079\u304d\u3067\u3059\u3002\u5065\u5eb7\u7684\u306a\u30e9\u30a4\u30d5\u30b9\u30bf\u30a4\u30eb\u3092\u9001\u308b\u3053\u3068\u306f\u3001\u9577\u671f\u7684\u306a\u75c5\u6c17\u306e\u4e88\u9632\u306b\u5f79\u7acb\u3061\u307e\u3059\u3002\u305d\u306e\u305f\u3081\u306e\u65b9\u6cd5\u306e\u4e00\u3064\u306f\u3001\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.griddb.net\/ja\/\u672a\u5206\u985e\/building-a-fitness-tracker-web-using-spring-boot\/\" \/>\n<meta property=\"og:locale\" content=\"ja_JP\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Spring Boot \u3092\u4f7f\u7528\u3057\u3066\u30d5\u30a3\u30c3\u30c8\u30cd\u30b9\u30c8\u30e9\u30c3\u30ab\u30fc\u30a6\u30a7\u30d6\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u69cb\u7bc9\u3059\u308b | GridDB: Open Source Time Series Database for IoT\" \/>\n<meta property=\"og:description\" content=\"\u5065\u5eb7\u3067\u30d5\u30a3\u30c3\u30c8\u306a\u72b6\u614b\u306f\u3001\u79c1\u305f\u3061\u306e\u30e9\u30a4\u30d5\u30b9\u30bf\u30a4\u30eb\u306e\u4e00\u90e8\u3067\u3042\u308b\u3079\u304d\u3067\u3059\u3002\u5065\u5eb7\u7684\u306a\u30e9\u30a4\u30d5\u30b9\u30bf\u30a4\u30eb\u3092\u9001\u308b\u3053\u3068\u306f\u3001\u9577\u671f\u7684\u306a\u75c5\u6c17\u306e\u4e88\u9632\u306b\u5f79\u7acb\u3061\u307e\u3059\u3002\u305d\u306e\u305f\u3081\u306e\u65b9\u6cd5\u306e\u4e00\u3064\u306f\u3001\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.griddb.net\/ja\/\u672a\u5206\u985e\/building-a-fitness-tracker-web-using-spring-boot\/\" \/>\n<meta property=\"og:site_name\" content=\"GridDB: Open Source Time Series Database for IoT\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/griddbcommunity\/\" \/>\n<meta property=\"article:published_time\" content=\"2025-03-07T08:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-11-14T15:57:04+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.griddb.net\/wp-content\/uploads\/2025\/03\/cover-image.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1100\" \/>\n\t<meta property=\"og:image:height\" content=\"653\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"griddb-admin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@GridDBCommunity\" \/>\n<meta name=\"twitter:site\" content=\"@GridDBCommunity\" \/>\n<meta name=\"twitter:label1\" content=\"\u57f7\u7b46\u8005\" \/>\n\t<meta name=\"twitter:data1\" content=\"griddb-admin\" \/>\n\t<meta name=\"twitter:label2\" content=\"\u63a8\u5b9a\u8aad\u307f\u53d6\u308a\u6642\u9593\" \/>\n\t<meta name=\"twitter:data2\" content=\"8\u5206\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.griddb.net\/ja\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.griddb.net\/ja\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/\"},\"author\":{\"name\":\"griddb-admin\",\"@id\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#\/schema\/person\/4fe914ca9576878e82f5e8dd3ba52233\"},\"headline\":\"Spring Boot \u3092\u4f7f\u7528\u3057\u3066\u30d5\u30a3\u30c3\u30c8\u30cd\u30b9\u30c8\u30e9\u30c3\u30ab\u30fc\u30a6\u30a7\u30d6\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u69cb\u7bc9\u3059\u308b\",\"datePublished\":\"2025-03-07T08:00:00+00:00\",\"dateModified\":\"2025-11-14T15:57:04+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.griddb.net\/ja\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/\"},\"wordCount\":157,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.griddb.net\/ja\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/#primaryimage\"},\"thumbnailUrl\":\"\/wp-content\/uploads\/2025\/03\/cover-image.png\",\"inLanguage\":\"ja\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.griddb.net\/ja\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.griddb.net\/ja\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/\",\"url\":\"https:\/\/www.griddb.net\/ja\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/\",\"name\":\"Spring Boot \u3092\u4f7f\u7528\u3057\u3066\u30d5\u30a3\u30c3\u30c8\u30cd\u30b9\u30c8\u30e9\u30c3\u30ab\u30fc\u30a6\u30a7\u30d6\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u69cb\u7bc9\u3059\u308b | GridDB: Open Source Time Series Database for IoT\",\"isPartOf\":{\"@id\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.griddb.net\/ja\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.griddb.net\/ja\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/#primaryimage\"},\"thumbnailUrl\":\"\/wp-content\/uploads\/2025\/03\/cover-image.png\",\"datePublished\":\"2025-03-07T08:00:00+00:00\",\"dateModified\":\"2025-11-14T15:57:04+00:00\",\"description\":\"\u5065\u5eb7\u3067\u30d5\u30a3\u30c3\u30c8\u306a\u72b6\u614b\u306f\u3001\u79c1\u305f\u3061\u306e\u30e9\u30a4\u30d5\u30b9\u30bf\u30a4\u30eb\u306e\u4e00\u90e8\u3067\u3042\u308b\u3079\u304d\u3067\u3059\u3002\u5065\u5eb7\u7684\u306a\u30e9\u30a4\u30d5\u30b9\u30bf\u30a4\u30eb\u3092\u9001\u308b\u3053\u3068\u306f\u3001\u9577\u671f\u7684\u306a\u75c5\u6c17\u306e\u4e88\u9632\u306b\u5f79\u7acb\u3061\u307e\u3059\u3002\u305d\u306e\u305f\u3081\u306e\u65b9\u6cd5\u306e\u4e00\u3064\u306f\u3001\",\"inLanguage\":\"ja\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.griddb.net\/ja\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"ja\",\"@id\":\"https:\/\/www.griddb.net\/ja\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/#primaryimage\",\"url\":\"\/wp-content\/uploads\/2025\/03\/cover-image.png\",\"contentUrl\":\"\/wp-content\/uploads\/2025\/03\/cover-image.png\",\"width\":1100,\"height\":653},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#website\",\"url\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/\",\"name\":\"GridDB: Open Source Time Series Database for IoT\",\"description\":\"GridDB is an open source time-series database with the performance of NoSQL and convenience of SQL\",\"publisher\":{\"@id\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"ja\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#organization\",\"name\":\"Fixstars\",\"url\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"ja\",\"@id\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/griddb.net\/wp-content\/uploads\/2019\/04\/fixstars_logo_web_tagline.png\",\"contentUrl\":\"https:\/\/griddb.net\/wp-content\/uploads\/2019\/04\/fixstars_logo_web_tagline.png\",\"width\":200,\"height\":83,\"caption\":\"Fixstars\"},\"image\":{\"@id\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/griddbcommunity\/\",\"https:\/\/x.com\/GridDBCommunity\",\"https:\/\/www.linkedin.com\/company\/griddb-by-toshiba\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#\/schema\/person\/4fe914ca9576878e82f5e8dd3ba52233\",\"name\":\"griddb-admin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"ja\",\"@id\":\"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/5bceca1cafc06886a7ba873e2f0a28011a1176c4dea59709f735b63ae30d0342?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/5bceca1cafc06886a7ba873e2f0a28011a1176c4dea59709f735b63ae30d0342?s=96&d=mm&r=g\",\"caption\":\"griddb-admin\"},\"url\":\"https:\/\/www.griddb.net\/ja\/author\/griddb-admin\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Spring Boot \u3092\u4f7f\u7528\u3057\u3066\u30d5\u30a3\u30c3\u30c8\u30cd\u30b9\u30c8\u30e9\u30c3\u30ab\u30fc\u30a6\u30a7\u30d6\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u69cb\u7bc9\u3059\u308b | GridDB: Open Source Time Series Database for IoT","description":"\u5065\u5eb7\u3067\u30d5\u30a3\u30c3\u30c8\u306a\u72b6\u614b\u306f\u3001\u79c1\u305f\u3061\u306e\u30e9\u30a4\u30d5\u30b9\u30bf\u30a4\u30eb\u306e\u4e00\u90e8\u3067\u3042\u308b\u3079\u304d\u3067\u3059\u3002\u5065\u5eb7\u7684\u306a\u30e9\u30a4\u30d5\u30b9\u30bf\u30a4\u30eb\u3092\u9001\u308b\u3053\u3068\u306f\u3001\u9577\u671f\u7684\u306a\u75c5\u6c17\u306e\u4e88\u9632\u306b\u5f79\u7acb\u3061\u307e\u3059\u3002\u305d\u306e\u305f\u3081\u306e\u65b9\u6cd5\u306e\u4e00\u3064\u306f\u3001","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.griddb.net\/ja\/\u672a\u5206\u985e\/building-a-fitness-tracker-web-using-spring-boot\/","og_locale":"ja_JP","og_type":"article","og_title":"Spring Boot \u3092\u4f7f\u7528\u3057\u3066\u30d5\u30a3\u30c3\u30c8\u30cd\u30b9\u30c8\u30e9\u30c3\u30ab\u30fc\u30a6\u30a7\u30d6\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u69cb\u7bc9\u3059\u308b | GridDB: Open Source Time Series Database for IoT","og_description":"\u5065\u5eb7\u3067\u30d5\u30a3\u30c3\u30c8\u306a\u72b6\u614b\u306f\u3001\u79c1\u305f\u3061\u306e\u30e9\u30a4\u30d5\u30b9\u30bf\u30a4\u30eb\u306e\u4e00\u90e8\u3067\u3042\u308b\u3079\u304d\u3067\u3059\u3002\u5065\u5eb7\u7684\u306a\u30e9\u30a4\u30d5\u30b9\u30bf\u30a4\u30eb\u3092\u9001\u308b\u3053\u3068\u306f\u3001\u9577\u671f\u7684\u306a\u75c5\u6c17\u306e\u4e88\u9632\u306b\u5f79\u7acb\u3061\u307e\u3059\u3002\u305d\u306e\u305f\u3081\u306e\u65b9\u6cd5\u306e\u4e00\u3064\u306f\u3001","og_url":"https:\/\/www.griddb.net\/ja\/\u672a\u5206\u985e\/building-a-fitness-tracker-web-using-spring-boot\/","og_site_name":"GridDB: Open Source Time Series Database for IoT","article_publisher":"https:\/\/www.facebook.com\/griddbcommunity\/","article_published_time":"2025-03-07T08:00:00+00:00","article_modified_time":"2025-11-14T15:57:04+00:00","og_image":[{"width":1100,"height":653,"url":"https:\/\/www.griddb.net\/wp-content\/uploads\/2025\/03\/cover-image.png","type":"image\/png"}],"author":"griddb-admin","twitter_card":"summary_large_image","twitter_creator":"@GridDBCommunity","twitter_site":"@GridDBCommunity","twitter_misc":{"\u57f7\u7b46\u8005":"griddb-admin","\u63a8\u5b9a\u8aad\u307f\u53d6\u308a\u6642\u9593":"8\u5206"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.griddb.net\/ja\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/#article","isPartOf":{"@id":"https:\/\/www.griddb.net\/ja\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/"},"author":{"name":"griddb-admin","@id":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#\/schema\/person\/4fe914ca9576878e82f5e8dd3ba52233"},"headline":"Spring Boot \u3092\u4f7f\u7528\u3057\u3066\u30d5\u30a3\u30c3\u30c8\u30cd\u30b9\u30c8\u30e9\u30c3\u30ab\u30fc\u30a6\u30a7\u30d6\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u69cb\u7bc9\u3059\u308b","datePublished":"2025-03-07T08:00:00+00:00","dateModified":"2025-11-14T15:57:04+00:00","mainEntityOfPage":{"@id":"https:\/\/www.griddb.net\/ja\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/"},"wordCount":157,"commentCount":0,"publisher":{"@id":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#organization"},"image":{"@id":"https:\/\/www.griddb.net\/ja\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/#primaryimage"},"thumbnailUrl":"\/wp-content\/uploads\/2025\/03\/cover-image.png","inLanguage":"ja","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.griddb.net\/ja\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.griddb.net\/ja\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/","url":"https:\/\/www.griddb.net\/ja\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/","name":"Spring Boot \u3092\u4f7f\u7528\u3057\u3066\u30d5\u30a3\u30c3\u30c8\u30cd\u30b9\u30c8\u30e9\u30c3\u30ab\u30fc\u30a6\u30a7\u30d6\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u69cb\u7bc9\u3059\u308b | GridDB: Open Source Time Series Database for IoT","isPartOf":{"@id":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.griddb.net\/ja\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/#primaryimage"},"image":{"@id":"https:\/\/www.griddb.net\/ja\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/#primaryimage"},"thumbnailUrl":"\/wp-content\/uploads\/2025\/03\/cover-image.png","datePublished":"2025-03-07T08:00:00+00:00","dateModified":"2025-11-14T15:57:04+00:00","description":"\u5065\u5eb7\u3067\u30d5\u30a3\u30c3\u30c8\u306a\u72b6\u614b\u306f\u3001\u79c1\u305f\u3061\u306e\u30e9\u30a4\u30d5\u30b9\u30bf\u30a4\u30eb\u306e\u4e00\u90e8\u3067\u3042\u308b\u3079\u304d\u3067\u3059\u3002\u5065\u5eb7\u7684\u306a\u30e9\u30a4\u30d5\u30b9\u30bf\u30a4\u30eb\u3092\u9001\u308b\u3053\u3068\u306f\u3001\u9577\u671f\u7684\u306a\u75c5\u6c17\u306e\u4e88\u9632\u306b\u5f79\u7acb\u3061\u307e\u3059\u3002\u305d\u306e\u305f\u3081\u306e\u65b9\u6cd5\u306e\u4e00\u3064\u306f\u3001","inLanguage":"ja","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.griddb.net\/ja\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/"]}]},{"@type":"ImageObject","inLanguage":"ja","@id":"https:\/\/www.griddb.net\/ja\/%e6%9c%aa%e5%88%86%e9%a1%9e\/building-a-fitness-tracker-web-using-spring-boot\/#primaryimage","url":"\/wp-content\/uploads\/2025\/03\/cover-image.png","contentUrl":"\/wp-content\/uploads\/2025\/03\/cover-image.png","width":1100,"height":653},{"@type":"WebSite","@id":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#website","url":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/","name":"GridDB: Open Source Time Series Database for IoT","description":"GridDB is an open source time-series database with the performance of NoSQL and convenience of SQL","publisher":{"@id":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"ja"},{"@type":"Organization","@id":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#organization","name":"Fixstars","url":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/","logo":{"@type":"ImageObject","inLanguage":"ja","@id":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#\/schema\/logo\/image\/","url":"https:\/\/griddb.net\/wp-content\/uploads\/2019\/04\/fixstars_logo_web_tagline.png","contentUrl":"https:\/\/griddb.net\/wp-content\/uploads\/2019\/04\/fixstars_logo_web_tagline.png","width":200,"height":83,"caption":"Fixstars"},"image":{"@id":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/griddbcommunity\/","https:\/\/x.com\/GridDBCommunity","https:\/\/www.linkedin.com\/company\/griddb-by-toshiba"]},{"@type":"Person","@id":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#\/schema\/person\/4fe914ca9576878e82f5e8dd3ba52233","name":"griddb-admin","image":{"@type":"ImageObject","inLanguage":"ja","@id":"https:\/\/griddb-linux-hte8hndjf8cka8ht.westus-01.azurewebsites.net\/en\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/5bceca1cafc06886a7ba873e2f0a28011a1176c4dea59709f735b63ae30d0342?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/5bceca1cafc06886a7ba873e2f0a28011a1176c4dea59709f735b63ae30d0342?s=96&d=mm&r=g","caption":"griddb-admin"},"url":"https:\/\/www.griddb.net\/ja\/author\/griddb-admin\/"}]}},"_links":{"self":[{"href":"https:\/\/www.griddb.net\/ja\/wp-json\/wp\/v2\/posts\/50930","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.griddb.net\/ja\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.griddb.net\/ja\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.griddb.net\/ja\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/www.griddb.net\/ja\/wp-json\/wp\/v2\/comments?post=50930"}],"version-history":[{"count":1,"href":"https:\/\/www.griddb.net\/ja\/wp-json\/wp\/v2\/posts\/50930\/revisions"}],"predecessor-version":[{"id":51746,"href":"https:\/\/www.griddb.net\/ja\/wp-json\/wp\/v2\/posts\/50930\/revisions\/51746"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.griddb.net\/ja\/wp-json\/wp\/v2\/media\/49781"}],"wp:attachment":[{"href":"https:\/\/www.griddb.net\/ja\/wp-json\/wp\/v2\/media?parent=50930"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.griddb.net\/ja\/wp-json\/wp\/v2\/categories?post=50930"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.griddb.net\/ja\/wp-json\/wp\/v2\/tags?post=50930"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}