Jekyll2023-06-07T00:03:42+00:00https://drugowick.dev/feed.xmlBruno DrugowickI love helping people to understand and deal with technology. If I can build something in the process, even better!What about that AI, the GPT thing? Will it really affect my profession in the future?2023-03-21T03:02:22+00:002023-03-21T03:02:22+00:00https://drugowick.dev/2023/03/21/what-about-that-ai-the-gpt-thing-will-it-really-affect-my-profession-in-the-future-503a<style type="text/css" media="screen">
.card {
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
transition: 0.3s;
width: 100%;
}
.card:hover {
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
}
.container {
padding: 2px 16px;
}
</style>
<p><code>[chatgpt, go, algorithms, career]</code></p>
<div class="card">
<div class="container">
<h4><b><br />You'll have a better experience reading in DEV</b></h4>
<p><a href="https://dev.to/brunodrugowick/what-about-that-ai-the-gpt-thing-will-it-really-affect-my-profession-in-the-future-503a" target="_blank">Click here to continue reading this post there >></a></p>
<p>However, if you want to know more about the project to mirror my posts from DEV here (and why), go ahead and <a href="https://dev.to/brunodrugowick/what-about-that-ai-the-gpt-thing-will-it-really-affect-my-profession-in-the-future-503a" target="_blank">read more</a>.</p>
<p>You can continue to read here too, it's up to you... =]</p>
</div>
</div>
<p><br /></p>
<h2>
<a name="id-never-used-gpt-until-today" href="#id-never-used-gpt-until-today">
</a>
I'd never used GPT... until today!
</h2>
<p>When all the hype about AI in programming started with GitHub Copilot I was not using VSCode plus the company I work for told us not to use it with any code from our clients. When ChatGPT came, a similar message also was sent to everybody. Put that together with my own recent career choice - to dedicate my time exclusively to code-related activities - and what happened is that I didn't care too much about all the hype until now.</p>
<p>Well, I've been reading, understanding, and discussing AI and how it relates to coding. I just haven't tried anything AI-related to help with my coding. And somehow I think that was a good decision. I didn't want a shortcut to get things done, I need mileage, I need to get my hands dirty with all sorts of programming problems, no matter how trivial. Apart from being fun, it's also the only way to re-learn some things that I ended up forgetting after a while (because, yeah, I forget very easily all the things - I might write about this in the future).</p>
<h2>
<a name="i-was-just-peacefully-coding" href="#i-was-just-peacefully-coding">
</a>
I was just peacefully coding.
</h2>
<p>But reading about something is not the same as using and applying it to a mundane activity. And that's what happened today. I was coding a very simple algorithm that a dear friend of mine was talking about with me. He said he was asked to code it in an interview and I decided to implement it in Golang as a challenge. I wouldn't look anywhere, I wouldn't try to find a solution or a hint of the solution online. The only thing I allowed myself to do was to search syntax-related things, because, as I stated above, I easily forget some things.</p>
<p>And during that activity, I ended up needing to implement a stack. I had some sort of a stack already but badly implemented in a string that I was manipulating throughout the code. So I did my own implementation of the stack, which took me around 15 minutes. Here is the first version I came up with:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight go"><code><span class="c">// package and imports suppressed</span>
<span class="k">type</span> <span class="n">unicodeSlice</span> <span class="p">[]</span><span class="kt">int32</span>
<span class="k">type</span> <span class="n">Stack</span> <span class="k">struct</span> <span class="p">{</span>
<span class="n">unicodeSlice</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">s</span> <span class="o">*</span><span class="n">Stack</span><span class="p">)</span> <span class="n">Push</span><span class="p">(</span><span class="n">v</span> <span class="kt">int32</span><span class="p">)</span> <span class="p">{</span>
<span class="n">s</span><span class="o">.</span><span class="n">unicodeSlice</span> <span class="o">=</span> <span class="nb">append</span><span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">unicodeSlice</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">s</span> <span class="o">*</span><span class="n">Stack</span><span class="p">)</span> <span class="n">Pop</span><span class="p">()</span> <span class="p">{</span>
<span class="n">lastElementPosition</span> <span class="o">:=</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">unicodeSlice</span><span class="p">)</span> <span class="o">-</span> <span class="m">1</span>
<span class="k">if</span> <span class="n">lastElementPosition</span> <span class="o">>=</span> <span class="m">0</span> <span class="p">{</span>
<span class="n">s</span><span class="o">.</span><span class="n">unicodeSlice</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">unicodeSlice</span><span class="p">[</span><span class="o">:</span><span class="n">lastElementPosition</span><span class="p">]</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">s</span> <span class="o">*</span><span class="n">Stack</span><span class="p">)</span> <span class="n">PopIfMatches</span><span class="p">(</span><span class="n">v</span> <span class="kt">int32</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
<span class="n">lastElementPosition</span> <span class="o">:=</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">unicodeSlice</span><span class="p">)</span> <span class="o">-</span> <span class="m">1</span>
<span class="k">if</span> <span class="n">lastElementPosition</span> <span class="o">>=</span> <span class="m">0</span> <span class="o">&&</span> <span class="n">s</span><span class="o">.</span><span class="n">unicodeSlice</span><span class="p">[</span><span class="n">lastElementPosition</span><span class="p">]</span> <span class="o">==</span> <span class="n">v</span> <span class="p">{</span>
<span class="n">s</span><span class="o">.</span><span class="n">Pop</span><span class="p">()</span>
<span class="k">return</span> <span class="no">nil</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Errorf</span><span class="p">(</span><span class="s">"value to pop (%d) does not match provided value (%d)"</span><span class="p">,</span> <span class="n">s</span><span class="o">.</span><span class="n">unicodeSlice</span><span class="p">[</span><span class="n">lastElementPosition</span><span class="p">],</span> <span class="n">v</span><span class="p">)</span>
<span class="p">}</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>My idea was to have a regular stack, hence both Push() and Pop(), and an additional PopIfMatches() which was what I needed: a way to only pop the stack if the top value of the stack was a determined value that I could pass. If not, I needed an error, and that's what I decided to return.</p>
<h2>
<a name="hunn-maybe-i-could-see-what-ai-is-all-about" href="#hunn-maybe-i-could-see-what-ai-is-all-about">
</a>
Hunn, maybe I could see what AI is all about.
</h2>
<p>Right away, after getting the implementation working, I started to review it, as I always do. Then it came to me: well, do I need to do that? Let's see what GPT can do! A stack is a very common implementation, but I wanted to see what GPT would do with my "PopIfMatches" requirement.</p>
<p>I typed this: "Can you make a stack in Go with a special method that pop (sic) only if the value corresponds to a passed value?" and got exactly this:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight go"><code><span class="c">// package and imports suppressed</span>
<span class="k">type</span> <span class="n">Stack</span> <span class="k">struct</span> <span class="p">{</span>
<span class="n">items</span> <span class="p">[]</span><span class="kt">int</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">s</span> <span class="o">*</span><span class="n">Stack</span><span class="p">)</span> <span class="n">Push</span><span class="p">(</span><span class="n">i</span> <span class="kt">int</span><span class="p">)</span> <span class="p">{</span>
<span class="n">s</span><span class="o">.</span><span class="n">items</span> <span class="o">=</span> <span class="nb">append</span><span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">items</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">s</span> <span class="o">*</span><span class="n">Stack</span><span class="p">)</span> <span class="n">PopIf</span><span class="p">(</span><span class="n">v</span> <span class="kt">int</span><span class="p">)</span> <span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="kt">bool</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">items</span><span class="p">)</span> <span class="o">==</span> <span class="m">0</span> <span class="p">{</span>
<span class="k">return</span> <span class="m">0</span><span class="p">,</span> <span class="no">false</span>
<span class="p">}</span>
<span class="n">lastIndex</span> <span class="o">:=</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">items</span><span class="p">)</span> <span class="o">-</span> <span class="m">1</span>
<span class="n">lastItem</span> <span class="o">:=</span> <span class="n">s</span><span class="o">.</span><span class="n">items</span><span class="p">[</span><span class="n">lastIndex</span><span class="p">]</span>
<span class="k">if</span> <span class="n">lastItem</span> <span class="o">==</span> <span class="n">v</span> <span class="p">{</span>
<span class="n">s</span><span class="o">.</span><span class="n">items</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">items</span><span class="p">[</span><span class="o">:</span><span class="n">lastIndex</span><span class="p">]</span>
<span class="k">return</span> <span class="n">lastItem</span><span class="p">,</span> <span class="no">true</span>
<span class="p">}</span>
<span class="k">return</span> <span class="m">0</span><span class="p">,</span> <span class="no">false</span>
<span class="p">}</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>Right off the bat, I noticed:</p>
<ul>
<li>
<code>PopIf</code> was easier to read than my code, with an early return for an empty stack.</li>
<li>
<code>PopIf</code> returns a bool instead of an error to indicate the success or failure of the code.</li>
<li>
<code>PopIf</code> also returns the removed value (or 0, which is the zero value for int in Go).</li>
</ul>
<p>But then I also noticed a few mistakes I made (which, granted, I would probably fix them on my review, but anyway...):</p>
<ul>
<li>Returning a <code>bool</code> instead of an error made way more sense. I just needed to indicate the success or failure of the operation. The meaning of it is something that the caller could take care of anyway.
<ul>
<li>That also helps with making the implementation cleaner because I didn't need to import anything like I was doing with <code>fmt</code>.</li>
</ul>
</li>
<li>There was no need for me to have my own <code>unicodeSlice</code> type. I could make it a slice of the type I needed.</li>
</ul>
<p>Not huge things, probably things I'd refactor right away or someone would get in a code review (in a real-world scenario), but still, things that are important. It took me less than 1 minute to formulate the prompt and get the implementation from the AI. Then a few more minutes to review the generated code and adapt it to my use. </p>
<h2>
<a name="time-is-money-and-money-is-what-matters-for-capitalism-at-least" href="#time-is-money-and-money-is-what-matters-for-capitalism-at-least">
</a>
Time is money. And money is what matters... for capitalism, at least
</h2>
<p>The time it took me to get the AI code and review it was still less than the 15 minutes that it took me to make the initial implementation from scratch, which would still require a quick review. Realistically, I would reward myself with a coffee or a quick walk around the house before getting into the review, so let's put that the final acceptable implementation, without the AI, would take around 40 minutes.</p>
<p>So it's around 10 minutes for the AI guided by the human against 40 minutes for the human aided by search engines. That's what capitalism is going to take into consideration! And I will go as far as saying that maybe that's already part of the reason we're seeing so many layoffs around the world.</p>
<h2>
<a name="who-am-i-or-who-will-i-become" href="#who-am-i-or-who-will-i-become">
</a>
Who am I? Or who will I become?
</h2>
<p>Is AI Code Reviewer a profession of the future? What about designing a whole application? Will there be something like AI Architect Reviewer? Is my next programming language the natural language?</p>
<p>So many questions, so many problems, and so little... time! Damn it! Who am I?</p>
<p>…</p>Automatically update your GitHub Pages website with posts from dev.to2022-03-26T21:29:55+00:002022-03-26T21:29:55+00:00https://drugowick.dev/2022/03/26/update-your-github-pages-website-with-posts-from-devto-ein<style type="text/css" media="screen">
.card {
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
transition: 0.3s;
width: 100%;
}
.card:hover {
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
}
.container {
padding: 2px 16px;
}
</style>
<p><code>[github, githubactions, jekyll, static]</code></p>
<div class="card">
<div class="container">
<h4><b><br />You'll have a better experience reading in DEV</b></h4>
<p><a href="https://dev.to/brunodrugowick/update-your-github-pages-website-with-posts-from-devto-ein" target="_blank">Click here to continue reading this post there >></a></p>
<p>However, if you want to know more about the project to mirror my posts from DEV here (and why), go ahead and <a href="https://dev.to/brunodrugowick/update-your-github-pages-website-with-posts-from-devto-ein" target="_blank">read more</a>.</p>
<p>You can continue to read here too, it's up to you... =]</p>
</div>
</div>
<p><br /></p>
<h2>
<a name="important-note" href="#important-note">
</a>
Important note
</h2>
<p>It's all free. The idea here is to get a working solution without spending a penny.</p>
<h2>
<a name="introduction" href="#introduction">
</a>
Introduction
</h2>
<p>I've already talked about GitHub Pages a long while ago when I first created my personal website.</p>
<p>Then, for convenience, I switched to another tool, which was free at the time, and never touched my website again. I won't name it because from around 2 years ago to now, when I decided to write a few things again, the tool is just a mess and it's not working. I'd have to pay to make it work the way I want, which is basically:</p>
<ul>
<li>A freaking simple static website with the list of my posts in dev.to.</li>
</ul>
<p>The reasons: I don't make any money from my website, is just a place to put some things I like to talk about; also, I forget things easily and will need to refer to these things in the future for sure.</p>
<h2>
<a name="cut-to-the-chase" href="#cut-to-the-chase">
</a>
Cut to the chase
</h2>
<h3>
<a name="1-build-your-personal-website" href="#1-build-your-personal-website">
</a>
1. Build your personal website
</h3>
<p>I won't spend time here, <a href="https://pages.github.com/">GitHub has lots of info about this</a>... and <a href="https://dev.to/brunodrugowick/github-pages-and-google-domains-together-5ded">I have a post on how to combine it with Google Domains</a> (that recently got out of Beta, by the way).</p>
<h3>
<a name="2-create-a-script-to-get-the-posts" href="#2-create-a-script-to-get-the-posts">
</a>
2. Create a script to get the posts
</h3>
<p>Dev.to as a <a href="https://developers.forem.com/api">very nice API</a> and, most importantly, GET requests are free! So we can create a very simple script to get your posts and then - and here's the trick, save to a file that your GitHub Pages website understands as data! Here's the script:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight plaintext"><code>#!/bin/bash
#### Thing to notice 1 ####
POSTS_FILE=./_data/devPosts.json
# Clear the posts file
echo -n "" >$POSTS_FILE
#### Thing to notice 2 ####
POSTS_JSON=$(curl https://dev.to/api/articles?username=brunodrugowick&?per_page=1000)
#### Thing to notice 3 ####
echo "$POSTS_JSON" | jq >>$POSTS_FILE
date >last_updated_date
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<h4>
<a name="thing-to-notice-1" href="#thing-to-notice-1">
</a>
Thing to notice 1
</h4>
<p>Notice how this variable points to a file under the <code>_data</code> directory. This is <a href="https://jekyllrb.com/docs/datafiles/">a special directory for GitHub Actions, that's powered by Jekyll</a>.</p>
<p>Files in this directory can be used as data when building your static website. You'll see in a few how this is used.</p>
<h4>
<a name="thing-to-notice-2" href="#thing-to-notice-2">
</a>
Thing to notice 2
</h4>
<p>This part of the script is the request to the dev.to API. Notice the parameters:</p>
<ul>
<li>
<code>username=brunodrugowick</code> is getting the posts for my user. You should change that to your username.</li>
<li>
<code>per_page=1000</code> is the number of posts to retrieve. I believe I won't ever get to that number, but in case I do I'll work on a loop to handle the pagination. =)</li>
</ul>
<h4>
<a name="thing-to-notice-3" href="#thing-to-notice-3">
</a>
Thing to notice 3
</h4>
<p>This part of the script writes the result of the request above to the data file that we referenced earlier. The pipe (<code>|</code>) to <code>jq</code> is to format nicely, probably not required.</p>
<h3>
<a name="3-list-the-posts-in-your-website" href="#3-list-the-posts-in-your-website">
</a>
3. List the posts in your website
</h3>
<p>At this point, this script can already be used in your local environment to fill in a data file that you can use in your GitHub Pages website. Let's do this then. </p>
<p>Put this code in your <code>index.md</code> page (you can use <code>.html</code> too or put this code anywhere else that you want in your website):<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight plaintext"><code>
## What about that AI, the GPT thing? Will it really affect my profession in the future?
Mar 21
I'd never used GPT... until today! When all the hype about AI in programming started with... [continue to read](https://dev.to/brunodrugowick/what-about-that-ai-the-gpt-thing-will-it-really-affect-my-profession-in-the-future-503a)
## Automatically update your GitHub Pages website with posts from dev.to
Mar 26 '22
Important note It's all free. The idea here is to get a working solution without spending... [continue to read](https://dev.to/brunodrugowick/update-your-github-pages-website-with-posts-from-devto-ein)
## Distributed Tracing with Spring Cloud Sleuth and Zipkin - AQAP Series
Jul 26 '20
There's a lot going on related to cloud services, libraries, providers etc. In fact, everything cloud... [continue to read](https://dev.to/brunodrugowick/distributed-tracing-with-spring-cloud-sleuth-and-zipkin-3moo)
## The most basic security for Spring Boot with Thymeleaf
Mar 25 '20
I like to develop small proof of concept applications. Although just validating, some security stuff... [continue to read](https://dev.to/brunodrugowick/the-most-basic-security-for-spring-boot-with-thymeleaf-339h)
## Three steps to extend a Spring Data JPA repository with your own code
Feb 21 '20
This is the post #6 of the series "Querying your Spring Data JPA Repository". What about an... [continue to read](https://dev.to/brunodrugowick/four-steps-to-extend-a-spring-data-jpa-repository-with-your-own-code-53b0)
## How do I secure this Spring Boot + Thymeleaf + Vue.js app?
Feb 12 '20
I have this app: Complete CRUD with Spring Boot, Vue.js, Axio... [continue to read](https://dev.to/brunodrugowick/how-do-i-secure-this-spring-boot-thymeleaf-vue-js-app-3f6h)
## Using JPQL on orm.xml file with Spring Data JPA
Feb 11 '20
This is the post #5 of the series "Querying your Spring Data JPA Repository". And this is a quick on... [continue to read](https://dev.to/brunodrugowick/using-jpql-on-orm-xml-file-with-spring-data-jpa-39ej)
## Complete CRUD with Spring Boot, Vue.js, Axios
Feb 9 '20
Following up on the last post of AQAP Series, here's the complete create-read-update-delete (CRUD) ap... [continue to read](https://dev.to/brunodrugowick/complete-crud-with-spring-boot-vue-js-axios-fg1)
## Spring Boot, Vue.js, Axios and Thymeleaf with Bootstrap in 4 commits
Feb 4 '20
Take a deep breath, hold... In 4 short and easy commits you'll get a Spring Boot app serving server-... [continue to read](https://dev.to/brunodrugowick/spring-boot-vue-js-axios-and-thymeleaf-with-bootstrap-in-4-commits-2b0l)
## Using JPQL with Spring Data JPA
Jan 30 '20
This is the post #4 of the series "Querying your Spring Data JPA Repository". Let's recap: by now yo... [continue to read](https://dev.to/brunodrugowick/using-jpql-with-spring-data-jpa-48c0)
## Complete API in 5 minutes with Spring Data REST - AQAP Series
Jan 21 '20
The following instructions will give you a complete REST API for two related resources in 5 minutes.... [continue to read](https://dev.to/brunodrugowick/complete-api-in-5-minutes-with-spring-data-rest-aqap-series-1ie8)
## Spring Data JPA Query Methods
Jan 15 '20
This is the post #3 of the series "Querying your Spring Data JPA Repository". If you're following th... [continue to read](https://dev.to/brunodrugowick/spring-data-jpa-query-methods-l43)
## Querying your Spring Data JPA Repository - Basic Setup
Jan 12 '20
Well, nothing is simple nowadays. I'm a fan of complexity, so here's not the place you'll find quick... [continue to read](https://dev.to/brunodrugowick/querying-your-spring-data-jpa-repository-basic-setup-1i8p)
## Querying your Spring Data JPA Repository - Introduction
Jan 6 '20
In this series of posts I'll show you several methods to query your Java JPA repositories using Sprin... [continue to read](https://dev.to/brunodrugowick/querying-your-spring-data-jpa-repository-introduction-4ani)
## Project Overview: the Basic Microservices Architecture with Spring Cloud
Dec 19 '19
This post presents the basic architecture of an online flower shop in a microservices architecture. I... [continue to read](https://dev.to/brunodrugowick/project-overview-the-basic-microservices-architecture-with-spring-cloud-2e8e)
## My first attempt at Generics...
Dec 18 '19
... it turns out I didn't need it! It's been a while that I wanted to try something with Generics. T... [continue to read](https://dev.to/brunodrugowick/my-first-attempt-at-generics-976)
## The SHAME ON ME fix
Dec 18 '19
I had a problem. On my 2-week journey into Javascript inspired by the Omnistack Week (a week where yo... [continue to read](https://dev.to/brunodrugowick/the-shame-on-me-fix-2aij)
## GitHub Pages and Google Domains together
Dec 18 '19
Oh, that annoyed me more than it should. Yes, it's quite easy to make your custom domain point to... [continue to read](https://dev.to/brunodrugowick/github-pages-and-google-domains-together-5ded)
## Adding SSH key to GitHub
Dec 18 '19
It's fairly easy to follow GitHub's official documentation on how to add your SSH key to your account... [continue to read](https://dev.to/brunodrugowick/adding-ssh-key-to-github-fem)
## How to Use GitHub Pages
Dec 18 '19
How to Use GitHub Pages GitHub Pages can serve static content for free using your GitHub a... [continue to read](https://dev.to/brunodrugowick/how-to-use-github-pages-j7d)
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>This is a simple <code>for</code> loop that goes through all of the posts that you saved to a <code>devPosts</code> file using the script that we created earlier.</p>
<p>The directive <code>site.data.devPosts</code> is what refers to that file. Everything that we access for a post in particular, like <code>devPost.description</code> or <code>devPost.url</code> is refering to the fields returned from the dev.to API.</p>
<h3>
<a name="4-automate-the-task-of-updating-your-website" href="#4-automate-the-task-of-updating-your-website">
</a>
4. Automate the task of updating your website
</h3>
<p>What we have so far is good enough, right? Every time you write something to dev.to you could go to your personal website repository, run the script and push a commit to let GitHub build and publish your website for you. </p>
<p>But we can go one step further and automate the whole thing with GitHub Actions! We'll create a workflow called Update Articles in your repository. Create a file <code>.github/workflows/update-articles.yml</code> in your repository with the following content:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight plaintext"><code>name: Update Articles
#### Thing to notice 1 ####
on:
schedule:
- cron: '30 23 * * *'
workflow_dispatch:
jobs:
run-script:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run script to get articles from dev.to
#### Thing to notice 2 ####
run: |
./scripts/post_files_from_GET_json.sh
- name: Commit to the repo
run: |
git config --global user.name 'Bruno Drugowick'
git config --global user.email 'brunodrugowick@users.noreply.github.com'
git add .
git commit -am "Update articles list"
git push
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<h4>
<a name="thing-to-notice-1" href="#thing-to-notice-1">
</a>
Thing to notice 1
</h4>
<p>This section ensures your workflow runs both on a <code>schedule</code> (every day at 11:30 PM) or whenever you want via <code>workflow_dispatch</code>. Here's what this will look like in your repository later:</p>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ehVJrxGe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/of1vlv0zfi4d4ybghh52.png" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ehVJrxGe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/of1vlv0zfi4d4ybghh52.png" alt="Running GitHub Action with workflow_dispatch option" loading="lazy" width="880" height="576" /></a></p>
<h4>
<a name="thing-to-notice-2" href="#thing-to-notice-2">
</a>
Thing to notice 2
</h4>
<p>This section is just instructing GitHub Actions to run your workflow on the local copy of the repository that was checked out one step above. </p>
<p>The rest of the script is a commit to the repository with the change. This simplified version makes sure we commit anything, even if none, but we could verify if there was a change before deciding to push a commit to the repository, for example.</p>
<h2>
<a name="conclusion" href="#conclusion">
</a>
Conclusion
</h2>
<p>There you have it, now whatever you write on dev.to is also mirrored on your personal website.</p>
<p>Please let me know in the comments if you know another solution for this "problem".</p>
<p>…</p>Distributed Tracing with Spring Cloud Sleuth and Zipkin - AQAP Series2020-07-26T14:11:39+00:002020-07-26T14:11:39+00:00https://drugowick.dev/2020/07/26/distributed-tracing-with-spring-cloud-sleuth-and-zipkin-3moo<style type="text/css" media="screen">
.card {
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
transition: 0.3s;
width: 100%;
}
.card:hover {
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
}
.container {
padding: 2px 16px;
}
</style>
<p><code>[spring, cloud, sleuth, zipkin]</code></p>
<div class="card">
<div class="container">
<h4><b><br />You'll have a better experience reading in DEV</b></h4>
<p><a href="https://dev.to/brunodrugowick/distributed-tracing-with-spring-cloud-sleuth-and-zipkin-3moo" target="_blank">Click here to continue reading this post there >></a></p>
<p>However, if you want to know more about the project to mirror my posts from DEV here (and why), go ahead and <a href="https://dev.to/brunodrugowick/distributed-tracing-with-spring-cloud-sleuth-and-zipkin-3moo" target="_blank">read more</a>.</p>
<p>You can continue to read here too, it's up to you... =]</p>
</div>
</div>
<p><br /></p>
<p>There's a lot going on related to cloud services, libraries, providers etc. In fact, everything cloud-related gets a lot of attention right now.</p>
<p>One thing I wanted to try was distribute tracing. Spring has a very interesting project under the <a href="https://spring.io/projects/spring-cloud">Spring Cloud umbrella</a>, called <a href="https://spring.io/projects/spring-cloud-sleuth">Spring Cloud Sleuth</a>, that auto-configures your app to work with distributed tracing tools.</p>
<p>Together with this project you have Spring Cloud Zipkin Client that takes your instrumented log information and sends it to a Zipkin server. The server provides a clean and easy to use interface to understand the relationships between your services.</p>
<h1>
<a name="the-demo" href="#the-demo">
</a>
The Demo
</h1>
<h2>
<a name="description" href="#description">
</a>
Description
</h2>
<p>To explore the projects I developed 3 "layers" of services that talk to each other. The first layer contain only one service (service-one) and requests data from the second layer, comprised of two services (service-two, service-three).</p>
<p>At this point we can talk about Spring Cloud Load Balancer. This project is used to configure client-side load-balancing on the service-one for the second layer of services. The picture below shows how the services relate to each other.</p>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BICIKCpv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5xnwc1qg4woi79wpxrvg.png" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BICIKCpv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5xnwc1qg4woi79wpxrvg.png" alt="Service dependency view" loading="lazy" width="880" height="229" /></a></p>
<p>Any service of the second layer then calls the service from the third layer (service-four). The list of services the request went through is put together and returned to the client by service-one. </p>
<p>The two possible responses to a call to <code>service-one/info</code> are:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight json"><code><span class="err">//Using</span><span class="w"> </span><span class="err">service-two</span><span class="w"> </span><span class="err">on</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">second</span><span class="w"> </span><span class="err">layer</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"info"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"Service 4"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Wow, request arrived this far!"</span><span class="p">,</span><span class="w">
</span><span class="nl">"Service 2"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ok"</span><span class="p">,</span><span class="w">
</span><span class="nl">"Service 1"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ok"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<div class="highlight js-code-highlight">
<pre class="highlight json"><code><span class="err">//Using</span><span class="w"> </span><span class="err">service-three</span><span class="w"> </span><span class="err">on</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">second</span><span class="w"> </span><span class="err">layer</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"info"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"Service 4"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Wow, request arrived this far!"</span><span class="p">,</span><span class="w">
</span><span class="nl">"Service 3"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ok"</span><span class="p">,</span><span class="w">
</span><span class="nl">"Service 1"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ok"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>Service-one will unevenly load-balance between service-two and service-three because there's a <code>Thread.sleep(200)</code> on service-three.</p>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QjEAIYiU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/14at6y5qpnt446ildrtd.png" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QjEAIYiU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/14at6y5qpnt446ildrtd.png" alt="Uneven balancing" loading="lazy" width="781" height="356" /></a></p>
<h2>
<a name="running" href="#running">
</a>
Running
</h2>
<p>To make it easy to run your own tests and see the distributed tracing in action, I've prepared a repository with the four services described above and a <code>.jar</code> for the Zipkin server.</p>
<div class="ltag-github-readme-tag">
<div class="readme-overview">
<h2>
<img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" loading="lazy" />
<a href="https://github.com/brunodrugowick">
brunodrugowick
</a> / <a style="font-weight: 600;" href="https://github.com/brunodrugowick/distributed-tracing-demo">
distributed-tracing-demo
</a>
</h2>
<h3>
A demonstration of the usage of Spring Cloud Sleuth and Zipkin to trace requests across Spring Boot services.
</h3>
</div>
</div>
<p>Clone it and just run <code>/scripts/start-all.sh</code>. If you have Java 11 and Apache Bench (and a GNU/Linux operating system), everything runs and perform 10 thousand requests to fill your Zipkin server with some data.</p>
<h2>
<a name="the-trace" href="#the-trace">
</a>
The trace
</h2>
<p>After waiting for a couple of minutes for the tests to run, the script instructs you to go to <code>http://localhost:9411</code>. Let's explore it a bit...</p>
<h3>
<a name="zipkin-server" href="#zipkin-server">
</a>
Zipkin server
</h3>
<p>After searching for traces by clicking on the search icon, that's what the homepage looks like:</p>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0Hw9Q2QD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/mec8314g16bbpqabjckb.png" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0Hw9Q2QD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/mec8314g16bbpqabjckb.png" alt="Zipkin homepage" loading="lazy" width="880" height="433" /></a></p>
<p>A very useful feature is the ability to filter traces based on the service name:</p>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dWmjtUQM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/koifoffbxrgv1bi1dvxl.png" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dWmjtUQM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/koifoffbxrgv1bi1dvxl.png" alt="Filter by service name" loading="lazy" width="687" height="328" /></a></p>
<p>Inspecting a trace gives you this view, where you can easily see how long the request took in every service:</p>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M7CZKWeh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/62qy4wu44lmlrpxiqq4o.png" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M7CZKWeh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/62qy4wu44lmlrpxiqq4o.png" alt="Inspecting one trace" loading="lazy" width="880" height="220" /></a></p>
<p>Finally, a very useful feature is understand your services dependencies using the Dependencies Page. Make sure you select a Start and End time and click the magnifier glass icon:</p>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M_rD4qHV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kfdv77n00bd4zcsaqsdb.png" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M_rD4qHV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kfdv77n00bd4zcsaqsdb.png" alt="Dependencies page" loading="lazy" width="880" height="432" /></a></p>
<h1>
<a name="aqap-series" href="#aqap-series">
</a>
AQAP Series
</h1>
<p>As Quickly As Possible (AQAP) is a series of quick posts on something I find interesting. I encourage (and take part on) the discussions on the comments to further explore the technology, library or code quickly explained here.</p>
<hr />
<p>Image by <a href="https://pixabay.com/pt/users/jaykingsta14-4885997/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=2358636">Jason King</a> por <a href="https://pixabay.com/pt/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=2358636">Pixabay</a></p>
<p>…</p>The most basic security for Spring Boot with Thymeleaf2020-03-25T02:54:09+00:002020-03-25T02:54:09+00:00https://drugowick.dev/2020/03/25/the-most-basic-security-for-spring-boot-with-thymeleaf-339h<style type="text/css" media="screen">
.card {
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
transition: 0.3s;
width: 100%;
}
.card:hover {
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
}
.container {
padding: 2px 16px;
}
</style>
<p><code>[spring, security, thymeleaf]</code></p>
<div class="card">
<div class="container">
<h4><b><br />You'll have a better experience reading in DEV</b></h4>
<p><a href="https://dev.to/brunodrugowick/the-most-basic-security-for-spring-boot-with-thymeleaf-339h" target="_blank">Click here to continue reading this post there >></a></p>
<p>However, if you want to know more about the project to mirror my posts from DEV here (and why), go ahead and <a href="https://dev.to/brunodrugowick/the-most-basic-security-for-spring-boot-with-thymeleaf-339h" target="_blank">read more</a>.</p>
<p>You can continue to read here too, it's up to you... =]</p>
</div>
</div>
<p><br /></p>
<p>I like to develop small proof of concept applications. Although just validating, some security stuff may be necessary sometimes. Most often than not I also want to have 2 or more users...</p>
<p>So if you're using Spring and Thymeleaf, for the most basic and quick setup for a Spring MVC web app, just do:</p>
<h2>
<a name="add-the-raw-pomxml-endraw-dependency" href="#add-the-raw-pomxml-endraw-dependency">
</a>
Add the <code>pom.xml</code> dependency
</h2>
<p>Just add this to the file:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight xml"><code><span class="nt"><dependency></span>
<span class="nt"><groupId></span>org.springframework.boot<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>spring-boot-starter-security<span class="nt"></artifactId></span>
<span class="nt"></dependency></span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<h2>
<a name="create-the-most-basic-security-config-ever" href="#create-the-most-basic-security-config-ever">
</a>
Create the most basic security config ever
</h2>
<div class="highlight js-code-highlight">
<pre class="highlight java"><code><span class="nd">@EnableWebSecurity</span>
<span class="nd">@Configuration</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">WebSecurityConfig</span> <span class="kd">extends</span> <span class="nc">WebSecurityConfigurerAdapter</span> <span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">configure</span><span class="o">(</span><span class="nc">AuthenticationManagerBuilder</span> <span class="n">auth</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">Exception</span> <span class="o">{</span>
<span class="n">auth</span>
<span class="o">.</span><span class="na">inMemoryAuthentication</span><span class="o">()</span>
<span class="o">.</span><span class="na">withUser</span><span class="o">(</span><span class="s">"username"</span><span class="o">).</span><span class="na">password</span><span class="o">(</span><span class="s">"{noop}password"</span><span class="o">).</span><span class="na">roles</span><span class="o">(</span><span class="s">"USER"</span><span class="o">).</span><span class="na">and</span><span class="o">()</span>
<span class="o">.</span><span class="na">withUser</span><span class="o">(</span><span class="s">"username2"</span><span class="o">).</span><span class="na">password</span><span class="o">(</span><span class="s">"{noop}password"</span><span class="o">).</span><span class="na">roles</span><span class="o">(</span><span class="s">"USER"</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<h2>
<a name="additional-stuff" href="#additional-stuff">
</a>
Additional stuff
</h2>
<p>Well, you're mostly done, but there're a few things that I believe are important to consider.</p>
<h3>
<a name="csrf-protection" href="#csrf-protection">
</a>
CSRF protection
</h3>
<p>The first thing is that with the current config you won't be able to make a HTTP POST request because Spring is automatically protecting your app from CSRF attacks. You must add the <code>csrf</code> token already provided by Spring when POSTing. </p>
<p>You do that by adding the following inside your <code><form></code> and <code></form></code> tags:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight xml"><code><span class="nt"><input</span> <span class="na">type=</span><span class="s">"hidden"</span> <span class="na">th:name=</span><span class="s">"${_csrf.parameterName}"</span> <span class="na">th:value=</span><span class="s">"${_csrf.token}"</span><span class="nt">/></span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<h3>
<a name="logout-link" href="#logout-link">
</a>
Logout link
</h3>
<p>The current configuration provides you a login page that may be enough for demonstrations. But having more than one user makes you want to logout and show some behavior with the other users.</p>
<p>For this, just add the following form somewhere in your app:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight html"><code><span class="nt"><div</span> <span class="na">class=</span><span class="s">"text-light"</span><span class="nt">></span>
<span class="nt"><form</span> <span class="na">action=</span><span class="s">"/logout"</span>
<span class="na">method=</span><span class="s">"post"</span><span class="nt">></span>
<span class="nt"><input</span> <span class="na">class=</span><span class="s">"btn btn-link"</span>
<span class="na">type=</span><span class="s">"submit"</span>
<span class="na">value=</span><span class="s">"Log out"</span> <span class="nt">/></span>
<span class="nt"><input</span> <span class="na">type=</span><span class="s">"hidden"</span>
<span class="na">th:name=</span><span class="s">"${_csrf.parameterName}"</span>
<span class="na">th:value=</span><span class="s">"${_csrf.token}"</span><span class="nt">/></span>
<span class="nt"></form></span>
<span class="nt"></div></span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<h3>
<a name="getting-the-logged-user" href="#getting-the-logged-user">
</a>
Getting the logged user
</h3>
<p>Finally, if you want to know which user is logged, inject a <code>Principal</code> instance on your controller methods. Here's an example:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight java"><code><span class="nd">@GetMapping</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">homePage</span><span class="o">(</span><span class="nc">Principal</span> <span class="n">principal</span><span class="o">,</span> <span class="nc">Model</span> <span class="n">model</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">String</span> <span class="n">username</span> <span class="o">=</span> <span class="n">principal</span><span class="o">.</span><span class="na">getName</span><span class="o">();</span>
<span class="n">model</span><span class="o">.</span><span class="na">addAttribute</span><span class="o">(</span><span class="s">"username"</span><span class="o">,</span> <span class="n">username</span><span class="o">);</span>
<span class="k">return</span> <span class="s">"index"</span><span class="o">;</span>
<span class="o">}</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>Now you can show the logged user right on your homepage.</p>
<h1>
<a name="aqap-series" href="#aqap-series">
</a>
AQAP Series
</h1>
<p>As Quickly As Possible (AQAP) is a series of quick posts on something I find interesting. I encourage (and take part on) the discussions on the comments to further explore the technology, library or code quickly explained here.</p>
<hr />
<p>Image by <a href="https://pixabay.com/pt/users/jaykingsta14-4885997/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=2358636">Jason King</a> por <a href="https://pixabay.com/pt/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=2358636">Pixabay</a></p>
<p>…</p>Three steps to extend a Spring Data JPA repository with your own code2020-02-21T01:37:14+00:002020-02-21T01:37:14+00:00https://drugowick.dev/2020/02/21/four-steps-to-extend-a-spring-data-jpa-repository-with-your-own-code-53b0<style type="text/css" media="screen">
.card {
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
transition: 0.3s;
width: 100%;
}
.card:hover {
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
}
.container {
padding: 2px 16px;
}
</style>
<p><code>[spring, repository, java, jpa]</code></p>
<div class="card">
<div class="container">
<h4><b><br />You'll have a better experience reading in DEV</b></h4>
<p><a href="https://dev.to/brunodrugowick/four-steps-to-extend-a-spring-data-jpa-repository-with-your-own-code-53b0" target="_blank">Click here to continue reading this post there >></a></p>
<p>However, if you want to know more about the project to mirror my posts from DEV here (and why), go ahead and <a href="https://dev.to/brunodrugowick/four-steps-to-extend-a-spring-data-jpa-repository-with-your-own-code-53b0" target="_blank">read more</a>.</p>
<p>You can continue to read here too, it's up to you... =]</p>
</div>
</div>
<p><br /></p>
<p>This is the post #6 of the series "Querying your Spring Data JPA Repository".</p>
<h1>
<a name="what-about-an-advanced-search" href="#what-about-an-advanced-search">
</a>
What about an advanced search?
</h1>
<p>What if you want to search by any field available on the Restaurant and combine then without having to select specific searches.</p>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--34wmF1Nc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/aex4l3r7r7zbngzlgif8.gif" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--34wmF1Nc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/aex4l3r7r7zbngzlgif8.gif" alt="Alt Text" loading="lazy" width="640" height="540" data-animated="true" /></a></p>
<p>Well, it's not Google, but it's powerful enough for a lot of use cases.</p>
<h1>
<a name="lets-prepare-the-app" href="#lets-prepare-the-app">
</a>
Let's prepare the app
</h1>
<p>Well, you know the drill. Let's build what's necessary for the app to work with this new advanced search option.</p>
<p>The search form looks like this:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight html"><code><span class="nt"><form</span> <span class="na">th:action=</span><span class="s">"@{/advancedSearch/perform}"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"card mb-auto"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"card-header"</span><span class="nt">></span>
<span class="nt"><h4></span>Filter Restaurants<span class="nt"></h4></span>
<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"card-body"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"form-group row small"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"col col-sm-4 float-left"</span><span class="nt">></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"col col-sm-8 float-right"</span><span class="nt">></span>
<span class="nt"><a</span> <span class="na">class=</span><span class="s">"float-right"</span> <span class="na">href=</span><span class="s">"/"</span><span class="nt">></span>Simple search<span class="nt"></a></span>
<span class="nt"></div></span>
<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"form-group row"</span><span class="nt">></span>
<span class="nt"><label</span> <span class="na">class=</span><span class="s">"col col-sm-4"</span> <span class="na">for=</span><span class="s">"name"</span><span class="nt">></span>Name<span class="nt"></label></span>
<span class="nt"><input</span> <span class="na">class=</span><span class="s">"form-control col-sm-8"</span> <span class="na">id=</span><span class="s">"name"</span> <span class="na">placeholder=</span><span class="s">"<empty>"</span> <span class="na">th:name=</span><span class="s">"name"</span>
<span class="na">th:value=</span><span class="s">"${search.name}"</span> <span class="na">type=</span><span class="s">"text"</span><span class="nt">/></span>
<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"form-group row"</span><span class="nt">></span>
<span class="nt"><label</span> <span class="na">class=</span><span class="s">"col col-sm-4"</span> <span class="na">for=</span><span class="s">"address"</span><span class="nt">></span>Address<span class="nt"></label></span>
<span class="nt"><input</span> <span class="na">class=</span><span class="s">"form-control col-sm-8"</span> <span class="na">id=</span><span class="s">"address"</span> <span class="na">placeholder=</span><span class="s">"<empty>"</span> <span class="na">th:name=</span><span class="s">"address"</span>
<span class="na">th:value=</span><span class="s">"${search.address}"</span> <span class="na">type=</span><span class="s">"text"</span><span class="nt">/></span>
<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"form-group row"</span><span class="nt">></span>
<span class="nt"><label</span> <span class="na">class=</span><span class="s">"col col-sm-4"</span> <span class="na">for=</span><span class="s">"minDeliveryFee"</span><span class="nt">></span>Delivery Fee<span class="nt"></label></span>
<span class="nt"><label</span> <span class="na">class=</span><span class="s">"col col-sm-1"</span> <span class="na">for=</span><span class="s">"minDeliveryFee"</span><span class="nt">></span>Min<span class="nt"></label></span>
<span class="nt"><input</span> <span class="na">class=</span><span class="s">"form-control col-sm-3"</span> <span class="na">id=</span><span class="s">"minDeliveryFee"</span> <span class="na">placeholder=</span><span class="s">"<min>"</span> <span class="na">th:name=</span><span class="s">"minDeliveryFee"</span>
<span class="na">th:value=</span><span class="s">"${search.minDeliveryFee}"</span> <span class="na">type=</span><span class="s">"text"</span><span class="nt">/></span>
<span class="nt"><label</span> <span class="na">class=</span><span class="s">"col col-sm-1"</span> <span class="na">for=</span><span class="s">"maxDeliveryFee"</span><span class="nt">></span>Max<span class="nt"></label></span>
<span class="nt"><input</span> <span class="na">class=</span><span class="s">"form-control col-sm-3"</span> <span class="na">id=</span><span class="s">"maxDeliveryFee"</span> <span class="na">placeholder=</span><span class="s">"<max>"</span> <span class="na">th:name=</span><span class="s">"maxDeliveryFee"</span>
<span class="na">th:value=</span><span class="s">"${search.maxDeliveryFee}"</span> <span class="na">type=</span><span class="s">"text"</span><span class="nt">/></span>
<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"form-group row"</span><span class="nt">></span>
<span class="nt"><label</span> <span class="na">class=</span><span class="s">"col col-sm-4"</span> <span class="na">for=</span><span class="s">"cuisine"</span><span class="nt">></span>Cuisine<span class="nt"></label></span>
<span class="nt"><input</span> <span class="na">class=</span><span class="s">"form-control col-sm-8"</span> <span class="na">id=</span><span class="s">"cuisine"</span> <span class="na">placeholder=</span><span class="s">"<empty>"</span> <span class="na">th:name=</span><span class="s">"cuisine"</span>
<span class="na">th:value=</span><span class="s">"${search.cuisine}"</span> <span class="na">type=</span><span class="s">"text"</span><span class="nt">/></span>
<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"form-group row"</span><span class="nt">></span>
<span class="nt"><label</span> <span class="na">class=</span><span class="s">"col col-sm-4"</span> <span class="na">for=</span><span class="s">"city"</span><span class="nt">></span>City<span class="nt"></label></span>
<span class="nt"><input</span> <span class="na">class=</span><span class="s">"form-control col-sm-8"</span> <span class="na">id=</span><span class="s">"city"</span> <span class="na">placeholder=</span><span class="s">"<empty>"</span> <span class="na">th:name=</span><span class="s">"city"</span>
<span class="na">th:value=</span><span class="s">"${search.city}"</span> <span class="na">type=</span><span class="s">"text"</span><span class="nt">/></span>
<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"form-group row"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"col col-sm-4"</span><span class="nt">></div></span>
<span class="nt"><input</span> <span class="na">class=</span><span class="s">"btn btn-primary col col-sm-8"</span> <span class="na">type=</span><span class="s">"submit"</span> <span class="na">value=</span><span class="s">"Submit"</span><span class="nt">></span>
<span class="nt"></div></span>
<span class="nt"></div></span>
<span class="nt"></div></span>
<span class="nt"></form></span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>We need new controller methods to handle the new page and the search operation:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight java"><code><span class="nd">@RequestMapping</span><span class="o">(</span><span class="s">"/advancedSearch"</span><span class="o">)</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">advancedSearch</span><span class="o">(</span><span class="nc">Model</span> <span class="n">model</span><span class="o">)</span> <span class="o">{</span>
<span class="n">model</span><span class="o">.</span><span class="na">addAttribute</span><span class="o">(</span><span class="s">"restaurants"</span><span class="o">,</span> <span class="n">restaurantRepository</span><span class="o">.</span><span class="na">findAll</span><span class="o">());</span>
<span class="n">model</span><span class="o">.</span><span class="na">addAttribute</span><span class="o">(</span><span class="s">"search"</span><span class="o">,</span> <span class="k">new</span> <span class="nc">AdvancedSearch</span><span class="o">());</span>
<span class="k">return</span> <span class="s">"advancedSearch"</span><span class="o">;</span>
<span class="o">}</span>
<span class="nd">@RequestMapping</span><span class="o">(</span><span class="s">"/advancedSearch/perform"</span><span class="o">)</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">advancedSearchWithQuery</span><span class="o">(</span><span class="nd">@ModelAttribute</span> <span class="nc">AdvancedSearch</span> <span class="n">advancedSearch</span><span class="o">,</span> <span class="nc">Model</span> <span class="n">model</span><span class="o">)</span> <span class="o">{</span>
<span class="n">model</span><span class="o">.</span><span class="na">addAttribute</span><span class="o">(</span><span class="s">"restaurants"</span><span class="o">,</span> <span class="n">restaurantRepository</span><span class="o">.</span><span class="na">advancedSearch</span><span class="o">(</span><span class="n">advancedSearch</span><span class="o">));</span>
<span class="n">model</span><span class="o">.</span><span class="na">addAttribute</span><span class="o">(</span><span class="s">"search"</span><span class="o">,</span> <span class="n">advancedSearch</span><span class="o">);</span>
<span class="k">return</span> <span class="s">"advancedSearch"</span><span class="o">;</span>
<span class="o">}</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aoGIl5HF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/jvo51qw5q568q0wyf5hb.gif" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aoGIl5HF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/jvo51qw5q568q0wyf5hb.gif" alt="Alt Text" loading="lazy" width="640" height="502" data-animated="true" /></a></p>
<p>Keep an eye on:</p>
<ul>
<li>The <code>@ModelAttribute</code> annotation: it maps the input to a new class called...</li>
<li>
<code>AdvancedSearch</code>. This is a simple bean with fields to hold data coming from the form. With the help of <a href="https://projectlombok.org/">Project Lombok</a> this class is <a href="https://github.com/brunodrugowick/jpa-queries-blog-post/blob/master/src/main/java/dev/drugowick/jpaqueriesblogpost/web/pages/dto/AdvancedSearch.java">quite simple</a>.
<ul>
<li>We use this class to pass data between the form and the app on both directions. This is how we can show the query inputs to the user even after the page is refreshed to show the search results (remember this is not your typical SPA, ok?).</li>
</ul>
</li>
</ul>
<h1>
<a name="custom-repository-methods" href="#custom-repository-methods">
</a>
Custom Repository Methods
</h1>
<p>But you may also have noticed that we are calling a new method on the <code>restaurantRepository</code> called <code>advancedSearch</code> passing the homonym <code>advancedSearch</code> object via parameter. No, that's not a Spring Data JPA default method (would be nice, hã?), but the ability to create our own custom methods is the powerful stuff we're learning here!</p>
<p>Let's see how in 3 steps.</p>
<h2>
<a name="step-1-create-a-new-interface-to-hold-the-method-declarations" href="#step-1-create-a-new-interface-to-hold-the-method-declarations">
</a>
Step 1: Create a new interface to hold the method declarations
</h2>
<div class="highlight js-code-highlight">
<pre class="highlight java"><code><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">CustomRestaurantRepository</span> <span class="o">{</span>
<span class="nc">List</span><span class="o"><</span><span class="nc">Restaurant</span><span class="o">></span> <span class="nf">advancedSearch</span><span class="o">(</span><span class="nc">AdvancedSearch</span> <span class="n">advancedSearch</span><span class="o">);</span>
<span class="o">}</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>Notice the definition of the <code>advancedSearch</code> method. That's all we need right now.</p>
<h2>
<a name="step-2-make-your-spring-data-jpa-repository-extend-your-new-interface" href="#step-2-make-your-spring-data-jpa-repository-extend-your-new-interface">
</a>
Step 2: Make your Spring Data JPA repository extend your new interface
</h2>
<div class="highlight js-code-highlight">
<pre class="highlight java"><code><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">RestaurantRepository</span> <span class="kd">extends</span> <span class="nc">JpaRepository</span><span class="o"><</span><span class="nc">Restaurant</span><span class="o">,</span> <span class="nc">Long</span><span class="o">>,</span> <span class="nc">CustomRestaurantRepository</span> <span class="o">{</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>Notice now that our repository extends both <code>JPARepository</code> (from Spring Data JPA project) and <code>CustomRestaurantRepository</code> (a class of our own to define repository methods). We can now call our new method, but what about its code?</p>
<h2>
<a name="step-3-implement-the-custom-method" href="#step-3-implement-the-custom-method">
</a>
Step 3: Implement the custom method
</h2>
<p>Now it's just a matter of implementing the code we need. We will create a <code>CustomRestaurantRepositoryImpl</code> class that implements our newly created <code>CustomRestaurantRepository</code> interface.<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight java"><code><span class="nd">@Repository</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">CustomRestaurantRepositoryImpl</span> <span class="kd">implements</span> <span class="nc">CustomRestaurantRepository</span> <span class="o">{</span>
<span class="nd">@PersistenceContext</span>
<span class="kd">private</span> <span class="nc">EntityManager</span> <span class="n">entityManager</span><span class="o">;</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="nc">List</span><span class="o"><</span><span class="nc">Restaurant</span><span class="o">></span> <span class="nf">advancedSearch</span><span class="o">(</span><span class="nc">AdvancedSearch</span> <span class="n">advancedSearch</span><span class="o">)</span> <span class="o">{</span>
<span class="kt">var</span> <span class="n">jpql</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">StringBuilder</span><span class="o">();</span>
<span class="n">jpql</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"from Restaurant where 1=1 "</span><span class="o">);</span>
<span class="kt">var</span> <span class="n">parameters</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">HashMap</span><span class="o"><</span><span class="nc">String</span><span class="o">,</span> <span class="nc">Object</span><span class="o">>();</span>
<span class="k">if</span> <span class="o">(</span><span class="nc">StringUtils</span><span class="o">.</span><span class="na">hasLength</span><span class="o">(</span><span class="n">advancedSearch</span><span class="o">.</span><span class="na">getName</span><span class="o">()))</span> <span class="o">{</span>
<span class="n">jpql</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"and name like :name "</span><span class="o">);</span>
<span class="n">parameters</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"name"</span><span class="o">,</span> <span class="s">"%"</span> <span class="o">+</span> <span class="n">advancedSearch</span><span class="o">.</span><span class="na">getName</span><span class="o">()</span> <span class="o">+</span> <span class="s">"%"</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="nc">StringUtils</span><span class="o">.</span><span class="na">hasLength</span><span class="o">(</span><span class="n">advancedSearch</span><span class="o">.</span><span class="na">getAddress</span><span class="o">()))</span> <span class="o">{</span>
<span class="n">jpql</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"and address like :address "</span><span class="o">);</span>
<span class="n">parameters</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"address"</span><span class="o">,</span> <span class="s">"%"</span> <span class="o">+</span> <span class="n">advancedSearch</span><span class="o">.</span><span class="na">getAddress</span><span class="o">()</span> <span class="o">+</span> <span class="s">"%"</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="n">advancedSearch</span><span class="o">.</span><span class="na">getMinDeliveryFee</span><span class="o">()</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="n">jpql</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"and deliveryFee >= :startFee "</span><span class="o">);</span>
<span class="n">parameters</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"startFee"</span><span class="o">,</span> <span class="n">advancedSearch</span><span class="o">.</span><span class="na">getMinDeliveryFee</span><span class="o">());</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="n">advancedSearch</span><span class="o">.</span><span class="na">getMaxDeliveryFee</span><span class="o">()</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="n">jpql</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"and deliveryFee <= :endingFee "</span><span class="o">);</span>
<span class="n">parameters</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"endingFee"</span><span class="o">,</span> <span class="n">advancedSearch</span><span class="o">.</span><span class="na">getMaxDeliveryFee</span><span class="o">());</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="nc">StringUtils</span><span class="o">.</span><span class="na">hasLength</span><span class="o">(</span><span class="n">advancedSearch</span><span class="o">.</span><span class="na">getCuisine</span><span class="o">()))</span> <span class="o">{</span>
<span class="n">jpql</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"and cuisine.name like :cuisine "</span><span class="o">);</span>
<span class="n">parameters</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"cuisine"</span><span class="o">,</span> <span class="s">"%"</span> <span class="o">+</span> <span class="n">advancedSearch</span><span class="o">.</span><span class="na">getCuisine</span><span class="o">()</span> <span class="o">+</span> <span class="s">"%"</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="nc">StringUtils</span><span class="o">.</span><span class="na">hasLength</span><span class="o">(</span><span class="n">advancedSearch</span><span class="o">.</span><span class="na">getCity</span><span class="o">()))</span> <span class="o">{</span>
<span class="n">jpql</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"and city like :city "</span><span class="o">);</span>
<span class="n">parameters</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"city"</span><span class="o">,</span> <span class="s">"%"</span> <span class="o">+</span> <span class="n">advancedSearch</span><span class="o">.</span><span class="na">getCity</span><span class="o">()</span> <span class="o">+</span> <span class="s">"%"</span><span class="o">);</span>
<span class="o">}</span>
<span class="nc">TypedQuery</span><span class="o"><</span><span class="nc">Restaurant</span><span class="o">></span> <span class="n">query</span> <span class="o">=</span> <span class="n">entityManager</span><span class="o">.</span><span class="na">createQuery</span><span class="o">(</span><span class="n">jpql</span><span class="o">.</span><span class="na">toString</span><span class="o">(),</span> <span class="nc">Restaurant</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="n">parameters</span><span class="o">.</span><span class="na">forEach</span><span class="o">((</span><span class="n">key</span><span class="o">,</span> <span class="n">value</span><span class="o">)</span> <span class="o">-></span> <span class="n">query</span><span class="o">.</span><span class="na">setParameter</span><span class="o">(</span><span class="n">key</span><span class="o">,</span> <span class="n">value</span><span class="o">));</span>
<span class="k">return</span> <span class="n">query</span><span class="o">.</span><span class="na">getResultList</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>A lot to unpack here:</p>
<ul>
<li>First, we get ahold of an <code>EntityManager</code> injecting it via <code>@PersistenceContext</code>. With this we can perform operations via JPA.</li>
<li>Then we override the <code>advancedSearch</code> method to:
<ul>
<li>Check each and every property of the <code>AdvancedSearch</code> object adding it, if not null, to a custom JPQL query.</li>
<li>Match the appropriate parameters. First, on a temporary map and then actually mapping on the query.</li>
<li>Execute the query returning the results.</li>
</ul>
</li>
<li>Last, but not least, the suffix <code>Impl</code> is what actually tell Spring Data JPA that this is a custom implementation of the existing <code>RestaurantRepository</code>. Adding our interface and making the Spring Data JPA interface extend is only to make the code readable. You should do this!</li>
</ul>
<h1>
<a name="additional-challenge" href="#additional-challenge">
</a>
Additional challenge
</h1>
<p>Notice that the example app has also an option to select the logical operator to use when performing the advanced search, <code>AND</code> or <code>OR</code>. You may want to try to implement it yourself, but if you don't want to, <a href="https://github.com/brunodrugowick/jpa-queries-blog-post/commit/a5d756ce3f968beeea536d0740005ae2e36c19a0">here's the implementation</a> for you.</p>
<p>That's the final result:</p>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--34wmF1Nc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/aex4l3r7r7zbngzlgif8.gif" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--34wmF1Nc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/aex4l3r7r7zbngzlgif8.gif" alt="Alt Text" loading="lazy" width="640" height="540" data-animated="true" /></a></p>
<h1>
<a name="the-example-app" href="#the-example-app">
</a>
The example app
</h1>
<p>The working app is <a href="https://jpa-queries-blog-post.herokuapp.com/">here</a> (wait for Heroku to load the app, it takes a few seconds on the free tier).</p>
<h1>
<a name="commits-related-to-this-post" href="#commits-related-to-this-post">
</a>
Commits related to this post
</h1>
<p>The preparation and core code is <a href="https://github.com/brunodrugowick/jpa-queries-blog-post/commit/b7fb9718824cfe9fa74113ed2ad772a32690a46f">here</a>.<br />
The logical operator addition is <a href="https://github.com/brunodrugowick/jpa-queries-blog-post/commit/a5d756ce3f968beeea536d0740005ae2e36c19a0">here</a>.<br />
And there's a UI improvement I did <a href="https://github.com/brunodrugowick/jpa-queries-blog-post/commit/06dbc0f30d2e108282176b3a2044bf7ef53e8357">here</a></p>
<div class="ltag-github-readme-tag">
<div class="readme-overview">
<h2>
<img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" loading="lazy" />
<a href="https://github.com/brunodrugowick">
brunodrugowick
</a> / <a style="font-weight: 600;" href="https://github.com/brunodrugowick/jpa-queries-blog-post">
jpa-queries-blog-post
</a>
</h2>
<h3>
A demo project for a blog post about (Spring Data) JPA.
</h3>
</div>
</div>
<p>…</p>How do I secure this Spring Boot + Thymeleaf + Vue.js app?2020-02-12T23:33:52+00:002020-02-12T23:33:52+00:00https://drugowick.dev/2020/02/12/how-do-i-secure-this-spring-boot-thymeleaf-vue-js-app-3f6h<style type="text/css" media="screen">
.card {
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
transition: 0.3s;
width: 100%;
}
.card:hover {
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
}
.container {
padding: 2px 16px;
}
</style>
<p><code>[spring, vue, java, help]</code></p>
<div class="card">
<div class="container">
<h4><b><br />You'll have a better experience reading in DEV</b></h4>
<p><a href="https://dev.to/brunodrugowick/how-do-i-secure-this-spring-boot-thymeleaf-vue-js-app-3f6h" target="_blank">Click here to continue reading this post there >></a></p>
<p>However, if you want to know more about the project to mirror my posts from DEV here (and why), go ahead and <a href="https://dev.to/brunodrugowick/how-do-i-secure-this-spring-boot-thymeleaf-vue-js-app-3f6h" target="_blank">read more</a>.</p>
<p>You can continue to read here too, it's up to you... =]</p>
</div>
</div>
<p><br /></p>
<p>I have this app:</p>
<div class="ltag__link">
<a href="/brunodrugowick" class="ltag__link__link">
<div class="ltag__link__pic">
<img src="https://res.cloudinary.com/practicaldev/image/fetch/s--U-wFRb7a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--LpCY0EbU--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/213112/5bb8eb2e-29a2-4307-be73-1ffdd76f8f9c.jpg" alt="brunodrugowick" loading="lazy" />
</div>
</a>
<a href="/brunodrugowick/complete-crud-with-spring-boot-vue-js-axios-fg1" class="ltag__link__link">
<div class="ltag__link__content">
<h2>Complete CRUD with Spring Boot, Vue.js, Axios</h2>
<h3>Bruno Drugowick ・ Feb 9 '20 ・ 4 min read</h3>
<div class="ltag__link__taglist">
<span class="ltag__link__tag">#java</span>
<span class="ltag__link__tag">#vue</span>
<span class="ltag__link__tag">#axios</span>
<span class="ltag__link__tag">#spring</span>
</div>
</div>
</a>
</div>
<p>And I'm wondering how do I properly secure it? The app has:</p>
<ul>
<li>Server-rendered pages with Thymeleaf.</li>
<li>An API providing data for the pages.</li>
</ul>
<p>What's the proper way to secure and its underlying limitations considering this architecture?</p>
<p>Some questions that pop on my mind, to help you understand why I'm asking:</p>
<ul>
<li>Can I go with Spring Security defaults (adding csrf token on my forms that POST/PUT with Vue.js)?</li>
<li>How do I integrate this with my DELETE via API, for example?</li>
<li>Should I disable csrf?</li>
<li>Does this architecture makes sense? What are the caveats?</li>
</ul>
<p>...</p>
<hr />
<p>Cover image from <a href="https://www.flickr.com/photos/140988606@N08/28124222664">Christoph Scholz</a>.</p>
<p>…</p>Using JPQL on orm.xml file with Spring Data JPA2020-02-11T01:06:39+00:002020-02-11T01:06:39+00:00https://drugowick.dev/2020/02/11/using-jpql-on-orm-xml-file-with-spring-data-jpa-39ej<style type="text/css" media="screen">
.card {
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
transition: 0.3s;
width: 100%;
}
.card:hover {
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
}
.container {
padding: 2px 16px;
}
</style>
<p><code>[spring, jpa, repository, xml]</code></p>
<div class="card">
<div class="container">
<h4><b><br />You'll have a better experience reading in DEV</b></h4>
<p><a href="https://dev.to/brunodrugowick/using-jpql-on-orm-xml-file-with-spring-data-jpa-39ej" target="_blank">Click here to continue reading this post there >></a></p>
<p>However, if you want to know more about the project to mirror my posts from DEV here (and why), go ahead and <a href="https://dev.to/brunodrugowick/using-jpql-on-orm-xml-file-with-spring-data-jpa-39ej" target="_blank">read more</a>.</p>
<p>You can continue to read here too, it's up to you... =]</p>
</div>
</div>
<p><br /></p>
<p>This is the post #5 of the series "Querying your Spring Data JPA Repository".</p>
<p>And this is a quick one.</p>
<h1>
<a name="remember-our-jpql" href="#remember-our-jpql">
</a>
Remember our JPQL?
</h1>
<p>Yeah, what if for some reason you want to externalize the query to a XML file. Why, you ask? Well, I'm not sure. More on that at the end of this post.</p>
<h1>
<a name="just-put-it-on-a-ormxml-file" href="#just-put-it-on-a-ormxml-file">
</a>
Just put it on a orm.xml file!
</h1>
<p>The file must be at <code>resources/META-INF/orm.xml</code>:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight xml"><code><span class="cp"><?xml version="1.0" encoding="UTF-8"?></span>
<span class="nt"><entity-mappings</span>
<span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span class="na">xmlns=</span><span class="s">"http://xmlns.jcp.org/xml/ns/persistence/orm"</span>
<span class="na">version=</span><span class="s">"2.2"</span><span class="nt">></span>
<span class="nt"><named-query</span> <span class="na">name=</span><span class="s">"Restaurant.activeGrabngoByCity"</span><span class="nt">></span>
<span class="nt"><query></span>from Restaurant r where r.active = true and r.grabngo = true and r.city like concat('%', :city, '%')<span class="nt"></query></span>
<span class="nt"></named-query></span>
<span class="c"><!-- You can have more...
<named-query name="Restaurant.otherMethodName">
<query>from Restaurant where whatever ... </query>
</named-query>
--></span>
<span class="nt"></entity-mappings></span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>As always, let's highlight some things:</p>
<ul>
<li>The <code>entity-mappings</code> tag must be exactly what you see here. Just copy and paste this part.</li>
<li>The attribute <code>name</code> on the tag <code>named-query</code> specifies the method the query will relate to. Whenever the method is called this JPA query is executed.</li>
<li>The query had to go over a small change, did you notice? We added a <code>concat</code> to form the last portion of the <code>where</code>clause. Without it an <code>unexpected token: %</code> error would be thrown.</li>
</ul>
<p>You'd also have to change other things, like replace <code><</code> with <code>&lt;</code> and <code>></code> with <code>&gt;</code>, which is the way to use those characters in XML.</p>
<h1>
<a name="the-benefit" href="#the-benefit">
</a>
The benefit
</h1>
<p>I can think of one benefit which is the ability to change the query without recompiling the application. This is questionable, though, but... well... it's possible.</p>
<p>To be honest here, there's way more you can do on this file related to persistence and JPA. In fact, you could configure everything JPA-related on this file and ditch annotations. Everybody, though, is moving out from XML-based configuration to Annotations-based configuration.</p>
<p>Yet, this is still something to learn, I believe. I just like how complicated things get over time, and this is one manifestation of this complexity.</p>
<h1>
<a name="the-example-app" href="#the-example-app">
</a>
The example app
</h1>
<p>The working app is <a href="https://jpa-queries-blog-post.herokuapp.com/">here</a> (wait for Heroku to load the app, it takes a few seconds on the free tier).</p>
<h1>
<a name="commits-related-to-this-post" href="#commits-related-to-this-post">
</a>
Commits related to this post
</h1>
<p>There's only <a href="https://github.com/brunodrugowick/jpa-queries-blog-post/commit/8548e75043ede44252d2c9514c6c12e7993b32a6">this</a> (and I promptly reverted afterwards).</p>
<div class="ltag-github-readme-tag">
<div class="readme-overview">
<h2>
<img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" loading="lazy" />
<a href="https://github.com/brunodrugowick">
brunodrugowick
</a> / <a style="font-weight: 600;" href="https://github.com/brunodrugowick/jpa-queries-blog-post">
jpa-queries-blog-post
</a>
</h2>
<h3>
A demo project for a blog post about (Spring Data) JPA.
</h3>
</div>
</div>
<p>…</p>Complete CRUD with Spring Boot, Vue.js, Axios2020-02-09T02:35:04+00:002020-02-09T02:35:04+00:00https://drugowick.dev/2020/02/09/complete-crud-with-spring-boot-vue-js-axios-fg1<style type="text/css" media="screen">
.card {
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
transition: 0.3s;
width: 100%;
}
.card:hover {
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
}
.container {
padding: 2px 16px;
}
</style>
<p><code>[java, vue, axios, spring]</code></p>
<div class="card">
<div class="container">
<h4><b><br />You'll have a better experience reading in DEV</b></h4>
<p><a href="https://dev.to/brunodrugowick/complete-crud-with-spring-boot-vue-js-axios-fg1" target="_blank">Click here to continue reading this post there >></a></p>
<p>However, if you want to know more about the project to mirror my posts from DEV here (and why), go ahead and <a href="https://dev.to/brunodrugowick/complete-crud-with-spring-boot-vue-js-axios-fg1" target="_blank">read more</a>.</p>
<p>You can continue to read here too, it's up to you... =]</p>
</div>
</div>
<p><br /></p>
<p>Following up on the last post of AQAP Series, here's the complete create-read-update-delete (CRUD) app relying on Spring (Boot), Vue.js and Axios.</p>
<p>See it in action:</p>
<p><iframe width="710" height="399" src="https://www.youtube.com/embed/72TV4XjlsCU" allowfullscreen="" loading="lazy">
</iframe>
</p>
<p>I didn't mention Thymeleaf because there's no changes to the pages served by the back-end on this post.</p>
<p>I'll illustrate the code using the Role entity, but as always the complete code and the app running is available at the end.</p>
<p>Without further ado...</p>
<h1>
<a name="adding-rest-operations" href="#adding-rest-operations">
</a>
Adding REST operations
</h1>
<p>We start adding two new operations on the <code>RoleController.java</code>:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight java"><code><span class="nd">@PostMapping</span><span class="o">(</span><span class="s">"roles"</span><span class="o">)</span>
<span class="kd">public</span> <span class="nc">Role</span> <span class="nf">save</span><span class="o">(</span><span class="nd">@RequestBody</span> <span class="nc">Role</span> <span class="n">role</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">roleRepository</span><span class="o">.</span><span class="na">save</span><span class="o">(</span><span class="n">role</span><span class="o">);</span>
<span class="o">}</span>
<span class="nd">@DeleteMapping</span><span class="o">(</span><span class="s">"roles/{id}"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">get</span><span class="o">(</span><span class="nd">@PathVariable</span> <span class="nc">Long</span> <span class="n">id</span><span class="o">)</span> <span class="o">{</span>
<span class="n">roleRepository</span><span class="o">.</span><span class="na">deleteById</span><span class="o">(</span><span class="n">id</span><span class="o">);</span>
<span class="o">}</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>The <code>save</code> method takes care of both <code>create</code> and <code>update</code> operations. Spring is smart enough to update when there's an ID present and to create a new entity otherwise.</p>
<h1>
<a name="the-role-form" href="#the-role-form">
</a>
The Role Form
</h1>
<p>This is our HTML form now:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight html"><code><span class="nt"><form</span> <span class="na">v-on:submit.prevent=</span><span class="s">"postRole"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"card mb-auto"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">aria-controls=</span><span class="s">"roleForm"</span> <span class="na">aria-expanded=</span><span class="s">"false"</span> <span class="na">class=</span><span class="s">"card-header"</span> <span class="na">data-target=</span><span class="s">"#roleForm"</span>
<span class="na">data-toggle=</span><span class="s">"collapse"</span> <span class="na">id=</span><span class="s">"formHeader"</span> <span class="na">style=</span><span class="s">"cursor: pointer"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"float-left"</span><span class="nt">></span>New/Edit Role<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"float-right"</span><span class="nt">></span>+<span class="nt"></div></span>
<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"card card-body collapse"</span> <span class="na">id=</span><span class="s">"roleForm"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"form-group row"</span><span class="nt">></span>
<span class="nt"><label</span> <span class="na">for=</span><span class="s">"roleName"</span> <span class="na">class=</span><span class="s">"col-sm-4 col-form-label"</span><span class="nt">></span>Role Name<span class="nt"></label></span>
<span class="nt"><input</span> <span class="na">id=</span><span class="s">"roleId"</span> <span class="na">type=</span><span class="s">"hidden"</span> <span class="na">v-model=</span><span class="s">"role_id"</span><span class="nt">></span>
<span class="nt"><input</span> <span class="na">id=</span><span class="s">"roleName"</span> <span class="na">class=</span><span class="s">"form-control col-sm-8"</span> <span class="na">placeholder=</span><span class="s">"Role Name"</span> <span class="na">type=</span><span class="s">"text"</span>
<span class="na">v-model=</span><span class="s">"role_name"</span><span class="nt">/></span>
<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"form-group row"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"col col-sm-4"</span><span class="nt">></div></span>
<span class="nt"><input</span> <span class="na">class=</span><span class="s">"btn btn-primary col col-sm-8"</span> <span class="na">type=</span><span class="s">"submit"</span> <span class="na">value=</span><span class="s">"Save"</span><span class="nt">></span>
<span class="nt"></div></span>
<span class="nt"></div></span>
<span class="nt"></div></span>
<span class="nt"></form></span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>Two things to notice here:</p>
<ul>
<li>
<code>v-on:submit.prevent="postRole"</code> is a Vue.js tag to specify the method to run when submitting the form and to prevent the default behaviour of page reloading on submit.</li>
<li>
<code>v-model</code> is another Vue.js tag. This binds an input with Vue.js data.</li>
</ul>
<h1>
<a name="new-edit-and-delete-buttons" href="#new-edit-and-delete-buttons">
</a>
New Edit and Delete buttons
</h1>
<p>On the <code>Actions</code> column of our HTML table, just add two new buttons:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight html"><code><span class="nt"><td></span>
<span class="nt"><button</span> <span class="na">class=</span><span class="s">"btn btn-primary"</span> <span class="na">v-on:click=</span><span class="s">"editRole(role)"</span><span class="nt">></span>Edit<span class="nt"></button></span>
<span class="nt"><button</span> <span class="na">class=</span><span class="s">"btn btn-danger"</span> <span class="na">v-on:click=</span><span class="s">"deleteRole(role)"</span><span class="nt">></span>Delete<span class="nt"></button></span>
<span class="nt"></td></span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>Notice the same <code>v-on</code> tag, but now with an action of <code>click</code>. This binds the button click to a Vue.js method.</p>
<h1>
<a name="the-vuejs-magic-again" href="#the-vuejs-magic-again">
</a>
The Vue.js Magic... again.
</h1>
<p>Our Vue.js script is now a little scary:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight javascript"><code><span class="o"><</span><span class="nx">script</span><span class="o">></span>
<span class="kd">var</span> <span class="nx">app</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Vue</span><span class="p">({</span>
<span class="na">el</span><span class="p">:</span> <span class="dl">'</span><span class="s1">#main</span><span class="dl">'</span><span class="p">,</span>
<span class="nx">data</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">{</span>
<span class="na">roles</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="na">role_id</span><span class="p">:</span> <span class="dl">''</span><span class="p">,</span>
<span class="na">role_name</span><span class="p">:</span> <span class="dl">''</span><span class="p">,</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="nx">mounted</span><span class="p">(){</span>
<span class="k">this</span><span class="p">.</span><span class="nx">getRoles</span><span class="p">();</span>
<span class="p">},</span>
<span class="na">methods</span><span class="p">:</span> <span class="p">{</span>
<span class="na">getRoles</span><span class="p">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">axios</span>
<span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">/api/v1/roles</span><span class="dl">"</span><span class="p">)</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">response</span> <span class="o">=></span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">roles</span> <span class="o">=</span> <span class="nx">response</span><span class="p">.</span><span class="nx">data</span><span class="p">))</span>
<span class="p">},</span>
<span class="na">postRole</span><span class="p">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Creating</span>
<span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">role_id</span> <span class="o">==</span> <span class="dl">''</span> <span class="o">||</span> <span class="k">this</span><span class="p">.</span><span class="nx">role_id</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">axios</span>
<span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="dl">"</span><span class="s2">/api/v1/roles</span><span class="dl">"</span><span class="p">,</span> <span class="p">{</span>
<span class="dl">"</span><span class="s2">name</span><span class="dl">"</span><span class="p">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">role_name</span><span class="p">,</span>
<span class="p">})</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">savedRole</span> <span class="o">=></span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">roles</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">savedRole</span><span class="p">.</span><span class="nx">data</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">role_name</span> <span class="o">=</span> <span class="dl">''</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">role_id</span> <span class="o">=</span> <span class="dl">''</span><span class="p">;</span>
<span class="p">})</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="c1">// Updating</span>
<span class="nx">axios</span>
<span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="dl">"</span><span class="s2">/api/v1/roles</span><span class="dl">"</span><span class="p">,</span> <span class="p">{</span>
<span class="dl">"</span><span class="s2">id</span><span class="dl">"</span><span class="p">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">role_id</span><span class="p">,</span>
<span class="dl">"</span><span class="s2">name</span><span class="dl">"</span><span class="p">:</span> <span class="k">this</span><span class="p">.</span><span class="nx">role_name</span><span class="p">,</span>
<span class="p">})</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">savedRole</span> <span class="o">=></span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">getRoles</span><span class="p">();</span>
<span class="k">this</span><span class="p">.</span><span class="nx">role_name</span> <span class="o">=</span> <span class="dl">''</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">role_id</span> <span class="o">=</span> <span class="dl">''</span><span class="p">;</span>
<span class="p">})</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="na">editRole</span><span class="p">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">role</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">role_id</span> <span class="o">=</span> <span class="nx">role</span><span class="p">.</span><span class="nx">id</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">role_name</span> <span class="o">=</span> <span class="nx">role</span><span class="p">.</span><span class="nx">name</span><span class="p">;</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">roleForm</span><span class="dl">'</span><span class="p">).</span><span class="nx">setAttribute</span><span class="p">(</span><span class="dl">"</span><span class="s2">class</span><span class="dl">"</span><span class="p">,</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">roleForm</span><span class="dl">'</span><span class="p">).</span><span class="nx">getAttribute</span><span class="p">(</span><span class="dl">"</span><span class="s2">class</span><span class="dl">"</span><span class="p">)</span> <span class="o">+</span> <span class="dl">"</span><span class="s2"> show</span><span class="dl">"</span><span class="p">);</span>
<span class="p">},</span>
<span class="na">deleteRole</span><span class="p">:</span> <span class="k">async</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">role</span><span class="p">)</span> <span class="p">{</span>
<span class="k">await</span> <span class="nx">axios</span>
<span class="p">.</span><span class="k">delete</span><span class="p">(</span><span class="dl">"</span><span class="s2">/api/v1/roles/</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">role</span><span class="p">.</span><span class="nx">id</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">getRoles</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="p">})</span>
<span class="o"><</span><span class="sr">/script</span><span class="err">>
</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>But it's quite simple, actually. Let's explore what matters:</p>
<ul>
<li>
<code>el: '#main'</code> specifies that Vue.js is going to operate on this HTML element id. In our case this is <code>div</code> containing the form and table.</li>
<li>Inside <code>data()</code> we can specify variables that we are going to manipulate on the script and that the user may interact with. In our case notice that we have defined variables that represent the content of the form that the user interacts with.</li>
<li>
<code>mounted()</code> is called when Vue.js is ready (mounted on the element specified in <code>el</code> above). Here we call a method <code>getRoles()</code>. This method requests data to the API and sets it to a variable that is used to create the table of contents (using <code>v-for</code> explained on the last post).</li>
<li>
<code>methods</code> contains all the methods that interact with the API. Notice how they equate to the CRUD operations:
<ul>
<li>
<code>getRoles</code> is the <code>read</code> operation.</li>
<li>
<code>postRole</code> is the <code>create</code> operation.</li>
<li>
<code>editRole</code> is the <code>update</code> operation.</li>
<li>
<code>deleteRole</code> is the <code>delete</code> operation.</li>
</ul>
</li>
</ul>
<h1>
<a name="the-app" href="#the-app">
</a>
The app
</h1>
<p>You can see the app running <a href="https://spring-thymeleaf-vue-crud.herokuapp.com/">here</a> (slightly modified since this is an ongoing analysis).</p>
<p>The repository and the aforementioned commits, also slightly modified, <a href="https://github.com/brunodrugowick/spring-thymeleaf-vue-crud-example/commits/master">here</a>.</p>
<div class="ltag-github-readme-tag">
<div class="readme-overview">
<h2>
<img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" loading="lazy" />
<a href="https://github.com/brunodrugowick">
brunodrugowick
</a> / <a style="font-weight: 600;" href="https://github.com/brunodrugowick/spring-thymeleaf-vue-crud-example">
spring-thymeleaf-vue-crud-example
</a>
</h2>
<h3>
Complete CRUD example project with Spring Boot, Thymeleaf, Vue.js and Axios.
</h3>
</div>
</div>
<h1>
<a name="aqap-series" href="#aqap-series">
</a>
AQAP Series
</h1>
<p>As Quickly As Possible (AQAP) is a series of quick posts on something I find interesting. I encourage (and take part on) the discussions on the comments to further explore the technology, library or code quickly explained here.</p>
<hr />
<p>Image by <a href="https://pixabay.com/pt/users/jaykingsta14-4885997/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=2358636">Jason King</a> por <a href="https://pixabay.com/pt/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=2358636">Pixabay</a></p>
<p>…</p>Spring Boot, Vue.js, Axios and Thymeleaf with Bootstrap in 4 commits2020-02-04T05:12:42+00:002020-02-04T05:12:42+00:00https://drugowick.dev/2020/02/04/spring-boot-vue-js-axios-and-thymeleaf-with-bootstrap-in-4-commits-2b0l<style type="text/css" media="screen">
.card {
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
transition: 0.3s;
width: 100%;
}
.card:hover {
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
}
.container {
padding: 2px 16px;
}
</style>
<p><code>[beginners, vue, axios, spring]</code></p>
<div class="card">
<div class="container">
<h4><b><br />You'll have a better experience reading in DEV</b></h4>
<p><a href="https://dev.to/brunodrugowick/spring-boot-vue-js-axios-and-thymeleaf-with-bootstrap-in-4-commits-2b0l" target="_blank">Click here to continue reading this post there >></a></p>
<p>However, if you want to know more about the project to mirror my posts from DEV here (and why), go ahead and <a href="https://dev.to/brunodrugowick/spring-boot-vue-js-axios-and-thymeleaf-with-bootstrap-in-4-commits-2b0l" target="_blank">read more</a>.</p>
<p>You can continue to read here too, it's up to you... =]</p>
</div>
</div>
<p><br /></p>
<p>Take a deep breath, hold...</p>
<p>In 4 short and easy commits you'll get a Spring Boot app serving server-side rendered pages via Thymeleaf (with Bootstrap) and Vue.js scripts making use of Axios to make asynchronous requests to the server to update pages with data without reloading the whole page from the server again.</p>
<p>... release! Explaining the previous phrase may take longer than to implement it. So let's do it.</p>
<h1>
<a name="commit-1-spring-initializr" href="#commit-1-spring-initializr">
</a>
Commit #1 - Spring Initializr
</h1>
<p>This one is easy. The first commit is the base Spring Boot application from Spring Initializr + a few extras. </p>
<p>You can generate your own version downloading <a href="https://start.spring.io/#!type=maven-project&language=java&platformVersion=2.2.4.RELEASE&packaging=jar&jvmVersion=1.8&groupId=com.example&artifactId=demo&name=demo&description=Demo%20project%20for%20Spring%20Boot&packageName=com.example.demo&dependencies=web,data-jpa,thymeleaf,h2,lombok,devtools">from this link</a> (already populated with some dependencies).</p>
<p>Add the following to the <code>pom.xml</code> file after loading the project in your preferred IDE:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight xml"><code><span class="nt"><dependency></span>
<span class="nt"><groupId></span>org.webjars<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>bootstrap<span class="nt"></artifactId></span>
<span class="nt"><version></span>4.4.1<span class="nt"></version></span>
<span class="nt"></dependency></span>
<span class="nt"><dependency></span>
<span class="nt"><groupId></span>org.webjars<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>webjars-locator<span class="nt"></artifactId></span>
<span class="nt"><version></span>0.38<span class="nt"></version></span>
<span class="nt"></dependency></span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<h1>
<a name="commit-2-the-base-app" href="#commit-2-the-base-app">
</a>
Commit #2 - The base app
</h1>
<p>This commit may be long but it's not complicated at all. It adds Role and User entities:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight java"><code><span class="nd">@Entity</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s">"role"</span><span class="o">)</span>
<span class="nd">@Data</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Role</span> <span class="o">{</span>
<span class="nd">@Id</span>
<span class="nd">@GeneratedValue</span><span class="o">(</span><span class="n">strategy</span> <span class="o">=</span> <span class="nc">GenerationType</span><span class="o">.</span><span class="na">IDENTITY</span><span class="o">)</span>
<span class="kd">private</span> <span class="kt">long</span> <span class="n">id</span><span class="o">;</span>
<span class="kd">private</span> <span class="nc">String</span> <span class="n">name</span><span class="o">;</span>
<span class="o">}</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<div class="highlight js-code-highlight">
<pre class="highlight java"><code><span class="nd">@Entity</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s">"user"</span><span class="o">)</span>
<span class="nd">@Data</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">User</span> <span class="o">{</span>
<span class="nd">@Id</span>
<span class="nd">@GeneratedValue</span><span class="o">(</span><span class="n">strategy</span> <span class="o">=</span> <span class="nc">GenerationType</span><span class="o">.</span><span class="na">IDENTITY</span><span class="o">)</span>
<span class="kd">private</span> <span class="kt">long</span> <span class="n">id</span><span class="o">;</span>
<span class="kd">private</span> <span class="nc">String</span> <span class="n">firstName</span><span class="o">;</span>
<span class="kd">private</span> <span class="nc">String</span> <span class="n">lastName</span><span class="o">;</span>
<span class="nd">@ManyToOne</span>
<span class="nd">@JoinColumn</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s">"role_id"</span><span class="o">)</span>
<span class="kd">private</span> <span class="nc">Role</span> <span class="n">role</span><span class="o">;</span>
<span class="o">}</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>And their corresponding Spring Data JPA repositories:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight java"><code><span class="nd">@Repository</span>
<span class="kd">public</span> <span class="kd">interface</span> <span class="nc">RoleRepository</span> <span class="kd">extends</span> <span class="nc">JpaRepository</span><span class="o"><</span><span class="nc">Role</span><span class="o">,</span> <span class="nc">Long</span><span class="o">></span> <span class="o">{</span>
<span class="o">}</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<div class="highlight js-code-highlight">
<pre class="highlight java"><code><span class="nd">@Repository</span>
<span class="kd">public</span> <span class="kd">interface</span> <span class="nc">UserRepository</span> <span class="kd">extends</span> <span class="nc">JpaRepository</span><span class="o"><</span><span class="nc">User</span><span class="o">,</span> <span class="nc">Long</span><span class="o">></span> <span class="o">{</span>
<span class="o">}</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>Adds a controller to provide a server-side rendered page via Thymeleaf:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight java"><code><span class="nd">@Controller</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">MainController</span> <span class="o">{</span>
<span class="nd">@RequestMapping</span><span class="o">(</span><span class="s">"/"</span><span class="o">)</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">index</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="s">"index"</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>And the <code>index.html</code> page itself on the <code>resources/templates</code> folder:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight html"><code><span class="cp"><!DOCTYPE html></span>
<span class="nt"><html</span> <span class="na">lang=</span><span class="s">"en"</span> <span class="na">xmlns:th=</span><span class="s">"http://www.thymeleaf.org"</span><span class="nt">></span>
<span class="nt"><head></span>
<span class="nt"><link</span> <span class="na">href=</span><span class="s">'/webjars/bootstrap/css/bootstrap.min.css'</span> <span class="na">rel=</span><span class="s">'stylesheet'</span><span class="nt">></span>
<span class="nt"><meta</span> <span class="na">charset=</span><span class="s">"UTF-8"</span><span class="nt">></span>
<span class="nt"><title></span>Home<span class="nt"></title></span>
<span class="nt"></head></span>
<span class="nt"><body></span>
<span class="nt"><div</span> <span class="na">th:replace=</span><span class="s">"fragments/header :: header"</span><span class="nt">></div></span>
<span class="nt"><script </span><span class="na">src=</span><span class="s">"/webjars/jquery/jquery.min.js"</span><span class="nt">></script></span>
<span class="nt"><script </span><span class="na">src=</span><span class="s">"/webjars/bootstrap/js/bootstrap.min.js"</span><span class="nt">></script></span>
<span class="nt"></body></span>
<span class="nt"></html></span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>Did you notice the <code><div th:replace...</code>? This is a neat feature of Thymeleaf to reuse code. Here it goes the reusable <code>header.html</code> on the <code>resources/templates/fragments</code> folder:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight html"><code><span class="cp"><!DOCTYPE html></span>
<span class="nt"><html</span> <span class="na">lang=</span><span class="s">"en"</span> <span class="na">xmlns:th=</span><span class="s">"http://www.thymeleaf.org"</span><span class="nt">></span>
<span class="nt"><head></span>
<span class="nt"><link</span> <span class="na">href=</span><span class="s">'/webjars/bootstrap/css/bootstrap.min.css'</span> <span class="na">rel=</span><span class="s">'stylesheet'</span><span class="nt">></span>
<span class="nt"><meta</span> <span class="na">charset=</span><span class="s">"UTF-8"</span><span class="nt">></span>
<span class="nt"><title></span>Restaurants<span class="nt"></title></span>
<span class="nt"></head></span>
<span class="nt"><body></span>
<span class="nt"><nav</span> <span class="na">class=</span><span class="s">"navbar navbar-expand-lg navbar-dark bg-dark"</span> <span class="na">th:fragment=</span><span class="s">"header"</span><span class="nt">></span>
<span class="nt"><a</span> <span class="na">class=</span><span class="s">"navbar-brand"</span> <span class="na">href=</span><span class="s">"/"</span><span class="nt">></span>Home<span class="nt"></a></span>
<span class="nt"><button</span> <span class="na">aria-controls=</span><span class="s">"navbarSupportedContent"</span> <span class="na">aria-expanded=</span><span class="s">"false"</span> <span class="na">aria-label=</span><span class="s">"Toggle navigation"</span>
<span class="na">class=</span><span class="s">"navbar-toggler"</span> <span class="na">data-target=</span><span class="s">"#navbarSupportedContent"</span> <span class="na">data-toggle=</span><span class="s">"collapse"</span> <span class="na">type=</span><span class="s">"button"</span><span class="nt">></span>
<span class="nt"><span</span> <span class="na">class=</span><span class="s">"navbar-toggler-icon"</span><span class="nt">></span></span>
<span class="nt"></button></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"collapse navbar-collapse"</span> <span class="na">id=</span><span class="s">"navbarSupportedContent"</span><span class="nt">></span>
<span class="nt"><ul</span> <span class="na">class=</span><span class="s">"navbar-nav mr-auto"</span><span class="nt">></span>
<span class="nt"><li</span> <span class="na">class=</span><span class="s">"nav-item dropdown"</span><span class="nt">></span>
<span class="nt"><a</span> <span class="na">aria-expanded=</span><span class="s">"false"</span> <span class="na">aria-haspopup=</span><span class="s">"true"</span> <span class="na">class=</span><span class="s">"nav-link dropdown-toggle"</span> <span class="na">data-toggle=</span><span class="s">"dropdown"</span>
<span class="na">href=</span><span class="s">"#"</span> <span class="na">id=</span><span class="s">"navbarDropdown"</span> <span class="na">role=</span><span class="s">"button"</span><span class="nt">></span>
Entities
<span class="nt"></a></span>
<span class="nt"><div</span> <span class="na">aria-labelledby=</span><span class="s">"navbarDropdown"</span> <span class="na">class=</span><span class="s">"dropdown-menu"</span><span class="nt">></span>
<span class="nt"><a</span> <span class="na">class=</span><span class="s">"dropdown-item"</span> <span class="na">href=</span><span class="s">"/users"</span><span class="nt">></span>Users<span class="nt"></a></span>
<span class="nt"><a</span> <span class="na">class=</span><span class="s">"dropdown-item"</span> <span class="na">href=</span><span class="s">"/roles"</span><span class="nt">></span>Roles<span class="nt"></a></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"dropdown-divider"</span><span class="nt">></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"dropdown-item-text p-4 text-muted"</span> <span class="na">style=</span><span class="s">"max-width: 200px;"</span><span class="nt">></span>
<span class="nt"><p></span>
Administrative pages to list, edit, create and remove entities.
<span class="nt"></p></span>
<span class="nt"></div></span>
<span class="nt"></div></span>
<span class="nt"></li></span>
<span class="nt"></ul></span>
<span class="nt"><form</span> <span class="na">class=</span><span class="s">"form-inline my-2 my-lg-0"</span><span class="nt">></span>
<span class="nt"><input</span> <span class="na">aria-label=</span><span class="s">"Search"</span> <span class="na">class=</span><span class="s">"form-control mr-sm-2"</span> <span class="na">placeholder=</span><span class="s">"Search"</span> <span class="na">type=</span><span class="s">"search"</span><span class="nt">></span>
<span class="nt"><button</span> <span class="na">class=</span><span class="s">"btn btn-outline-success my-2 my-sm-0"</span> <span class="na">type=</span><span class="s">"submit"</span><span class="nt">></span>Search<span class="nt"></button></span>
<span class="nt"></form></span>
<span class="nt"></div></span>
<span class="nt"></nav></span>
<span class="nt"><script </span><span class="na">src=</span><span class="s">"/webjars/jquery/jquery.min.js"</span><span class="nt">></script></span>
<span class="nt"><script </span><span class="na">src=</span><span class="s">"/webjars/bootstrap/js/bootstrap.min.js"</span><span class="nt">></script></span>
<span class="nt"></body></span>
<span class="nt"></html></span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<h1>
<a name="commit-3-the-html-templates-for-the-entities" href="#commit-3-the-html-templates-for-the-entities">
</a>
Commit #3 - The HTML templates for the entities
</h1>
<p>Adds server-side-rendered templates and controllers. Templates provide basic html pages for Roles and Users.</p>
<p>This is the new <code>RoleController</code>:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight java"><code><span class="nd">@Controller</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">RoleController</span> <span class="o">{</span>
<span class="nd">@GetMapping</span><span class="o">(</span><span class="s">"/roles"</span><span class="o">)</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">rolesPage</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="s">"roles"</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>And the <code>UserController</code>:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight java"><code><span class="nd">@Controller</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">UserController</span> <span class="o">{</span>
<span class="nd">@GetMapping</span><span class="o">(</span><span class="s">"/users"</span><span class="o">)</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">users</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="s">"users"</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>The HTML for the <code>roles.html</code> page:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight html"><code><span class="cp"><!DOCTYPE html></span>
<span class="nt"><html</span> <span class="na">lang=</span><span class="s">"en"</span> <span class="na">xmlns:th=</span><span class="s">"http://www.thymeleaf.org"</span><span class="nt">></span>
<span class="nt"><head></span>
<span class="nt"><link</span> <span class="na">href=</span><span class="s">'/webjars/bootstrap/css/bootstrap.min.css'</span> <span class="na">rel=</span><span class="s">'stylesheet'</span><span class="nt">></span>
<span class="nt"><meta</span> <span class="na">charset=</span><span class="s">"UTF-8"</span><span class="nt">></span>
<span class="nt"><title></span>Roles<span class="nt"></title></span>
<span class="nt"></head></span>
<span class="nt"><body></span>
<span class="nt"><div</span> <span class="na">th:replace=</span><span class="s">"fragments/header :: header"</span><span class="nt">></div></span>
<span class="nt"><br><br></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"container"</span> <span class="na">id=</span><span class="s">"main"</span><span class="nt">></span>
<span class="nt"><table</span> <span class="na">class=</span><span class="s">"table table-striped table-bordered"</span><span class="nt">></span>
<span class="nt"><thead></span>
<span class="nt"><tr></span>
<span class="nt"><th></span>Role ID<span class="nt"></th></span>
<span class="nt"><th></span>Role Name<span class="nt"></th></span>
<span class="nt"><th></span>Actions<span class="nt"></th></span>
<span class="nt"></tr></span>
<span class="nt"></thead></span>
<span class="nt"><tbody></span>
<span class="nt"><tr></span>
<span class="nt"><td></td></span>
<span class="nt"><td></td></span>
<span class="nt"><td></span>
<span class="nt"><a></span>Edit<span class="nt"></a></span>
<span class="nt"><a></span>Delete<span class="nt"></a></span>
<span class="nt"></td></span>
<span class="nt"></tr></span>
<span class="nt"></tbody></span>
<span class="nt"></table></span>
<span class="nt"></div></span>
<span class="nt"><script </span><span class="na">src=</span><span class="s">"/webjars/jquery/jquery.min.js"</span><span class="nt">></script></span>
<span class="nt"><script </span><span class="na">src=</span><span class="s">"/webjars/bootstrap/js/bootstrap.min.js"</span><span class="nt">></script></span>
<span class="nt"></body></span>
<span class="nt"></html></span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>And <code>users.html</code> page:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight html"><code><span class="cp"><!DOCTYPE html></span>
<span class="nt"><html</span> <span class="na">lang=</span><span class="s">"en"</span> <span class="na">xmlns:th=</span><span class="s">"http://www.thymeleaf.org"</span><span class="nt">></span>
<span class="nt"><head></span>
<span class="nt"><link</span> <span class="na">href=</span><span class="s">'/webjars/bootstrap/css/bootstrap.min.css'</span> <span class="na">rel=</span><span class="s">'stylesheet'</span><span class="nt">></span>
<span class="nt"><meta</span> <span class="na">charset=</span><span class="s">"UTF-8"</span><span class="nt">></span>
<span class="nt"><title></span>Users<span class="nt"></title></span>
<span class="nt"></head></span>
<span class="nt"><body></span>
<span class="nt"><div</span> <span class="na">th:replace=</span><span class="s">"fragments/header :: header"</span><span class="nt">></div></span>
<span class="nt"><br><br></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"container"</span> <span class="na">id=</span><span class="s">"main"</span><span class="nt">></span>
<span class="nt"><table</span> <span class="na">class=</span><span class="s">"table table-striped table-bordered"</span><span class="nt">></span>
<span class="nt"><thead></span>
<span class="nt"><tr></span>
<span class="nt"><th></span>First Name<span class="nt"></th></span>
<span class="nt"><th></span>Last Name<span class="nt"></th></span>
<span class="nt"><th></span>Role<span class="nt"></th></span>
<span class="nt"><th></span>Actions<span class="nt"></th></span>
<span class="nt"></tr></span>
<span class="nt"></thead></span>
<span class="nt"><tbody></span>
<span class="nt"><tr></span>
<span class="nt"><td></td></span>
<span class="nt"><td></td></span>
<span class="nt"><td></td></span>
<span class="nt"><td></span>
<span class="nt"><a></span>Edit<span class="nt"></a></span>
<span class="nt"><a></span>Delete<span class="nt"></a></span>
<span class="nt"></td></span>
<span class="nt"></tr></span>
<span class="nt"></tbody></span>
<span class="nt"></table></span>
<span class="nt"></div></span>
<span class="nt"><script </span><span class="na">src=</span><span class="s">"/webjars/jquery/jquery.min.js"</span><span class="nt">></script></span>
<span class="nt"><script </span><span class="na">src=</span><span class="s">"/webjars/bootstrap/js/bootstrap.min.js"</span><span class="nt">></script></span>
<span class="nt"></body></span>
<span class="nt"></html></span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<h1>
<a name="commit-4-the-vue-magic" href="#commit-4-the-vue-magic">
</a>
Commit #4 - The Vue magic
</h1>
<p>Now we add Vue and Axios to the <code>pom.xml</code> file:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight xml"><code><span class="nt"><dependency></span>
<span class="nt"><groupId></span>org.webjars<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>vue<span class="nt"></artifactId></span>
<span class="nt"><version></span>2.6.11<span class="nt"></version></span>
<span class="nt"></dependency></span>
<span class="nt"><dependency></span>
<span class="nt"><groupId></span>org.webjars.npm<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>axios<span class="nt"></artifactId></span>
<span class="nt"><version></span>0.19.0<span class="nt"></version></span>
<span class="nt"></dependency></span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>Then a REST controller (this serializes the response to JSON by default) to return a list of Roles:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight java"><code><span class="nd">@RestController</span>
<span class="nd">@RequestMapping</span><span class="o">(</span><span class="s">"/api/v1"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">RolesController</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="nc">RoleRepository</span> <span class="n">roleRepository</span><span class="o">;</span>
<span class="kd">public</span> <span class="nf">RolesController</span><span class="o">(</span><span class="nc">RoleRepository</span> <span class="n">roleRepository</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">roleRepository</span> <span class="o">=</span> <span class="n">roleRepository</span><span class="o">;</span>
<span class="o">}</span>
<span class="nd">@GetMapping</span><span class="o">(</span><span class="s">"roles"</span><span class="o">)</span>
<span class="kd">public</span> <span class="nc">List</span><span class="o"><</span><span class="nc">Role</span><span class="o">></span> <span class="nf">list</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">roleRepository</span><span class="o">.</span><span class="na">findAll</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>And another one for Users:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight java"><code><span class="nd">@RestController</span>
<span class="nd">@RequestMapping</span><span class="o">(</span><span class="s">"/api/v1"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">UsersController</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="nc">UserRepository</span> <span class="n">userRepository</span><span class="o">;</span>
<span class="kd">public</span> <span class="nf">UsersController</span><span class="o">(</span><span class="nc">UserRepository</span> <span class="n">userRepository</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">userRepository</span> <span class="o">=</span> <span class="n">userRepository</span><span class="o">;</span>
<span class="o">}</span>
<span class="nd">@GetMapping</span><span class="o">(</span><span class="s">"/users"</span><span class="o">)</span>
<span class="kd">public</span> <span class="nc">List</span><span class="o"><</span><span class="nc">User</span><span class="o">></span> <span class="nf">list</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">userRepository</span><span class="o">.</span><span class="na">findAll</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>Finally, on the <code>roles.html</code> we edit the section where we want Vue.js to render the data and add a script:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight html"><code><span class="c"><!-- unrelated code omitted for brevity --></span>
<span class="nt"><tr</span> <span class="na">v-for=</span><span class="s">"role in roles"</span><span class="nt">></span>
<span class="nt"><td></span><span class="nt"></td></span>
<span class="nt"><td></span><span class="nt"></td></span>
<span class="c"><!-- unrelated code omitted for brevity --></span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<div class="highlight js-code-highlight">
<pre class="highlight javascript"><code><span class="c"><!--</span> <span class="nx">Vue</span><span class="p">.</span><span class="nx">js</span> <span class="nx">imports</span> <span class="o">--></span>
<span class="o"><</span><span class="nx">script</span> <span class="nx">src</span><span class="o">=</span><span class="dl">"</span><span class="s2">webjars/vue/vue.min.js</span><span class="dl">"</span><span class="o">><</span><span class="sr">/script</span><span class="err">>
</span><span class="o"><</span><span class="nx">script</span> <span class="nx">src</span><span class="o">=</span><span class="dl">"</span><span class="s2">webjars/axios/dist/axios.min.js</span><span class="dl">"</span><span class="o">><</span><span class="sr">/script</span><span class="err">>
</span><span class="c"><!--</span> <span class="nx">Actual</span> <span class="nx">Vue</span><span class="p">.</span><span class="nx">js</span> <span class="nx">script</span> <span class="o">--></span>
<span class="o"><</span><span class="nx">script</span><span class="o">></span>
<span class="kd">var</span> <span class="nx">app</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Vue</span><span class="p">({</span>
<span class="na">el</span><span class="p">:</span> <span class="dl">'</span><span class="s1">#main</span><span class="dl">'</span><span class="p">,</span>
<span class="nx">data</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">{</span>
<span class="na">roles</span><span class="p">:</span> <span class="kc">null</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="nx">mounted</span><span class="p">(){</span>
<span class="nx">axios</span>
<span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">/api/v1/roles</span><span class="dl">"</span><span class="p">)</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">response</span> <span class="o">=></span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">roles</span> <span class="o">=</span> <span class="nx">response</span><span class="p">.</span><span class="nx">data</span><span class="p">))</span>
<span class="p">},</span>
<span class="p">})</span>
<span class="o"><</span><span class="sr">/script</span><span class="err">>
</span></code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>The same modifications are made on the <code>users.html</code>:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight html"><code><span class="c"><!-- unrelated code omitted for brevity --></span>
<span class="nt"><tr</span> <span class="na">v-for=</span><span class="s">"user in users"</span><span class="nt">></span>
<span class="nt"><td></span><span class="nt"></td></span>
<span class="nt"><td></span><span class="nt"></td></span>
<span class="nt"><td></span><span class="nt"></td></span>
<span class="c"><!-- unrelated code omitted for brevity --></span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<div class="highlight js-code-highlight">
<pre class="highlight javascript"><code><span class="c"><!--</span> <span class="nx">Vue</span><span class="p">.</span><span class="nx">js</span> <span class="nx">imports</span> <span class="o">--></span>
<span class="o"><</span><span class="nx">script</span> <span class="nx">src</span><span class="o">=</span><span class="dl">"</span><span class="s2">webjars/vue/vue.min.js</span><span class="dl">"</span><span class="o">><</span><span class="sr">/script</span><span class="err">>
</span><span class="o"><</span><span class="nx">script</span> <span class="nx">src</span><span class="o">=</span><span class="dl">"</span><span class="s2">webjars/axios/dist/axios.min.js</span><span class="dl">"</span><span class="o">><</span><span class="sr">/script</span><span class="err">>
</span><span class="c"><!--</span> <span class="nx">Actual</span> <span class="nx">Vue</span><span class="p">.</span><span class="nx">js</span> <span class="nx">script</span> <span class="o">--></span>
<span class="o"><</span><span class="nx">script</span><span class="o">></span>
<span class="kd">var</span> <span class="nx">app</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Vue</span><span class="p">({</span>
<span class="na">el</span><span class="p">:</span> <span class="dl">'</span><span class="s1">#main</span><span class="dl">'</span><span class="p">,</span>
<span class="nx">data</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">{</span>
<span class="na">users</span><span class="p">:</span> <span class="kc">null</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="nx">mounted</span><span class="p">(){</span>
<span class="nx">axios</span>
<span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">/api/v1/users</span><span class="dl">"</span><span class="p">)</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">response</span> <span class="o">=></span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">users</span> <span class="o">=</span> <span class="nx">response</span><span class="p">.</span><span class="nx">data</span><span class="p">))</span>
<span class="p">},</span>
<span class="p">})</span>
<span class="o"><</span><span class="sr">/script</span><span class="err">>
</span></code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<h1>
<a name="the-app" href="#the-app">
</a>
The app
</h1>
<p>You can see the app running <a href="https://spring-thymeleaf-vue-crud.herokuapp.com/">here</a> (slightly modified since this is an ongoing analysis).</p>
<p>The repository and the aforementioned commits, also slightly modified, <a href="https://github.com/brunodrugowick/spring-thymeleaf-vue-crud-example/commits/master">here</a>.</p>
<div class="ltag-github-readme-tag">
<div class="readme-overview">
<h2>
<img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" loading="lazy" />
<a href="https://github.com/brunodrugowick">
brunodrugowick
</a> / <a style="font-weight: 600;" href="https://github.com/brunodrugowick/spring-thymeleaf-vue-crud-example">
spring-thymeleaf-vue-crud-example
</a>
</h2>
<h3>
Complete CRUD example project with Spring Boot, Thymeleaf, Vue.js and Axios.
</h3>
</div>
</div>
<h1>
<a name="aqap-series" href="#aqap-series">
</a>
AQAP Series
</h1>
<p>As Quickly As Possible (AQAP) is a series of quick posts on something I find interesting. I encourage (and take part on) the discussions on the comments to further explore the technology, library or code quickly explained here.</p>
<h1>
<a name="beginners-tag" href="#beginners-tag">
</a>
Beginners tag
</h1>
<p>I'm using this tag for the second time. Since there are rules to use it, please let me know if there's something here you (or a beginner) don't understand or that I took for granted and you're (or a beginner may be :) confused about it.</p>
<hr />
<p>Image by <a href="https://pixabay.com/pt/users/jaykingsta14-4885997/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=2358636">Jason King</a> por <a href="https://pixabay.com/pt/?utm_source=link-attribution&utm_medium=referral&utm_campaign=image&utm_content=2358636">Pixabay</a></p>
<p>…</p>Using JPQL with Spring Data JPA2020-01-30T03:35:50+00:002020-01-30T03:35:50+00:00https://drugowick.dev/2020/01/30/using-jpql-with-spring-data-jpa-48c0<style type="text/css" media="screen">
.card {
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
transition: 0.3s;
width: 100%;
}
.card:hover {
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
}
.container {
padding: 2px 16px;
}
</style>
<p><code>[java, spring, jpa, jpql]</code></p>
<div class="card">
<div class="container">
<h4><b><br />You'll have a better experience reading in DEV</b></h4>
<p><a href="https://dev.to/brunodrugowick/using-jpql-with-spring-data-jpa-48c0" target="_blank">Click here to continue reading this post there >></a></p>
<p>However, if you want to know more about the project to mirror my posts from DEV here (and why), go ahead and <a href="https://dev.to/brunodrugowick/using-jpql-with-spring-data-jpa-48c0" target="_blank">read more</a>.</p>
<p>You can continue to read here too, it's up to you... =]</p>
</div>
</div>
<p><br /></p>
<p>This is the post #4 of the series "Querying your Spring Data JPA Repository".</p>
<p>Let's recap: by now you should have an app like <a href="https://jpa-queries-blog-post.herokuapp.com/">this</a>. The frontend (Thymeleaf + a very bad UI design) doesn't matter since our goal is to understand several ways to query your Spring Data JPA repository. Regardless, I <a href="https://github.com/brunodrugowick/jpa-queries-blog-post/commit/020142ef12d5f2413837df50d341c459e8f10154">added Bootstrap</a> to make make it easier on the eyes.</p>
<h1>
<a name="adding-a-few-properties" href="#adding-a-few-properties">
</a>
Adding a few properties
</h1>
<p>Let's add <code>city</code>, <code>grabAndGo</code> and <code>active</code> properties to our <code>Restaurant</code> entity. You can see how on <a href="https://github.com/brunodrugowick/jpa-queries-blog-post/commit/ab7a2e4307a84a4208d5bb0b8755ab2c9c88821a">this commit</a>.</p>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q0nX5ejw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/il9urv4xz85gh1529iuy.png" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q0nX5ejw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/il9urv4xz85gh1529iuy.png" alt="Alt Text" loading="lazy" width="880" height="649" /></a></p>
<h1>
<a name="the-requirement" href="#the-requirement">
</a>
The requirement
</h1>
<p>Let's say now that you want to create a new custom search on our awesome search area:</p>
<p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ezu-ZRdN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/32hl2ruu78g21j7lto8m.png" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ezu-ZRdN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/32hl2ruu78g21j7lto8m.png" alt="Alt Text" loading="lazy" width="880" height="216" /></a></p>
<p>This searches for active restaurants with Grab'n'Go enabled in a city specified by the user. </p>
<h1>
<a name="i-know-how-to-do-this-already" href="#i-know-how-to-do-this-already">
</a>
"I know how to do this already"
</h1>
<p>Yes, if you're following the series you already know how to do this with Query Methods. Here you go:</p>
<ul>
<li>Create a new method on the RestaurantRepository interface:
</li>
</ul>
<div class="highlight js-code-highlight">
<pre class="highlight java"><code><span class="nc">List</span><span class="o"><</span><span class="nc">Restaurant</span><span class="o">></span> <span class="nf">findAllByActiveTrueAndGrabngoTrueAndCityContaining</span><span class="o">(</span><span class="nc">String</span> <span class="n">city</span><span class="o">);</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>That's going to work, but look at the size of the method name! C'mon! Imagine having to use this huge method name all over your code. That's not <a href="https://dzone.com/articles/naming-conventions-from-uncle-bobs-clean-code-phil">clean</a>!</p>
<h1>
<a name="-raw-query-endraw-to-the-rescue" href="#-raw-query-endraw-to-the-rescue">
</a>
<code>@Query</code> to the rescue
</h1>
<p>That's the exact scenario to use a JPQL query. Let's refactor the code above:</p>
<ul>
<li>Rename the method to something more acceptable, like <code>activeGrabngoByCity</code>.</li>
<li>Add the annotation <code>@Query</code> above the method name.</li>
<li>Create your custom JPQL query.</li>
</ul>
<p>Here's the result:<br />
</p>
<div class="highlight js-code-highlight">
<pre class="highlight java"><code><span class="nd">@Query</span><span class="o">(</span><span class="s">"from Restaurant r where r.active = true and r.grabngo = true and r.city like %:city%"</span><span class="o">)</span>
<span class="nc">List</span><span class="o"><</span><span class="nc">Restaurant</span><span class="o">></span> <span class="nf">activeGrabngoByCity</span><span class="o">(</span><span class="nc">String</span> <span class="n">city</span><span class="o">);</span>
</code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title>
<path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title>
<path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path>
</svg>
</div>
</div>
</div>
<p>Notice that you don't have to follow the Query Methods' rules to name the method. You're telling Spring that you're providing the query for this method via the <code>@Query</code> annotation.</p>
<p>For now that's all that I'm going to cover. You can take a look at <a href="https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#hql">this documentation</a> from Hibernate to do your own cool stuff, JPQL is very powerful!</p>
<h1>
<a name="the-example-app" href="#the-example-app">
</a>
The example app
</h1>
<p>The working app is <a href="https://jpa-queries-blog-post.herokuapp.com/">here</a> (wait for Heroku to load the app, it takes a few seconds on the free tier).</p>
<h1>
<a name="commits-related-to-this-post" href="#commits-related-to-this-post">
</a>
Commits related to this post
</h1>
<p>Adds Bootstrap: <a href="https://github.com/brunodrugowick/jpa-queries-blog-post/commit/020142ef12d5f2413837df50d341c459e8f10154">020142</a>.<br />
Adds new properties to the Restaurant: <a href="https://github.com/brunodrugowick/jpa-queries-blog-post/commit/ab7a2e4307a84a4208d5bb0b8755ab2c9c88821a">ab7a2e</a>.<br />
Refactors to JPQL: <a href="https://github.com/brunodrugowick/jpa-queries-blog-post/commit/8758bfb7544c9f2d7a7001467e9c317e59753509">8758bf</a>.</p>
<div class="ltag-github-readme-tag">
<div class="readme-overview">
<h2>
<img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo" loading="lazy" />
<a href="https://github.com/brunodrugowick">
brunodrugowick
</a> / <a style="font-weight: 600;" href="https://github.com/brunodrugowick/jpa-queries-blog-post">
jpa-queries-blog-post
</a>
</h2>
<h3>
A demo project for a blog post about (Spring Data) JPA.
</h3>
</div>
</div>
<p>…</p>