<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>PostgreSQL Archives - Codersee blog- Kotlin on the backend</title>
	<atom:link href="https://blog.codersee.com/tag/postgresql/feed/" rel="self" type="application/rss+xml" />
	<link></link>
	<description>Kotlin &#38; Backend Tutorials - Learn Through Practice.</description>
	<lastBuildDate>Wed, 16 Apr 2025 04:50:21 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://blog.codersee.com/wp-content/uploads/2025/04/cropped-codersee_logo_circle_2-32x32.png</url>
	<title>PostgreSQL Archives - Codersee blog- Kotlin on the backend</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>A Quick Introduction To PostgreSQL</title>
		<link>https://blog.codersee.com/discover-postgresql-comprehensive-introduction/</link>
					<comments>https://blog.codersee.com/discover-postgresql-comprehensive-introduction/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Tue, 03 Jan 2023 08:15:06 +0000</pubDate>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=5504141</guid>

					<description><![CDATA[<p>In this PostgreSQL introduction, we'll cover its gensis, key features, benefits and all the necessary things we should remember about.</p>
<p>The post <a href="https://blog.codersee.com/discover-postgresql-comprehensive-introduction/">A Quick Introduction To PostgreSQL</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>In this article, I will provide you with a <strong>beginner-friendly introduction to PostgreSQL</strong>. Together, we will see what exactly PostgreSQL is and learn more about its:</p>
<ul>
<li>history,</li>
<li>key features,</li>
<li>pros &amp; cons,</li>
<li>use cases,</li>
<li>and finally- examples of companies relying on this relational database.</li>
</ul>
<p>And a friendly reminder- this PostgreSQL introduction is a continuation of a <strong>top 8 databases series</strong> I covered <a href="https://blog.codersee.com/a-quick-intro-to-8-most-popular-databases/" target="_blank" rel="noopener">here</a>.</p>
<h2>Explainer Animation</h2>
<p>If you enjoy <strong>whiteboard animations</strong>, then right here you can find a video version of this article:</p>
<div style="text-align: center; width: 80%; margin-left: 10%;">
<a href="https://blog.codersee.com/discover-postgresql-comprehensive-introduction/"><img decoding="async" src="https://blog.codersee.com/wp-content/plugins/wp-youtube-lyte/lyteCache.php?origThumbUrl=%2F%2Fi.ytimg.com%2Fvi%2Fwo3Jo47LFAc%2Fhqdefault.jpg" alt="YouTube Video"></a><br /><br /></p>
</div>
<p>If you find this content useful,<strong> please leave a subscription </strong> 😉</p>
<h2>PostgreSQL History</h2>
<p>As usual, let&#8217;s start everything by learning more about PostgreSQL genesis.</p>
<p>PostgreSQL history started in the 1980s at the University of California, Berkeley when Michael Stonebraker began working on a new project, which would be the successor of Ingres. The goal of this project was to address the problems plaguing the databases of that time.</p>
<p>Initially, the project was called <strong>POSTGRES</strong>, highlighting that it is the successor of <strong>Ingres</strong> (which was created at the same university). But then, in 1996, the name was changed to PostgreSQL to emphasize its support for SQL. In 2007, the developer team decided to stick with the name PostgreSQL and Postgres as an alias.</p>
<p>But back to the development topic, the project target was to introduce support for data types and relationships between them. And, although the team took a lot of ideas from Ingres, the codebase was created from scratch.</p>
<p>The PostgreSQL introduction took place in 1986 when the team released official papers describing the system. Two years later, they performed a demo of a prototype at the ACM SIGMOD Conference. The team was gradually releasing new versions with new features and changes. Nevertheless, the number of feature requests and users began to surpass them, and the project ended in 1994, and POSTGRES Version 4.2 was released under the MIT license.</p>
<p>As the official date of the PostgreSQL release, we take <strong>8th July of 1996</strong>, when Marc Fournier created the first non-university server for Postgres development.</p>
<h2>What Is PostgreSQL Then?</h2>
<p>So, with all of that being said, let&#8217;s answer the question- what is PostgreSQL?</p>
<p><a href="https://codersee.com/newsletter/"><img fetchpriority="high" decoding="async" class="aligncenter wp-image-3002956 size-large" src="http://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-1024x576.png" alt="Image shows two ebooks people can get for free after joining newsletter" width="800" height="419" /></a></p>
<p>Well, it&#8217;s an open-source relational database management system (aka RDBMS), based on the POSTGRES Version 4.2 (which we&#8217;ve already covered).</p>
<p>One of the key features of PostgreSQL is its support for a wide range of data types. Moreover, it also supports data types for special purposes, like arrays, JSON, and XML.</p>
<p>In addition to this, Postgres is also extendable and comes with a number of features that make it well-suited for a wide range of applications, like:</p>
<ul>
<li>transactions</li>
<li>stored procedures &amp; views</li>
<li>triggers</li>
<li>and support for complex queries.</li>
</ul>
<p>What&#8217;s worth mentioning here as well, the Postgres license lets everyone use, modify and distribute it entirely for free. Regardless of whether we do it commercially or for personal use.</p>
<h2>PostgreSQL Advantages</h2>
<p>Nextly, let&#8217;s take a while to go through the introduction of PostgreSQL&#8217;s <strong>advantages</strong>:</p>
<ul>
<li>First of all, <strong>it&#8217;s open source</strong>. Postgres usage is entirely free regardless of whether we use it commercially, or not. Whatsoever, we can always modify and adjust it to our needs. This makes PostgreSQL a great choice for everyone, who desires a high-quality DBMS at no cost.</li>
<li>Secondly, <strong>performance</strong>. It is known for its great performance, with the ability to handle large amounts of data and high levels of traffic.</li>
<li>Thirdly, PostgreSQL is <strong>community-driven</strong>. From its very beginning, Postgres is developed by many people and companies working together to deliver a high-quality solution.</li>
<li>Additionally, it has an <strong>active community</strong> and <strong>a lot of resources available online</strong>. Just like in previous articles, in this PostgresSQL introduction, I must use this argument. Tons of materials, libraries, forums, blog posts, etc. is a great advantage, which helps us incorporate it into our projects with ease. Moreover, in case of any problems we can easily find solutions on the internet, for example using <a href="https://stackoverflow.com/">StackOverflow</a>.</li>
<li>Whatsoever, Postgres<strong> supports a wide variety of programming languages</strong>. We can write SQL queries or custom functions with C or C++. Of course, we can find probably hundreds of libraries to write code with <a href="https://blog.codersee.com/tag/java/">Java</a>, <a href="https://blog.codersee.com/tag/kotlin/">Kotlin</a>, and many more.</li>
<li>Lastly, it comes with <strong>plenty of features</strong> and <strong>data types support</strong> out of the box. If you would like to learn more about its features, then I highly encourage you to visit its documentation <a href="https://www.postgresql.org/about/featurematrix/" target="_blank" rel="noopener">here</a>. Not to mention that we can always extend and customize its behavior due to its liberal licensing.</li>
</ul>
<h2>PostgreSQL Disadvantages</h2>
<p>With all of that great things being covered in our PostgreSQL introduction, let&#8217;s see its drawbacks:</p>
<ul>
<li>Firstly, it&#8217;s <strong>comparatively slower</strong> and <strong><span class="HwtZe" lang="en"><span class="jCAhz ChMk0b"><span class="ryNqvb">it </span></span></span><span class="HwtZe" lang="en"><span class="jCAhz ChMk0b"><span class="ryNqvb">is not as well supported as MySQL</span></span></span></strong> when it comes to different applications or hostings.</li>
<li>Secondly, <strong>complexity</strong>. PostgreSQL is a powerful ORDBMS, but it may require more resources and expertise to set up and manage than some other database systems.</li>
<li>Lastly, being an <strong>open-source solution</strong> means that there&#8217;s not a single organization, or a company standing behind it and it does not come with any warranty.</li>
</ul>
<h2>PostgreSQL Use Cases</h2>
<p>As the next thing, let&#8217;s see what PostgreSQL is well suited for:</p>
<ul>
<li>First of all, <strong>web-based apps</strong>. PostgreSQL with its great support for different (even complex) data types and other features can be a great solution for content management systems, e-commerce platforms, etc.</li>
<li><strong>Scientific</strong> and <strong>financial applications</strong>. Postgres is often used in apps, which require processing large amounts of data, support for data analysis, and transactions support.</li>
<li>Moreover, everywhere, where we <strong>need to process spatial data</strong>. Postgis, an extension for PostgreSQL, adds support for geographic objects allowing location queries to be run in SQL.</li>
</ul>
<p>Please keep in mind that these are not all possible use cases. And if you need a mature relational database management system, which supports various data types, complex queries, and large datasets, then PostgreSQL can be a great choice.</p>
<h2>Which Companies Use Postgres?</h2>
<p>As the last thing, let&#8217;s take a second to see the list of companies using PostgreSQL.</p>
<p>As the first one- Apple. It uses Postgres to store and manage data for a number of its internal systems, including its online store and customer support systems. They even replaced MySQL with Postgres as an embedded DB in the OS X Lion.</p>
<p>And as the next one, Instagram. Yes, a giant with over 2 billion users makes use of PostgreSQL as one of the main databases for their system.</p>
<p>Of course, the list of all companies is much longer, but let&#8217;s see a couple more:</p>
<ul>
<li>Skype</li>
<li>Yahoo</li>
<li>Spotify</li>
<li>Salesforce</li>
<li>Pinterest</li>
<li>Uber and Airbnb.</li>
</ul>
<h2>Summary Of The PostgreSQL Introduction</h2>
<p>And that&#8217;s all for this article, in which we cover what exactly PostgreSQL is. Together, we walked through its genesis, advantages, disadvantages, and use cases, as well.</p>
<p>I hope you enjoy this series about the most popular databases on my blog. Please keep in mind, that I have other articles, in which you can learn how to work with Postgres in practice, for example:</p>
<ul>
<li><a class="LinkSuggestion__Link-sc-1gewdgc-4 iyHtHS" href="https://blog.codersee.com/rest-api-ktor-ktorm-postgresql/" target="_blank" rel="noopener">REST API With Ktor, Ktorm, and PostgreSQL</a></li>
<li><a class="LinkSuggestion__Link-sc-1gewdgc-4 iyHtHS" href="https://blog.codersee.com/youtube-course-on-rest-api-with-spring-webflux-and-kotlin/" target="_blank" rel="noopener">YouTube Course On REST API With Spring WebFlux and Kotlin</a></li>
</ul>
<p>Alternatively, you might wan to check other articles in a series:</p>
<ul>
<li><a href="https://blog.codersee.com/redis-database-explained/">Redis Database Explained</a></li>
<li><a href="https://blog.codersee.com/mysql-essentials-fast-paced-introduction/">MySQL Essentials: A Fast-Paced Introduction</a></li>
</ul>
<p><strong>Finally, the task for you:</strong> please leave a comment with your thoughts or feedback in the comments section below.</p>
<p>The post <a href="https://blog.codersee.com/discover-postgresql-comprehensive-introduction/">A Quick Introduction To PostgreSQL</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/discover-postgresql-comprehensive-introduction/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>A Quick Intro To The 8 Most Popular Databases</title>
		<link>https://blog.codersee.com/a-quick-intro-to-8-most-popular-databases/</link>
					<comments>https://blog.codersee.com/a-quick-intro-to-8-most-popular-databases/#comments</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Wed, 14 Dec 2022 07:00:33 +0000</pubDate>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Elasticsearch]]></category>
		<category><![CDATA[MariaDB]]></category>
		<category><![CDATA[Microsoft SQL Server]]></category>
		<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[Redis]]></category>
		<category><![CDATA[SQLite]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=5504084</guid>

					<description><![CDATA[<p>Data is one of the most valuable assets of the XXI century and in this article, we will take a look at the 8 most popular databases of 2022.</p>
<p>The post <a href="https://blog.codersee.com/a-quick-intro-to-8-most-popular-databases/">A Quick Intro To The 8 Most Popular Databases</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>It won&#8217;t be a surprise if I say that <strong>data is one of the most valuable assets of the XXI century</strong>. Many organizations and businesses rely on the most popular databases to store, manage and analyze their data to support their business needs.</p>
<p>In this article, we will take a look at the <strong>8 most popular databases</strong> and their key features, as well as common use cases.</p>
<p>So, no matter whether you are a database administrator, programmer, or simply would like to learn more about the different databases available, this article will provide you with all the necessary information to start with.</p>
<h2>Doodly Animation</h2>
<p>If you find this content useful, please leave a <strong>thumb up</strong>, or a <strong>sub</strong>. This way you can help me, so that I can help you 😉</p>
<p>&nbsp;</p>
<div style="text-align: center;">
<a href="https://blog.codersee.com/a-quick-intro-to-8-most-popular-databases/"><img decoding="async" src="https://blog.codersee.com/wp-content/plugins/wp-youtube-lyte/lyteCache.php?origThumbUrl=%2F%2Fi.ytimg.com%2Fvi%2Fu81dp3igZVU%2Fhqdefault.jpg" alt="YouTube Video"></a><br /><br /></p>
</div>
<h2>What Does &#8220;The Most Popular Databases&#8221; Mean?</h2>
<p>Well, before we start I wanna make sure that we are on the same page when referring to the &#8220;most popular&#8221;.</p>
<p>Depending on who wrote the article, this term can have various meanings. For some, it will be the frequency of Google searches. For others, it may be data provided by the <a href="https://db-engines.com" target="_blank" rel="noopener">https://db-engines.com</a> page.</p>
<p>And for the purpose of this blog post, I decided to use the <a href="https://survey.stackoverflow.co/2022/" target="_blank" rel="noopener">Stack Overflow Developer Survey 2022 results</a>, which present, as follows:</p>
<p>&nbsp;</p>
<p><img decoding="async" class="aligncenter wp-image-5504088 size-large" src="http://blog.codersee.com/wp-content/uploads/2022/12/the_most_popular_databases_2022_so_developer_survey-1024x480.png" alt="The image is a screenshot of StackOverflow Developer Survey 2022 presenting the most popular databases: MySQL, PostgreSQL, SQLite, MongoDB, Microsoft SQL Server, Redis, MariaDB and ElasticSearch" width="800" height="375" srcset="https://blog.codersee.com/wp-content/uploads/2022/12/the_most_popular_databases_2022_so_developer_survey-1024x480.png 1024w, https://blog.codersee.com/wp-content/uploads/2022/12/the_most_popular_databases_2022_so_developer_survey-300x141.png 300w, https://blog.codersee.com/wp-content/uploads/2022/12/the_most_popular_databases_2022_so_developer_survey-768x360.png 768w, https://blog.codersee.com/wp-content/uploads/2022/12/the_most_popular_databases_2022_so_developer_survey.png 1253w" sizes="(max-width: 800px) 100vw, 800px" /></p>
<p>&nbsp;</p>
<p>In the next chapters, I will walk you through each of the 8 most popular databases shortly.</p>
<p>Please keep in mind that this article is a general overview and expect that more thorough continuations will be added in the upcoming weeks 🙂</p>
<h2>MySQL Database</h2>
<p>With that being said, let&#8217;s start with the winner- <strong>MySQL</strong>.</p>
<p>It&#8217;s an open-source relational database management system (aka RDBMS). It was created in 1994 by the Swedish company <em>MySQL AB</em> (which was later acquired by <em>Sun Microsystems</em> and then by <em>Oracle Corporation</em>). Originally, it was developed as a proprietary database system but later it was open-sourced under the GNU General Public License.<br />
<a href="https://codersee.com/newsletter/"><img decoding="async" class="aligncenter wp-image-3002956 size-large" src="http://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-1024x576.png" alt="Image shows two ebooks people can get for free after joining newsletter" width="800" height="419" /></a></p>
<p>&nbsp;</p>
<h3>Advantages:</h3>
<ul>
<li><strong>It&#8217;s free and open-source,</strong> so it can be a great choice for smaller companies and organizations preferring to cut costs.</li>
<li>Moreover, MySQL <strong>is widely supported by web hosting providers,</strong> which means that it can be easily integrated with web-based applications.</li>
<li>Additionally, it has a <strong>low learning curve</strong> and a <strong>simple and intuitive user interface</strong>, so it is not only a great choice for beginners but also for less experienced teams.</li>
<li>Finally, <strong>a large and active community of users and developers </strong>means a lot of online resources and support available.</li>
</ul>
<h3>Disadvantages:</h3>
<ul>
<li>First of all, <strong>it is not designed for applications that require real-time data processing or high availability</strong>. And technically- this is not a drawback, but rather something you should be aware of.</li>
<li>Whatsoever, MySQL is <strong>hard to scale</strong> and <strong>not intended for large amounts</strong> of data.</li>
<li>Lastly, <strong>it does not have built-in support for full-text search or geospatial data and some advanced features that are available in other database systems.</strong></li>
</ul>
<p>As I mentioned above, I will create a separate blog post about the king of the most popular databases. But for now, I can invite you to check out my other how-to guides with MySQL:</p>
<ul>
<li><a href="https://blog.codersee.com/deploy-kotlin-spring-boot-app-with-mysql-on-kubernetes/">Deploy Kotlin Spring Boot App with MySQL on Kubernetes</a></li>
<li><a href="https://blog.codersee.com/spring-boot-on-google-gke-with-cloud-sql-and-kotlin/">Spring Boot on Google GKE with Cloud SQL and Kotlin</a></li>
</ul>
<h2>PostgreSQL (aka Postgres)</h2>
<p>Nextly, let&#8217;s take a look at the second place with a 3% difference- <strong>PostgreSQL</strong> (also known as <strong>Postgres</strong>).</p>
<p>It was created in 1986 by a team of researchers at the University of California and is a <strong>powerful</strong> and <strong>open-source</strong> object-relational database management system. PostgreSQL is a highly popular and widely used DBMS, known for its flexibility, reliability, and performance.</p>
<h3>Advantages:</h3>
<ul>
<li>Just like MySQL, PostgreSQL is <strong>open-source</strong>, <strong>free to use, </strong>and<strong> has a large, active community.</strong></li>
<li>It is <strong>highly reliable</strong> and <strong>performs well even under heavy workloads</strong>. This makes it a good choice for applications that require high availability and scalability.</li>
<li>Moreover, Postgres comes with <strong>support for a lot of features and capabilities </strong>out of the box. For example triggers, stored procedures, and multi-row transactions.</li>
</ul>
<h3>Disadvantages:</h3>
<ul>
<li>Firstly, PostgreSQL <strong>may not be as widely supported</strong> or compatible with technologies, as some other database systems.</li>
<li>Secondly, it <strong>may not be as fast or scalable as other database management systems</strong>, such as NoSQL databases. And whereas it works well for large datasets, it is not the speed demon.</li>
<li>Whatsoever, Postgres is a pretty complex system, so <strong>it might be a bit difficult to learn</strong>.</li>
</ul>
<p>And just like previously, please expect new articles to come. But, again, if you would like to learn backend development with Kotlin and Postgres, then I can recommend:</p>
<ul>
<li><a href="https://codersee.com/courses/rest-api-with-spring-webflux-and-kotlin/">REST API With Spring WebFlux and Kotlin Course</a></li>
<li><a href="https://blog.codersee.com/tag/postgresql/">other tutorials on how to use it with Spring Boot, or Ktor</a></li>
</ul>
<h2>SQLite</h2>
<p>As the next step, let&#8217;s take a while to the no 3 in the most popular databases summary- <strong>SQLite</strong>.</p>
<p>SQLite is an embedded SQL database engine. The main difference between it and most of SQL databases is that it is self-contained and does not run a separate server process. To put it simply- it&#8217;s a library, which reads and writes directly to plain disk files.</p>
<p>It was created in 2000 by Dwayne Richard Hipp using a C programming language and since then, it become the most deployed database engine- used, among others of top web browsers.</p>
<h3>Advantages:</h3>
<ul>
<li>First of all, SQLite is a <strong>really lightweight</strong> database and it can be successfully used in low-memory environments. With all features enabled, its size can be as little as 750KiB.</li>
<li>Additionally, the database file format<strong> is portable</strong> and can be migrated between 32-bit ad 64-bit systems.</li>
<li>Moreover, SQLite is<strong> easy to learn</strong> and <strong>requires zero configuration</strong>, which makes it a great choice for beginners (and not only!)</li>
<li>Finally, it is <strong>really fast</strong> and can bring much better performance, than the filesystem.</li>
</ul>
<h3>Disadvantages:</h3>
<ul>
<li>Firstly- it&#8217;s <strong>not suitable for client/server applications</strong>. Although it can work over the network, the filesystem latency might be an issue.</li>
<li>Secondly (and lastly), SQLite <strong>is not a good choice for high-volume websites</strong> or environments demanding<strong> high concurrency</strong>. Whereas it does not have a limit for concurrent reads, only one writer can write at any instant in time.</li>
</ul>
<h2>MongoDB (aka Mongo)</h2>
<p>And the prize for 4th place in our most popular databases ranking goes to <strong>MongoDB</strong>.</p>
<p>MongoDB is an open-source document-oriented database. It was created in 2009 by MongoDB Inc., which makes it not only the first NoSQL representative in the ranking but also the youngest one (so far). In contrast to previous examples, MongoDB is an example of a NoSQL database, so instead of <em>tables</em> and <em>rows</em>, Mongo stores JSON-like <em>documents</em>, which are organized in <em>collections</em>.</p>
<h3>Advantages:</h3>
<ul>
<li>First of all, Mongo was built with environments demanding <strong>high performance</strong> and <strong>scalability </strong>in mind.</li>
<li>Secondly, it&#8217;s <strong>flexible</strong>. MongoDB does not require you to define a schema for your collections (SQL tables counterparts), which can help you reduce the time spent on schema changes. Moreover, it can be a great choice everywhere, where a schema is hard to define.</li>
<li>Finally, just like MySQL and Postgres, MongoDB is <strong>widely supported</strong> and really <strong>easy to learn</strong>.</li>
</ul>
<h3>Disadvantages:</h3>
<ul>
<li>No <strong>out-of-the-box support for joins</strong>. Unlike relational databases, Mongo by design does not support joins between collections, so if you have to work with highly connected data sets, then it might not be the best choice.</li>
<li>Moreover, MongoDB documents <strong>cannot exceed 16MB.</strong></li>
<li>Lastly, the lack of joins oftentimes leads to <strong>data duplication</strong>, which may become troublesome to maintain in the future.</li>
</ul>
<p>If you would like to learn Mongo in practice, then check out my <a href="https://blog.codersee.com/tag/mongodb/">practical guides about MongoDB with Spring Boot, Ktor, and Micronaut</a>.</p>
<h2>Microsoft SQL Server</h2>
<p>Following, the 5th position on the most popular databases list is taken by <strong>Microsoft SQL Server</strong>.</p>
<p>It was developed by Microsoft and first released in 1989 and is another relational database in our ranking. Microsoft SQL Server lets us communicate using T-SQL (Transact-SQL), which is an extension of SQL bringing additional features, like functions, or local variables. Whatsoever, it comes with an entire range of products, which we can optionally configure together to make it a really powerful tool.</p>
<h3>Advantages:</h3>
<ul>
<li>Firstly, Microsoft SQL Server has really <strong>good support</strong> and <strong>documentation</strong>.</li>
<li>Additionally, it&#8217;s relatively <strong>easy to set up and use</strong>.</li>
<li>Whatsoever, SQL Server provides a range of <strong>security features</strong> to protect against unauthorized access to the data and <strong>ensure data integrity</strong>.</li>
<li>Lastly, a strong point is <strong>data recovery</strong> in case anything happens.</li>
</ul>
<h3>Disadvantages:</h3>
<ul>
<li>First of all, <strong>pricing</strong>. And although there is a free option available, Enterprise or Standard editions are pretty expensive.</li>
<li>Secondly, <strong>compatibility</strong>. Prior to the SQL Server 2017, it could be run on Windows-based servers, but this is the first version, which can be deployed on Linux.</li>
<li>Lastly, it <strong>may require additional Microsoft software purchases</strong> to fully make use of it in your case.</li>
</ul>
<h2>Redis</h2>
<p>And the 6th most popular database of 2022 is <strong>Redis</strong>.</p>
<p>It was created by Salvatore Sanfilippo, also known as antirez, in 2009, which makes it a relatively young database in our summary.</p>
<p>Redis is an open-source, in-memory data structure store that is commonly used as a database, cache, and message broker. It is known for its high performance, flexibility, and scalability, making it a popular choice for a wide range of applications. Whatsoever, it is oftentimes used in conjunction with Microservices and containers, to support the needs of modern, distributed applications.</p>
<h3>Advantages:</h3>
<ul>
<li>As the first one- Redis is an <strong>in-memory data store</strong>, which results in much higher data processing, than disk-based databases.</li>
<li>Additionally, it&#8217;s <strong>highly scalable</strong> and <strong>can be easily expanded</strong> to support the growing needs of our application.</li>
<li>Lastly, Redis is an <strong>open-source</strong> project with a<strong> large community</strong> and a<strong> huge amount of online resources</strong> to use and learn from. Moreover, <strong>it is compatible</strong> and pretty<strong> easy to use with most of the popular frameworks</strong>.</li>
</ul>
<h3>Disadvantages:</h3>
<ul>
<li>So the first advantage is the disadvantage, as well. The in-memory data store is actually faster, but we have to keep in mind that it is <strong>more expensive</strong>.</li>
<li>Moreover, it is a key-value store (like a dictionary), which means that the application(s) connecting to Redis <strong>have to be aware of the structure</strong>, which requires additional config.</li>
<li>Lastly, Redis is doing fine when dealing with key-based operations, but <strong>is not that efficient, when it comes to searching through the data set</strong>.</li>
</ul>
<p>If you would like to learn more about it in detail, then check out my more detailed article: <a href="https://blog.codersee.com/redis-database-explained/">Redis Database Explained</a></p>
<h2>MariaDB</h2>
<p>As the one before last in the most popular databases list, let&#8217;s see what exactly <strong>MariaDB</strong> is.</p>
<p>First of all, MariaDB is a fork of the MySQL DBMS, created by the original developers of MySQL in response to the acquisition of MySQL by Oracle Corporation. The acquisition raised concerns among many users and developers about the future direction of the MySQL project, which resulted in MariaDB being released in 2009.</p>
<p>It&#8217;s known for its performance, reliability, and ease of use, and is used by many organizations around the world. Moreover, it is designed to be backward-compatible with MySQL and can be used as a drop-in replacement for it in most cases.</p>
<h3>Advantages:</h3>
<ul>
<li>Firstly, MariaDB is known for its <strong>performance</strong> and <strong>reliability</strong>.</li>
<li>Additionally, it is an<strong> open-source</strong> project with a <strong>large</strong> and <strong>active community</strong> of users and developers. And I know that this argument repeats often in this article. But trust me- a wealth of resources, such as documentation and tutorials, available online comes with plenty of other perks.</li>
<li>Thirdly, as mentioned above <strong>it can be used as a MySQL drop-in replacement in most cases</strong>. This allows companies to replace the database without changing the other tools. However, please remember that this is not always the case and sometimes may require additional work.</li>
<li>And lastly, it is <strong>widely supported</strong> by various programming languages and frameworks.</li>
</ul>
<h3>Disadvantages:</h3>
<ul>
<li>As the first one, <strong>some of the MySQL Enterprise Edition features are not available out of the box</strong>. And although this can be done through plugins, this is an additional, independent tool we have to incorporate.</li>
<li>Whatsoever, <strong>migrating from MySQL may result in additional adjustments and config</strong>.</li>
<li>And as the last thing- <strong>the support may be pretty expensive</strong>.</li>
</ul>
<h2>Elasticsearch</h2>
<p>And finally, we come to the last position in the top 8 most popular databases of 2022- <strong>Elasticsearch</strong>.</p>
<p>Elasticsearch is an open-source, full-text search and analytics engine created by Shay Banon in 2010.</p>
<p>It is designed to be scalable and flexible, making it a popular choice for building search and data analysis applications. Moreover, it&#8217;s based on the Lucene search engine library and provides a RESTful API for interacting with the data. Elasticsearch is known for its powerful search and analytics capabilities, and can be used to search, analyze, and visualize data from a variety of sources, including structured, unstructured, and time-series data.</p>
<h3>Advantages:</h3>
<ul>
<li>Elasticsearch is an <strong>open-source</strong> project, supported by a<strong> large and active community of users</strong>.</li>
<li>Similarly, it&#8217;s <strong>compatible with plenty of programming languages and frameworks</strong>, which helps in its implementation.</li>
<li>Lastly, it is a <strong>highly scalable search and analytics engine</strong>, which is intended for fast full-text searches. Thus, it can be a great solution for website searches.</li>
</ul>
<h3>Disadvantages:</h3>
<ul>
<li>Elasticsearch <strong>may not be as fast or efficient as some other search and analytics platforms</strong> in certain scenarios- for example, a large amount of data.</li>
<li>Moreover, it <strong>is more difficult to learn</strong> compared to similar tools. But this comes with being more powerful, as well.</li>
<li>Lastly, Elasticsearch <strong>should not be used as primary storage</strong>.</li>
</ul>
<h2>The Most Popular Databases Summary</h2>
<p>And that&#8217;s all for this article about the 8 most popular databases. As I already mentioned, this is the initial, bird&#8217;s-eye view post and you can expect more detailed articles to come.</p>
<p>Let me know your thoughts in the comments section below!</p>
<p>The post <a href="https://blog.codersee.com/a-quick-intro-to-8-most-popular-databases/">A Quick Intro To The 8 Most Popular Databases</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/a-quick-intro-to-8-most-popular-databases/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Postgres jsonb to Jackson JsonNode With Spring Data JPA</title>
		<link>https://blog.codersee.com/postgres-jsonb-to-jackson-jsonnode-jpa/</link>
					<comments>https://blog.codersee.com/postgres-jsonb-to-jackson-jsonnode-jpa/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Mon, 12 Dec 2022 06:00:28 +0000</pubDate>
				<category><![CDATA[Spring]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=5504034</guid>

					<description><![CDATA[<p>This time, I would like to show you how to solve a pretty common issue when mapping Postgres jsonb to Jackson JsonNode in Spring Data JPA.</p>
<p>The post <a href="https://blog.codersee.com/postgres-jsonb-to-jackson-jsonnode-jpa/">Postgres jsonb to Jackson JsonNode With Spring Data JPA</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="h-1-introduction">1. Introduction</h2>



<p>Hello friend! In this post, I would like to show you how to solve a pretty common issue when mapping <strong>Postgres jsonb to Jackson JsonNode in Spring Data JPA</strong>.</p>



<p>If you are not a regular reader of my blog, then I&#8217;m pretty sure that you wanted to map a PostgreSQL jsonb field in your Spring Boot with Spring Data JPA application to Jsonnode and you saw something like that:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>org.springframework.beans.factory.BeanCreationException: Error creating bean with name &#8216;entityManagerFactory&#8217; defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Could not determine recommended JdbcType for `com.fasterxml.jackson.databind.JsonNode`</p>
</blockquote>



<p>If that&#8217;s the case, then you&#8217;ve come to the right place. 😁</p>



<h2 class="wp-block-heading" id="h-2-quick-setup">2. Quick Setup</h2>



<p>Before we learn how to solve the issue with Postgres jsonb mapping to JsonNode, let&#8217;s take a minute to reproduce the error.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Note:</strong> If you would like to cut straight to the point and see how to implement a mapper, then you can skip this part and navigate directly to <strong>chapter 3.</strong></p>
</blockquote>



<h3 class="wp-block-heading" id="h-2-1-postgresql-table-with-jsonb-column">2.1. PostgreSQL Table With jsonb column</h3>



<p>Firstly, we need to have a PostgreSQL table with jsonb column:</p>



<figure class="wp-block-image aligncenter wp-image-5504040 size-full"><img loading="lazy" decoding="async" width="407" height="306" src="http://blog.codersee.com/wp-content/uploads/2022/12/postgres_jsonb_to_jsonnode_db_table_diagram.png" alt="Image shows a diagram of PostgreSQL table called some_table with columns definitions (including a jsonb column), which will be used to demonstrate how to map Postgres jsonb to JsonNode." class="wp-image-5504040" srcset="https://blog.codersee.com/wp-content/uploads/2022/12/postgres_jsonb_to_jsonnode_db_table_diagram.png 407w, https://blog.codersee.com/wp-content/uploads/2022/12/postgres_jsonb_to_jsonnode_db_table_diagram-300x226.png 300w" sizes="auto, (max-width: 407px) 100vw, 407px" /><figcaption class="wp-element-caption">Diagram generated with <a href="https://dbdiagram.io/" target="_blank" rel="noopener">https://dbdiagram.io/</a></figcaption></figure>



<p>As we can see, it&#8217;s just a dummy table called&nbsp;<em>some_table</em>, which contains 4 example columns.</p>



<p>And to be on the same it page, I created it and populated with the following SQL script:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="sql" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">create table some_table(
  id serial not null primary key,
  string_column text not null,
  integer_column smallint not null,
  jsonb_column jsonb not null
);

insert into 
  some_table(string_column, integer_column, jsonb_column)
values
  ('value-1', 1, '{ "some_field": "some_value"}'),
  ('value-2', 2, '{ "another_field": "some_value_2"}');</pre>


<p>[elementor-template id=&#8221;9007393&#8243;]</p>



<h3 class="wp-block-heading" id="h-2-2-spring-boot-and-dependencies">2.2. Spring Boot and Dependencies</h3>



<p>Nextly, let&#8217;s create an example Spring Boot project. As always, we can do that with the <a href="https://start.spring.io/" target="_blank" rel="noopener">Spring Initializr page</a>.</p>



<p>Please configure it according to your needs, but do not forget to add the <strong>PostgreSQL Driver</strong> and <strong>Spring Data JPA</strong> dependencies. Additionally, we will need to enrich the project with <strong>Jackson Databind</strong>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("com.fasterxml.jackson.core:jackson-databind:2.14.1")
runtimeOnly("org.postgresql:postgresql")</pre>



<h3 class="wp-block-heading" id="h-2-3-application-yaml">2.3. application.yaml</h3>



<p>With that being done, let&#8217;s specify the connection details:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="yaml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">spring:
  datasource:
    url: "jdbc:postgresql://localhost:5432/postgres"
      username: postgres
      password: postgres</pre>



<h3 class="wp-block-heading" id="h-2-4-implement-entity">2.4. Implement Entity</h3>



<p>As the next step, let&#8217;s open up the project and add the <em>SomeEntity</em> data class:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Entity
@Table(name = "some_table")
data class SomeEntity(
  @Id
  val id: Long? = null,
  val stringColumn: String,
  val integerColumn: Int,
  val jsonbColumn: JsonNode
)</pre>



<p>As we can see, we use the <strong>@Entity</strong> annotation to mark the class as the entity and <strong>@Table</strong> to explicitly provide Spring with the table name. Moreover, the necessary <strong>@Id</strong> annotation specifies the primary key of our entity and the <em>jsonbColumn</em> will contain the value of the Postgres jsonb column mapped to JsonNode.</p>



<h3 class="wp-block-heading" id="h-2-5-create-jpa-repository">2.5. Create JPA repository</h3>



<p>With that being done, let&#8217;s add the <em>SomeEntityRepository</em> interface:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">interface SomeEntityRepository : CrudRepository&lt;SomeEntity, Long></pre>



<p>This time, the only thing we have to do is to extend the CrudRepository interface, which comes with a basic CRUD operations.</p>



<h3 class="wp-block-heading" id="h-2-6-create-jpa-repository">2.6. Create JPA repository</h3>



<p>Lastly, let&#8217;s add the logic, which will be run when the application starts:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Component
class MyRunner(
  private val repository: SomeEntityRepository
) : CommandLineRunner {

  override fun run(vararg args: String?) {
    repository.findAll()
      .forEach(::println)
  }
}</pre>



<p>As we can see, a repository instance will be injected into this simple component. Moreover, we implement the <em>run</em> method of <em>CommandLineRunner</em>, which instructs Spring Boot to run after the app starts (if you would like to learn more about it, then check out my article about the <a href="https://blog.codersee.com/spring-boot-console-app-with-kotlin/" target="_blank" rel="noopener">Spring Boot console app</a>).</p>



<p>Finally, we want to print out all entities, nevertheless, such a stack trace appears to our eyes:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Could not determine recommended JdbcType for `com.fasterxml.jackson.databind.JsonNode`
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1751) ~[spring-beans-6.0.2.jar:6.0.2]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:599) ~[spring-beans-6.0.2.jar:6.0.2]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521) ~[spring-beans-6.0.2.jar:6.0.2]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-beans-6.0.2.jar:6.0.2]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.0.2.jar:6.0.2]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[spring-beans-6.0.2.jar:6.0.2]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.0.2.jar:6.0.2]
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1130) ~[spring-context-6.0.2.jar:6.0.2]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:905) ~[spring-context-6.0.2.jar:6.0.2]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:584) ~[spring-context-6.0.2.jar:6.0.2]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730) ~[spring-boot-3.0.0.jar:3.0.0]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432) ~[spring-boot-3.0.0.jar:3.0.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[spring-boot-3.0.0.jar:3.0.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302) ~[spring-boot-3.0.0.jar:3.0.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291) ~[spring-boot-3.0.0.jar:3.0.0]
at com.codersee.jsonbtojsonnode.JsonbToJsonNodeApplicationKt.main(JsonbToJsonNodeApplication.kt:14) ~[main/:na]
Caused by: org.hibernate.type.descriptor.java.spi.JdbcTypeRecommendationException: Could not determine recommended JdbcType for `com.fasterxml.jackson.databind.JsonNode`
at org.hibernate.type.descriptor.java.spi.UnknownBasicJavaType.getRecommendedJdbcType(UnknownBasicJavaType.java:37) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]
at org.hibernate.boot.model.process.internal.InferredBasicValueResolver.from(InferredBasicValueResolver.java:222) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]
at org.hibernate.mapping.BasicValue.buildResolution(BasicValue.java:507) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]
at org.hibernate.mapping.BasicValue.resolve(BasicValue.java:315) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]
at org.hibernate.mapping.BasicValue.resolve(BasicValue.java:305) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.lambda$processValueResolvers$4(InFlightMetadataCollectorImpl.java:1767) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]
at java.base/java.util.ArrayList.removeIf(ArrayList.java:1682) ~[na:na]
at java.base/java.util.ArrayList.removeIf(ArrayList.java:1660) ~[na:na]
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processValueResolvers(InFlightMetadataCollectorImpl.java:1766) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1752) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:300) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1350) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1421) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:66) ~[spring-orm-6.0.2.jar:6.0.2]
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:376) ~[spring-orm-6.0.2.jar:6.0.2]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) ~[spring-orm-6.0.2.jar:6.0.2]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) ~[spring-orm-6.0.2.jar:6.0.2]
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:352) ~[spring-orm-6.0.2.jar:6.0.2]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1797) ~[spring-beans-6.0.2.jar:6.0.2]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1747) ~[spring-beans-6.0.2.jar:6.0.2]
... 15 common frames omitted</pre>



<h2 class="wp-block-heading" id="h-3-map-postgres-jsonb-to-jsonnode">3. Map Postgres jsonb to JsonNode</h2>



<p>So at this point, we can see that Spring &#8220;<em>Could not determine recommended JdbcType for `com.fasterxml.jackson.databind.JsonNode`</em>&#8220;.</p>



<figure class="wp-block-image aligncenter"><a href="https://codersee.com/newsletter/"><img loading="lazy" decoding="async" width="1024" height="576" src="http://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-1024x576.png" alt="Image shows two ebooks people can get for free after joining newsletter" class="wp-image-3002956" srcset="https://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-1024x576.png 1024w, https://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-300x169.png 300w, https://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-768x432.png 768w, https://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter.png 1440w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></figure>



<p>One of the possible solutions is to implement a custom <strong>AttributeConverter</strong> and mark it with <strong>@Converter</strong> annotation:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Converter(autoApply = true)
class MyConverter : AttributeConverter&lt;JsonNode, String> {

  private val mapper = ObjectMapper()

  override fun convertToDatabaseColumn(node: JsonNode): String {
    return node.asText()
  }

  override fun convertToEntityAttribute(data: String): JsonNode {
    return mapper.readTree(data)
  }
}</pre>



<p>As we can see, the <em>AttributeConverter</em> interface comes with two methods: <em>convertToDatabaseColumn</em> and <em>convertToEntityAttribute</em>, which will be invoked when mapping between the entity and the database.</p>



<p>Additionally, we have set the <strong>autoApply</strong> to <em>true</em> (the default is <em>false</em>) to inform the persistence provider that it should use this converter to map between Postgres jsonb and JsonNode. Without it, we would have to explicitly annotate the desired field:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Convert(converter = MyConverter::class)
val jsonbColumn: JsonNode</pre>



<p>Of course, the choice is up to you and may vary depending on your project, but most of the time you would like to choose the first solution.</p>



<p>Finally, if we run the application, we should see that everything is working, as expected and logs contain the following lines:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">SomeEntity(id=1, stringColumn=value-1, integerColumn=1, jsonbColumn={"some_field":"some_value"})
SomeEntity(id=2, stringColumn=value-2, integerColumn=2, jsonbColumn={"another_field":"some_value_2"})</pre>



<h2 class="wp-block-heading" id="h-4-summary">4. Summary</h2>



<p>And that&#8217;s all for this short, hands-on article on how to map Postgres jsonb to JsonNode. As always, you can find the source code in <a href="https://github.com/codersee-blog/spring-boot-kotlin-jsonb-to-jsonnode" target="_blank" rel="noopener">this GitHub repository</a>.</p>



<p>As always, happy to hear your thoughts on how would you solve this issue.</p>
<p>The post <a href="https://blog.codersee.com/postgres-jsonb-to-jackson-jsonnode-jpa/">Postgres jsonb to Jackson JsonNode With Spring Data JPA</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/postgres-jsonb-to-jackson-jsonnode-jpa/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Add Flyway To An Existing Spring Boot Project</title>
		<link>https://blog.codersee.com/add-flyway-to-existing-spring-boot-project/</link>
					<comments>https://blog.codersee.com/add-flyway-to-existing-spring-boot-project/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Thu, 08 Dec 2022 18:16:48 +0000</pubDate>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Spring]]></category>
		<category><![CDATA[Flyway]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=5504025</guid>

					<description><![CDATA[<p>In this, the third article about Flyway on my blog, we will see how to add Flyway to an existing Spring Boot project. </p>
<p>The post <a href="https://blog.codersee.com/add-flyway-to-existing-spring-boot-project/">Add Flyway To An Existing Spring Boot Project</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2 class="article-heading-introduction">1. Introduction</h2>
<p>In this article, which is a continuation of a <a href="https://flywaydb.org/" target="_blank" rel="noopener">Flyway</a> series we will learn how to <strong>add Flyway to an existing Spring Boot Project</strong>.</p>
<p>If you are not sure what this tool can bring to your project, then check out my first post on <a href="https://blog.codersee.com/flyway-migrations-with-spring-boot/">Flyway with Spring Boot</a>, where you can find all the necessary information.</p>
<h2 class="article-heading-introduction">2. Project Setup</h2>
<p>This time, we won&#8217;t spend time preparing a sample project (well, if you are here then there&#8217;s a 95% chance that you already have some project).</p>
<p>Nevertheless, if you don&#8217;t have one, but still would like to learn how to add a flyway to an existing Spring Boot project, then no worries. You can find an example project in<a href="https://github.com/codersee-blog/spring-boot-add-flyway-to-existing-project" target="_blank" rel="noopener"> this GitHub repository</a>.</p>
<blockquote><p><strong>Note: </strong>If you use the project from the linked repository, then please run the script.sql file (can be found in /resources/sql directory) against your database, before you start the application.</p></blockquote>
<h2 class="article-heading-introduction">3. Strategy For Adding Flyway To Existing Spring Boot Project</h2>
<p>Before we start anything, I just wanted to emphasize that <strong>we should always create a backup, before performing any operations on our databases</strong>. Regardless of how widely used, or well-tested the tool is, something can always go wrong and we have to be ready to roll out plan B.</p>
<h3 class="article-heading-introduction">3.1 Add Flyway Dependency</h3>
<p>As the first thing (after the backup!), let&#8217;s add the necessary dependency to our Spring Boot project.</p>
<p>For gradle, it will look as follows:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="groovy">implementation 'org.flywaydb:flyway-core:9.8.3'</pre>
<p>On the other hand, when working with Maven, we need to add these lines:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="xml">&lt;dependency&gt;
  &lt;groupId&gt;org.flywaydb&lt;/groupId&gt;
  &lt;artifactId&gt;flyway-core&lt;/artifactId&gt;
  &lt;version&gt;9.8.3&lt;/version&gt;
&lt;/dependency&gt;</pre>
<h3 class="article-heading-introduction">3.2 Verify Spring Boot App</h3>
<p>Nextly, let&#8217;s run our Spring Boot application and verify, whether anything changed:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'flywayInitializer' defined in class path resource [org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration$FlywayConfiguration.class]: Found non-empty schema(s) "custom_schema" but no schema history table. Use baseline() or set baselineOnMigrate to true to initialize the schema history table.
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1751) ~[spring-beans-6.0.2.jar:6.0.2]</pre>
<p>As we can clearly see, Flyway throws a pretty descriptive message:</p>
<blockquote><p>Found non-empty schema(s) &#8220;custom_schema&#8221; but no schema history table. Use baseline() or set baselineOnMigrate to true to initialize the schema history table.</p></blockquote>
<p>And basically, this error clearly states what we need to do when adding a Flyway to an existing project. But before that, we have to take care of a couple of things.</p>
<h3 class="article-heading-introduction">3.3 Generate Production Database DDL</h3>
<p>Firstly, we have to generate an <strong>SQL script containing the DDL our production database</strong>, including:</p>
<ul>
<li>procedures</li>
<li>views,</li>
<li>triggers etc&#8230;</li>
</ul>
<p>I won&#8217;t go into details here, because DDL script generation depends on the database you are using. Nevertheless, it can be done pretty easily with tools like <em>DataGrip</em>, <em>DBeaver</em>, or <em>pg_dump</em>.<br />
<a href="https://codersee.com/newsletter/"><img loading="lazy" decoding="async" class="aligncenter wp-image-3002956 size-large" src="http://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-1024x576.png" alt="Image shows two ebooks people can get for free after joining newsletter" width="800" height="419" /></a></p>
<p>And when the SQL script with DDL is ready, let&#8217;s save it in the Flyway migrations directory (in Spring Boot the default one is <em>resources/db/migration)</em>. Of course, we have to remember about the Flyway files naming convention, so the file name should be something, like <em>V1__my_custom_init_script.sql</em>.</p>
<h3 class="article-heading-introduction">3.4 Clear Irrelevant Data</h3>
<p>If you have only one database instance in your project, then feel free to skip this point. Otherwise,<strong> we have to make sure that the generated DDL script will work in every other environment</strong>. And we can achieve it in two ways:</p>
<ul>
<li><strong>if we don&#8217;t care about the data</strong> in other environments- then we can run the Flyway <em>clean</em> command.</li>
<li>however, <strong>if we do actually care about the data</strong> in our non-production environments, then there&#8217;s no other choice than to manually verify and align other database structures with the main one.</li>
</ul>
<p>The choice here is yours and will depend on many factors, like the importance of test data, amount of environments, etc.</p>
<p>Nevertheless, if you would like to clean your database before adding a Flyway to your existing project, then you can do that with the following:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">flyway -defaultSchema="custom_schema" \
  -url="jdbc:postgresql://{URL}:{PORT}/{DB_NAME}" \
  -cleanDisabled="false" \
  -user="{USERNAME}" \
  clean</pre>
<p>Please keep in mind that with this command, we will be prompted to enter the password manually (and alternatively, we can pass the <em>-password</em> flag).</p>
<h3 class="article-heading-introduction">3.5 Add Flyway To Existing Spring Boot Project</h3>
<p>Finally, with all that being done, we can return to the Spring Boot project and make the necessary changes.</p>
<p>Let&#8217;s navigate to the <strong>application.yaml</strong> file and insert these settings:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="yaml">spring:
  flyway:
    baselineOnMigrate: true
    defaultSchema: "custom_schema" # not necessary, but most likely you'll want to point to a specific schema</pre>
<p>As we can see, with <strong>baselineOnMigrate</strong> set to true, we instruct Flyway to <strong>automatically call baseline when migration is executed against a non-empty schema</strong>. Additionally, we set the <strong>defaultSchema</strong> value, but this is totally optional.</p>
<p>As a result, the <strong>flyway_schema_history</strong> table is added to our schema:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-5504072 size-full" src="http://blog.codersee.com/wp-content/uploads/2022/12/flyway_schema_history_table_structure.png" alt="Image presents screenshot with flyway_schema_history table structure, which was added when we add Flyway to existing Spring Boot project." width="837" height="218" srcset="https://blog.codersee.com/wp-content/uploads/2022/12/flyway_schema_history_table_structure.png 837w, https://blog.codersee.com/wp-content/uploads/2022/12/flyway_schema_history_table_structure-300x78.png 300w, https://blog.codersee.com/wp-content/uploads/2022/12/flyway_schema_history_table_structure-768x200.png 768w" sizes="auto, (max-width: 837px) 100vw, 837px" /></p>
<p>and populated with <strong>precisely one record</strong>.</p>
<p>Please remember that the <strong>baselineOnMigrate set to true is only required once- when we do the initial deployment</strong> and can be removed after that.</p>
<h3 class="article-heading-introduction">3.6 Add Flyway Using Commands</h3>
<p>Basically, with all of the above being done we are finished and can enjoy the benefits of Flyway in our project.</p>
<p>However, if you would like to learn how to add Flyway to an existing project using CLI, then you can do exactly the same with the following command:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">flyway -defaultSchema="custom_schema" \ 
-url="jdbc:postgresql://{HOST}:{PORT}/{DB_NAME}" \
-user="USERNAME" \
-baselineOnMigrate="true" \
-locations="filesystem:." migrate</pre>
<p>Or, alternatively<strong>, run baseline only: </strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">flyway -defaultSchema="custom_schema" \ 
-url="jdbc:postgresql://{HOST}:{PORT}/{DB_NAME}" \
-user="USERNAME" \
-locations="filesystem:." baseline</pre>
<p>And then <strong>the migrate command</strong>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">flyway -defaultSchema="custom_schema" \ 
-url="jdbc:postgresql://{HOST}:{PORT}/{DB_NAME}" \
-user="USERNAME" \
-locations="filesystem:." migrate</pre>
<p>These commands might be useful if you would like to extract migrations to a separate directory (and/or repository).</p>
<h2>4. Add Flyway To An Existing Spring Boot Project Summary</h2>
<p>And that would be all in this article about how to add Flyway to an existing Spring Boot project. If you&#8217;d like to get to know Flyway better, then I highly encourage you to visit <a href="https://flywaydb.org/documentation/" target="_blank" rel="noopener">their documentation</a>.</p>
<p>Let me know in the comments section in case of any questions 🙂</p>
<p>The post <a href="https://blog.codersee.com/add-flyway-to-existing-spring-boot-project/">Add Flyway To An Existing Spring Boot Project</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/add-flyway-to-existing-spring-boot-project/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>YouTube Course On REST API With Spring WebFlux and Kotlin</title>
		<link>https://blog.codersee.com/youtube-course-on-rest-api-with-spring-webflux-and-kotlin/</link>
					<comments>https://blog.codersee.com/youtube-course-on-rest-api-with-spring-webflux-and-kotlin/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Fri, 15 Jul 2022 05:25:10 +0000</pubDate>
				<category><![CDATA[Spring]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[Spring WebFlux]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=3002981</guid>

					<description><![CDATA[<p>In this course, I will show you how to create a REST API using Spring WebFlux, PostgreSQL, and Kotlin and containerize it with Docker.</p>
<p>The post <a href="https://blog.codersee.com/youtube-course-on-rest-api-with-spring-webflux-and-kotlin/">YouTube Course On REST API With Spring WebFlux and Kotlin</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>A couple of days ago, I&#8217;ve shared a new YouTube course on how to create a <strong>REST API With Spring WebFlux and Kotlin. </strong>This video series is a step-by-step tutorial on how to create a REST API, connect the application to <strong>PostgreSQL</strong>, and moreover, containerize it using <strong>Docker</strong>.</p>
<p>If you are not sure, whether this is something for you, then please check the introduction, which will help you to make the decision:</p>
<p><a href="https://blog.codersee.com/youtube-course-on-rest-api-with-spring-webflux-and-kotlin/"><img decoding="async" src="https://blog.codersee.com/wp-content/plugins/wp-youtube-lyte/lyteCache.php?origThumbUrl=%2F%2Fi.ytimg.com%2Fvi%2FY3FA2y_eakA%2Fhqdefault.jpg" alt="YouTube Video"></a></p>
<p>If you would like to see the course, then you can find all videos in <a href="https://youtube.com/playlist?list=PLvN8k8yxjoesNnd0C0t4s36bgVHUwJkIR" target="_blank" rel="noopener">this playlist</a>.</p>
<p>This is my first YouTube course and I will be forever thankful if you would like to <strong>share some feedback with me</strong>. You can do that in the comments section here, under the video, or by using the <a href="https://codersee.com/contact/" target="_blank" rel="noopener">contact form</a>. I am constantly trying to improve the content quality, but that&#8217;s simply impossible without hearing from you and your perspective 🙂</p>
<p>Let me know what you think about this course and what topics would you like to see after Spring WebFlux.</p>
<p>Finally, I wanted to share that Codersee was announced as one of the <a href="https://blog.feedspot.com/kotlin_programming_blogs/" target="_blank" rel="noopener"><strong>Top 10 Kotlin Programming Blogs</strong></a><strong> </strong>on the web by FeedSpot. On this occasion, I would like to thank you, dear reader, for your input and for being a part of this journey. Thanks and see you in the near future! 🙂<br />
<a href="https://codersee.com/newsletter/"><img loading="lazy" decoding="async" class="aligncenter wp-image-3002956 size-large" src="http://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-1024x576.png" alt="Image shows two ebooks people can get for free after joining newsletter" width="800" height="419" /></a></p>
<p>&nbsp;</p>
<p>The post <a href="https://blog.codersee.com/youtube-course-on-rest-api-with-spring-webflux-and-kotlin/">YouTube Course On REST API With Spring WebFlux and Kotlin</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/youtube-course-on-rest-api-with-spring-webflux-and-kotlin/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Deploy Spring Boot App With AWS App Runner And Amazon RDS</title>
		<link>https://blog.codersee.com/spring-boot-app-aws-app-runner-amazon-rds/</link>
					<comments>https://blog.codersee.com/spring-boot-app-aws-app-runner-amazon-rds/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Fri, 17 Jun 2022 15:31:07 +0000</pubDate>
				<category><![CDATA[Spring]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=2502915</guid>

					<description><![CDATA[<p>In this step-by-step guide, we will see how to deploy an example Spring Boot application to AWS App Runner and connect to Amazon RDS.</p>
<p>The post <a href="https://blog.codersee.com/spring-boot-app-aws-app-runner-amazon-rds/">Deploy Spring Boot App With AWS App Runner And Amazon RDS</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="h-1-introduction">1. Introduction</h2>



<p>In this step-by-step guide, we will learn how to deploy a <strong>Spring Boot</strong> application With <strong>AWS App Runner</strong> and <strong>Amazon RDS </strong>(using <strong>PostgreSQL</strong>).</p>



<p>Although as an example, we will create and containerize the Spring Boot app, the knowledge you&#8217;re going to gain today is universal and you will be able to easily apply it in any project.</p>



<p><strong>Important note:</strong> Please delete ALL RESOURCES created in AWS after finishing this tutorial. Otherwise, you might easily forget about them and be charged for using them.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p></p>
<cite>In my articles, I focus on the practice and getting things done. <br><br>However, if you would like to get a strong understading of DevOps concepts, which are more and more in demand nowadays, then I highly recommend to check out KodeKloud courses, like this <a href="https://shrsl.com/46wvo" rel="sponsored nofollow">Docker</a> and <a href="https://shareasale.com/r.cfm?b=2183704&amp;u=3507867&amp;m=132199&amp;urllink=&amp;afftrack=" rel="sponsored nofollow">Kubernetes</a> learning paths. </cite></blockquote>



<h2 class="wp-block-heading" id="h-2-what-is-aws-app-runner">2. What Is AWS App Runner?</h2>



<p>Basically, it is a fully managed service, which allows us, the developers, to deploy containerized applications with ease. It takes care of plenty of things, like load balancing, scaling, and deployment out of the box so that we can focus on the source code. Definitely, a tool worth considering when working in smaller teams. If you are interested in more details, please refer to <a href="https://aws.amazon.com/apprunner/" target="_blank" rel="noopener">this web page</a>.</p>



<h2 class="wp-block-heading" id="h-3-what-is-amazon-rds">3. What Is Amazon RDS?</h2>



<p>Well, to be even more specific, <strong>Amazon Relational Database Service (RDS)</strong>, is a fully managed cloud database service. Like the App Runner, it takes care of many things related to database setup, operating, and scaling. Moreover, we can choose between several engines, like:</p>



<ul class="wp-block-list">
<li>PostgreSQL</li>



<li>MySQL</li>



<li>SQL Server, and a few more&#8230;</li>
</ul>



<p>Similarly, I highly encourage you to visit <a href="https://aws.amazon.com/rds/" target="_blank" rel="noopener">this AWS page</a> to see more information.</p>



<h2 class="wp-block-heading" id="h-4-create-spring-boot-project">4. Create Spring Boot Project</h2>



<p>As I mentioned in the beginning, in this post we will create a simple <strong>Spring Boot</strong> application connecting to <strong>PostgreSQL</strong>, however, we won&#8217;t get into many details here, so that we can focus on <strong>AWS App Runner and Amazon RDS part</strong>.</p>



<p>If you don&#8217;t want to spend more time here, please see <a href="https://github.com/codersee-blog/spring-boot-rds-app-runner" target="_blank" rel="noopener">this GitHub repository</a> and skip to <strong>step 5</strong>.</p>



<figure class="wp-block-image aligncenter"><a href="https://codersee.com/newsletter/"><img loading="lazy" decoding="async" width="1024" height="576" src="http://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-1024x576.png" alt="Image shows two ebooks people can get for free after joining newsletter" class="wp-image-3002956" srcset="https://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-1024x576.png 1024w, https://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-300x169.png 300w, https://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-768x432.png 768w, https://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter.png 1440w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></figure>



<h3 class="wp-block-heading" id="h-4-1-imports">4.1. Imports</h3>



<p>As the first step, let&#8217;s take care of imports in our project. Let&#8217;s add the following to the standard Spring Boot imports:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">implementation("org.springframework.boot:spring-boot-starter-data-jdbc")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.flywaydb:flyway-core:8.5.11")
runtimeOnly("org.postgresql:postgresql")</pre>



<p>Please note, that we&#8217;ve added a <a href="https://blog.codersee.com/tag/flyway/" target="_blank" rel="noopener">Flyway</a>, as well. It will be useful to set up the database effortlessly.</p>



<h3 class="wp-block-heading" id="h-4-2-flyway-migration">4.2. Flyway Migration</h3>



<p>With that being done, let&#8217;s add a new file called <code class="EnlighterJSRAW" data-enlighter-language="raw">V1__Init_And_Create_User_Table.sql</code> inside the <code class="EnlighterJSRAW" data-enlighter-language="raw">/resources/db/migration</code> directory:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="sql" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">CREATE TABLE app_user (
  id SERIAL NOT NULL PRIMARY KEY,
  email TEXT NOT NULL
);

INSERT INTO app_user(id, email) values (1, 'john@doe.com');</pre>



<h3 class="wp-block-heading" id="h-4-3-add-model-and-repository">4.3. Add Model and Repository</h3>



<p>Following, let&#8217;s create an <code class="EnlighterJSRAW" data-enlighter-language="kotlin">AppUser</code> data class:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Table("app_user")
data class AppUser(
    @Column("id") val id: Long? = null,
    @Column("email") val email: String
)</pre>



<p>And related <code class="EnlighterJSRAW" data-enlighter-language="kotlin">AppUserRepository</code>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">interface AppUserRepository : CrudRepository&lt;AppUser, Long></pre>



<h3 class="wp-block-heading" id="h-4-4-expose-test-endpoints">4.4. Expose Test Endpoints</h3>



<p>Nextly, let&#8217;s expose two test endpoints:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@RestController
@RequestMapping("/api/users")
class AppUserController(
    private val appUserRepository: AppUserRepository
) {

    @GetMapping
    fun getAllUsers(): List&lt;AppUser> =
        appUserRepository
            .findAll()
            .toList()

    @GetMapping("/non-db")
    fun getAllUsersNonDb(): List&lt;AppUser> =
        listOf(
            AppUser(1L, "piotr@wolak.com")
        )
}</pre>



<p>These two endpoints will be helpful to validate if our Spring Boot App is deployed successfully on App Runner and connected to Amazon RDS.</p>



<h3 class="wp-block-heading" id="h-4-5-configure-application-yaml">4.5. Configure application.yaml</h3>



<p>After that, let&#8217;s configure <code class="EnlighterJSRAW" data-enlighter-language="raw">application.yaml</code> file:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="yaml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">spring:
  datasource:
    url: jdbc:postgresql://${DB_HOST:localhost}:5432/
    username: postgres
    password: password</pre>



<p>As we can see, these 3 values will be used to connect our application to the PostgreSQL instance. Whatsoever, we&#8217;ve introduced the <code class="EnlighterJSRAW" data-enlighter-language="generic">DB_HOST</code> environment variable, so that we will be able to inject database URL easily later.</p>



<h3 class="wp-block-heading" id="h-4-6-create-jar-file">4.6. Create JAR File</h3>



<p>With that being set, let&#8217;s build our service and create a JAR file. When working with gradle (and gradle wrapper), we can simply run the following:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">./gradlew build</pre>



<p>With that, a new file called <code class="EnlighterJSRAW" data-enlighter-language="raw">app-runner-rds-0.0.1-SNAPSHOT.jar</code> is generated inside the <code class="EnlighterJSRAW" data-enlighter-language="raw">/build/libs</code> directory.</p>



<h3 class="wp-block-heading" id="h-4-7-prepare-a-dockerfile">4.7. Prepare a Dockerfile</h3>



<p>Finally, let&#8217;s create a Dockerfile inside the root directory:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="dockerfile" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">FROM openjdk:17-jdk-alpine
RUN mkdir /work
WORKDIR /work
COPY /build/libs/app-runner-rds-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/work/app.jar"]</pre>



<h2 class="wp-block-heading" id="h-5-create-iam-user">5. Create IAM User</h2>



<p>With all of the above being done, we can start the process of deployment to App Runner with Amazon RDS. Nevertheless, please make sure that you have already installed AWS Command Line Interface on your machine. We will need it to push our Docker image later.</p>



<p>If yes, then the first thing we have to do is create an <strong>IAM user</strong>. Let&#8217;s head to the Amazon Management Console and type<strong> &#8220;users&#8221;</strong> in the search bar:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="1104" height="621" src="http://blog.codersee.com/wp-content/uploads/2022/06/1_management_console_users_search.png" alt="" class="wp-image-3002923" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/1_management_console_users_search.png 1104w, https://blog.codersee.com/wp-content/uploads/2022/06/1_management_console_users_search-300x169.png 300w, https://blog.codersee.com/wp-content/uploads/2022/06/1_management_console_users_search-1024x576.png 1024w, https://blog.codersee.com/wp-content/uploads/2022/06/1_management_console_users_search-768x432.png 768w" sizes="auto, (max-width: 1104px) 100vw, 1104px" /></figure>



<p>Let&#8217;s select Users (IAM feature) and on the next page, hit the <strong>Add users</strong> button:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="1346" height="280" src="http://blog.codersee.com/wp-content/uploads/2022/06/2_iam_users_console_first_view.png" alt="" class="wp-image-3002924" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/2_iam_users_console_first_view.png 1346w, https://blog.codersee.com/wp-content/uploads/2022/06/2_iam_users_console_first_view-300x62.png 300w, https://blog.codersee.com/wp-content/uploads/2022/06/2_iam_users_console_first_view-1024x213.png 1024w, https://blog.codersee.com/wp-content/uploads/2022/06/2_iam_users_console_first_view-768x160.png 768w" sizes="auto, (max-width: 1346px) 100vw, 1346px" /></figure>



<p>As the next step, let&#8217;s specify the <strong>User name</strong> and<strong> credential type</strong>. Please select the <strong>Access key &#8211; Programmatic access</strong> option:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="967" height="489" src="http://blog.codersee.com/wp-content/uploads/2022/06/3_add_user_name_and_access_type.png" alt="" class="wp-image-3002925" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/3_add_user_name_and_access_type.png 967w, https://blog.codersee.com/wp-content/uploads/2022/06/3_add_user_name_and_access_type-300x152.png 300w, https://blog.codersee.com/wp-content/uploads/2022/06/3_add_user_name_and_access_type-768x388.png 768w" sizes="auto, (max-width: 967px) 100vw, 967px" /></figure>



<p>With this setting, we will be able to push our Docker image using CLI.</p>



<p>Then, let&#8217;s click the <strong>Next: Permissions</strong> button and <strong>Create group</strong> on the next page:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="981" height="590" src="http://blog.codersee.com/wp-content/uploads/2022/06/4_add_user_permissions_boundary.png" alt="" class="wp-image-3002926" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/4_add_user_permissions_boundary.png 981w, https://blog.codersee.com/wp-content/uploads/2022/06/4_add_user_permissions_boundary-300x180.png 300w, https://blog.codersee.com/wp-content/uploads/2022/06/4_add_user_permissions_boundary-768x462.png 768w" sizes="auto, (max-width: 981px) 100vw, 981px" /></figure>



<p>Similarly, let&#8217;s specify the <strong>Group name</strong> and select the <strong>AdministratorAccess</strong> option:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="1514" height="725" src="http://blog.codersee.com/wp-content/uploads/2022/06/5_create_group.png" alt="" class="wp-image-3002927" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/5_create_group.png 1514w, https://blog.codersee.com/wp-content/uploads/2022/06/5_create_group-300x144.png 300w, https://blog.codersee.com/wp-content/uploads/2022/06/5_create_group-1024x490.png 1024w, https://blog.codersee.com/wp-content/uploads/2022/06/5_create_group-768x368.png 768w" sizes="auto, (max-width: 1514px) 100vw, 1514px" /></figure>



<p>Please keep in mind that in real-life scenarios, we would stick to the principle of least privilege and I picked the above option for the simplicity of this tutorial.</p>



<p>Nextly, let&#8217;s click the <strong>Next: Tags </strong>button:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="967" height="309" src="http://blog.codersee.com/wp-content/uploads/2022/06/6_add_tags.png" alt="" class="wp-image-3002928" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/6_add_tags.png 967w, https://blog.codersee.com/wp-content/uploads/2022/06/6_add_tags-300x96.png 300w, https://blog.codersee.com/wp-content/uploads/2022/06/6_add_tags-768x245.png 768w" sizes="auto, (max-width: 967px) 100vw, 967px" /></figure>



<p>We don&#8217;t need any tags for the purpose of this article, so let&#8217;s hit <strong>Next: Review</strong>:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="972" height="557" src="http://blog.codersee.com/wp-content/uploads/2022/06/7_add_user_review.png" alt="" class="wp-image-3002929" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/7_add_user_review.png 972w, https://blog.codersee.com/wp-content/uploads/2022/06/7_add_user_review-300x172.png 300w, https://blog.codersee.com/wp-content/uploads/2022/06/7_add_user_review-768x440.png 768w" sizes="auto, (max-width: 972px) 100vw, 972px" /></figure>



<p>Everything seems to be perfect, so let&#8217;s finalize the process with <strong>Create user</strong> button.</p>



<p>The user has been created successfully and we can hit the <strong>Download .csv</strong> button:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="971" height="626" src="http://blog.codersee.com/wp-content/uploads/2022/06/8_add_user_success_page.png" alt="" class="wp-image-3002930" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/8_add_user_success_page.png 971w, https://blog.codersee.com/wp-content/uploads/2022/06/8_add_user_success_page-300x193.png 300w, https://blog.codersee.com/wp-content/uploads/2022/06/8_add_user_success_page-768x495.png 768w" sizes="auto, (max-width: 971px) 100vw, 971px" /></figure>



<p>With that being done, let&#8217;s open the download directory with the terminal and run:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">aws configure import --csv file://new_user_credentials.csv

# Result:
Successfully imported 1 profile(s)</pre>



<p>The output indicates everything went perfectly. Nevertheless, we can double-check by simply going to the <code class="EnlighterJSRAW" data-enlighter-language="raw">.aws</code> directory and checking the credentials file. On Windows, it is <code class="EnlighterJSRAW" data-enlighter-language="raw">C:\Users\[your-user]\.aws</code> :</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">[app-runner-rds-user]
aws_access_key_id = &lt;your-key-id>
aws_secret_access_key = &lt;your-access-key></pre>



<p><strong>Important note:</strong> Please remember that these data are confidential and you should <strong>never share them with anyone</strong>.</p>



<h2 class="wp-block-heading" id="h-6-push-the-docker-image-to-ecr">6. Push the Docker image to ECR</h2>



<p>After that, we can push our Docker image to the <strong>Elastic Container Registry</strong>. This way, we will share it with our <strong>App Runner</strong> instance later.</p>



<p>Let&#8217;s get back to the Amazon Management Console and search for <strong>&#8220;registry&#8221;</strong> and select the appropriate option:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="1101" height="495" src="http://blog.codersee.com/wp-content/uploads/2022/06/9_search_for_registry.png" alt="" class="wp-image-3002931" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/9_search_for_registry.png 1101w, https://blog.codersee.com/wp-content/uploads/2022/06/9_search_for_registry-300x135.png 300w, https://blog.codersee.com/wp-content/uploads/2022/06/9_search_for_registry-1024x460.png 1024w, https://blog.codersee.com/wp-content/uploads/2022/06/9_search_for_registry-768x345.png 768w" sizes="auto, (max-width: 1101px) 100vw, 1101px" /></figure>



<p>On the next page, let&#8217;s hit the <strong>Create repository</strong> button:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="1370" height="372" src="http://blog.codersee.com/wp-content/uploads/2022/06/10_ecr_first_view.png" alt="" class="wp-image-3002932" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/10_ecr_first_view.png 1370w, https://blog.codersee.com/wp-content/uploads/2022/06/10_ecr_first_view-300x81.png 300w, https://blog.codersee.com/wp-content/uploads/2022/06/10_ecr_first_view-1024x278.png 1024w, https://blog.codersee.com/wp-content/uploads/2022/06/10_ecr_first_view-768x209.png 768w" sizes="auto, (max-width: 1370px) 100vw, 1370px" /></figure>



<p>Following, let&#8217;s set the visibility settings to <strong>Private</strong> and <strong>specify the name</strong> of our registry. Let&#8217;s keep the rest of the settings with their defaults and hit the <strong>Create repository</strong> button:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="811" height="1196" src="http://blog.codersee.com/wp-content/uploads/2022/06/11_ecr_create_repository_settings.png" alt="" class="wp-image-3002934" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/11_ecr_create_repository_settings.png 811w, https://blog.codersee.com/wp-content/uploads/2022/06/11_ecr_create_repository_settings-203x300.png 203w, https://blog.codersee.com/wp-content/uploads/2022/06/11_ecr_create_repository_settings-694x1024.png 694w, https://blog.codersee.com/wp-content/uploads/2022/06/11_ecr_create_repository_settings-768x1133.png 768w" sizes="auto, (max-width: 811px) 100vw, 811px" /></figure>



<p>Then, we should see that our repository was created successfully:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="1378" height="352" src="http://blog.codersee.com/wp-content/uploads/2022/06/12_created_repository_list_view.png" alt="" class="wp-image-3002935" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/12_created_repository_list_view.png 1378w, https://blog.codersee.com/wp-content/uploads/2022/06/12_created_repository_list_view-300x77.png 300w, https://blog.codersee.com/wp-content/uploads/2022/06/12_created_repository_list_view-1024x262.png 1024w, https://blog.codersee.com/wp-content/uploads/2022/06/12_created_repository_list_view-768x196.png 768w" sizes="auto, (max-width: 1378px) 100vw, 1378px" /></figure>



<p>After that, let&#8217;s open our project root directory in the terminal and run the following:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">docker build -t [your-repo-uri]:latest .</pre>



<p>This step is required and<strong> URI has to match</strong> our private repository URI so that we will be able to push it.</p>



<p>Following, let&#8217;s authenticate to the ECR registry:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">aws ecr get-login-password --profile app-runner-rds-user --region us-east-1 | docker login --username AWS --password-stdin [you-repo-uri]

#Result: 
Login Succeeded</pre>



<p>Finally, let&#8217;s push our previously created Docker image (if you don&#8217;t remember the tag, you can simply run <code class="EnlighterJSRAW" data-enlighter-language="raw">docker images</code> command):</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">docker push [our_image_tag]</pre>



<p>Of course, this step may take some time, but in the end, we should see the following in our repository:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="1385" height="369" src="http://blog.codersee.com/wp-content/uploads/2022/06/13_pushed_image_view.png" alt="" class="wp-image-3002936" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/13_pushed_image_view.png 1385w, https://blog.codersee.com/wp-content/uploads/2022/06/13_pushed_image_view-300x80.png 300w, https://blog.codersee.com/wp-content/uploads/2022/06/13_pushed_image_view-1024x273.png 1024w, https://blog.codersee.com/wp-content/uploads/2022/06/13_pushed_image_view-768x205.png 768w" sizes="auto, (max-width: 1385px) 100vw, 1385px" /></figure>



<h2 class="wp-block-heading" id="h-7-create-rds-service">7. Create RDS Service</h2>



<p>As the next step, let&#8217;s create a new RDS (Managed Relational Database Service) instance.</p>



<p>Let&#8217;s type rds in the search bar and select the first option:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="929" height="479" src="http://blog.codersee.com/wp-content/uploads/2022/06/14_rds_search.png" alt="This image shows Amazon search result for term &quot;rds&quot;" class="wp-image-3002939" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/14_rds_search.png 929w, https://blog.codersee.com/wp-content/uploads/2022/06/14_rds_search-300x155.png 300w, https://blog.codersee.com/wp-content/uploads/2022/06/14_rds_search-768x396.png 768w" sizes="auto, (max-width: 929px) 100vw, 929px" /></figure>



<p>After that, let&#8217;s click the <strong>Create database</strong> button.</p>



<p>On the next page, let&#8217;s specify the <strong>Standard create</strong> along with <strong>PostgreSQL type, version</strong>, and <strong>Free tier template</strong>:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="765" height="1057" src="http://blog.codersee.com/wp-content/uploads/2022/06/15_rds_method_postgresql_free_tier.png" alt="Screenshot presents Amazon RDS Standard Createa, PostgreSQL with version and Free tier template type." class="wp-image-3002940" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/15_rds_method_postgresql_free_tier.png 765w, https://blog.codersee.com/wp-content/uploads/2022/06/15_rds_method_postgresql_free_tier-217x300.png 217w, https://blog.codersee.com/wp-content/uploads/2022/06/15_rds_method_postgresql_free_tier-741x1024.png 741w" sizes="auto, (max-width: 765px) 100vw, 765px" /></figure>



<p>Following, let&#8217;s specify the <strong>database identifier</strong> with <strong>username</strong> and <strong>password</strong> (in our case, <strong>postgres</strong> and <strong>password</strong> values):</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="764" height="924" src="http://blog.codersee.com/wp-content/uploads/2022/06/16_rds_id_username_password.png" alt="Image presents screenshot from DB instance identifier, master username and password settings" class="wp-image-3002941" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/16_rds_id_username_password.png 764w, https://blog.codersee.com/wp-content/uploads/2022/06/16_rds_id_username_password-248x300.png 248w" sizes="auto, (max-width: 764px) 100vw, 764px" /></figure>



<p>For the storage, let&#8217;s go with the minimum 20Gib value and disabled autoscaling:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="766" height="408" src="http://blog.codersee.com/wp-content/uploads/2022/06/17_rds_storage.png" alt="" class="wp-image-3002942" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/17_rds_storage.png 766w, https://blog.codersee.com/wp-content/uploads/2022/06/17_rds_storage-300x160.png 300w" sizes="auto, (max-width: 766px) 100vw, 766px" /></figure>



<p>Nextly, let&#8217;s configure the connectivity:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="772" height="954" src="http://blog.codersee.com/wp-content/uploads/2022/06/17_rds-connectivity.png" alt="Screenshot shows RDS Connectivity settings." class="wp-image-3002943" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/17_rds-connectivity.png 772w, https://blog.codersee.com/wp-content/uploads/2022/06/17_rds-connectivity-243x300.png 243w, https://blog.codersee.com/wp-content/uploads/2022/06/17_rds-connectivity-768x949.png 768w" sizes="auto, (max-width: 772px) 100vw, 772px" /></figure>



<p>For the <strong>Database authentication</strong>, let&#8217;s keep the defaults and proceed to the <strong>Additional configuration</strong>:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="678" height="1205" src="http://blog.codersee.com/wp-content/uploads/2022/06/18_rds-database_options.png" alt="Screenshot presents RDS database options settings" class="wp-image-3002945" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/18_rds-database_options.png 678w, https://blog.codersee.com/wp-content/uploads/2022/06/18_rds-database_options-169x300.png 169w, https://blog.codersee.com/wp-content/uploads/2022/06/18_rds-database_options-576x1024.png 576w" sizes="auto, (max-width: 678px) 100vw, 678px" /></figure>



<p>After we disable all unnecessary options, let&#8217;s hit <strong>Create database</strong>. Similarly, this process may take a while.</p>



<p>Finally, we should see the green bar and the following details page:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="1419" height="852" src="http://blog.codersee.com/wp-content/uploads/2022/06/19_rds_created_details.png" alt="" class="wp-image-3002946" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/19_rds_created_details.png 1419w, https://blog.codersee.com/wp-content/uploads/2022/06/19_rds_created_details-300x180.png 300w, https://blog.codersee.com/wp-content/uploads/2022/06/19_rds_created_details-1024x615.png 1024w, https://blog.codersee.com/wp-content/uploads/2022/06/19_rds_created_details-768x461.png 768w" sizes="auto, (max-width: 1419px) 100vw, 1419px" /></figure>



<p><strong>Do not close it</strong>, we need these data later.</p>



<p>The last thing we would like to do here is to click the<strong> security group</strong> and add the <strong>All traffic Inbound rule</strong> for <span class="awsui-select-option-label">the </span><strong><span class="awsui-select-option-label">0.0.0.0/0</span></strong> source (for simplicity):</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="1439" height="651" src="http://blog.codersee.com/wp-content/uploads/2022/06/24_inbound.png" alt="Screenhot presents security group" class="wp-image-3002951" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/24_inbound.png 1439w, https://blog.codersee.com/wp-content/uploads/2022/06/24_inbound-300x136.png 300w, https://blog.codersee.com/wp-content/uploads/2022/06/24_inbound-1024x463.png 1024w, https://blog.codersee.com/wp-content/uploads/2022/06/24_inbound-768x347.png 768w" sizes="auto, (max-width: 1439px) 100vw, 1439px" /></figure>



<h2 class="wp-block-heading" id="h-8-configure-app-runner">8. Configure App Runner</h2>



<p>With all of that being done, we can finally switch to the AWS App Runner setup process.</p>



<p>Let&#8217;s navigate to the <strong>App Runner page and hit the Create an App Runner service</strong> button. On the next page, let&#8217;s select the previously pushed image and create a new service role:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="724" height="849" src="http://blog.codersee.com/wp-content/uploads/2022/06/20_app_runner_first_page.png" alt="Screenshot presents first step of App Runner config." class="wp-image-3002947" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/20_app_runner_first_page.png 724w, https://blog.codersee.com/wp-content/uploads/2022/06/20_app_runner_first_page-256x300.png 256w" sizes="auto, (max-width: 724px) 100vw, 724px" /></figure>



<p>Nextly, let&#8217;s specify the service name and add one environment variable:</p>



<ul class="wp-block-list">
<li><strong>DB_HOST</strong>&#8211; database endpoint value from <strong>RDS Management Console</strong></li>
</ul>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="720" height="569" src="http://blog.codersee.com/wp-content/uploads/2022/06/21_app_runner_service_settings.png" alt="Screenshot shows App Runner service settings page." class="wp-image-3002948" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/21_app_runner_service_settings.png 720w, https://blog.codersee.com/wp-content/uploads/2022/06/21_app_runner_service_settings-300x237.png 300w" sizes="auto, (max-width: 720px) 100vw, 720px" /></figure>



<p>In the Auto scaling, let&#8217;s select the <strong>Custom configuration</strong> and <strong>Add new</strong>. Then, let&#8217;s set the <strong>Maximum size</strong> to 1 (although it is not necessary, it is always a good practice to limit scaling to our needs):</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="548" height="643" src="http://blog.codersee.com/wp-content/uploads/2022/06/22_app_runner_custom_scaling_configuration.png" alt="Screenshot presents App Runner custom scaling configuration." class="wp-image-3002949" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/22_app_runner_custom_scaling_configuration.png 548w, https://blog.codersee.com/wp-content/uploads/2022/06/22_app_runner_custom_scaling_configuration-256x300.png 256w" sizes="auto, (max-width: 548px) 100vw, 548px" /></figure>



<p>For the <strong>Health check</strong> and <strong>Security</strong>, let&#8217;s leave the defaults. However, for <strong>Networking</strong>, let&#8217;s select <strong>Custom VPC</strong> and click <strong>Add new</strong>.</p>



<p>Although the connector name is not relevant, the rest of the values should be set properly:</p>



<ul class="wp-block-list">
<li><strong>VPC and Security groups</strong>&#8211; to match database instance values</li>



<li><strong>Subnets</strong>&#8211; at least one value from RDS networking</li>
</ul>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="556" height="687" src="http://blog.codersee.com/wp-content/uploads/2022/06/23_app_runner_custom_connector.png" alt="Screenshot presents a custom connector for AWS App Runner" class="wp-image-3002950" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/23_app_runner_custom_connector.png 556w, https://blog.codersee.com/wp-content/uploads/2022/06/23_app_runner_custom_connector-243x300.png 243w" sizes="auto, (max-width: 556px) 100vw, 556px" /></figure>



<p>After that, let&#8217;s click <strong>Next</strong> and on the next page, the <strong>Create &amp; Deploy</strong> button.</p>



<p>After some time, we should see a green bar once again indicating that the service is up and running:</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1584" height="784" src="http://blog.codersee.com/wp-content/uploads/2022/06/25_app_runner_correct.png" alt="Screenshot shows that App Runner instance is up and running correctly." class="wp-image-3002952" srcset="https://blog.codersee.com/wp-content/uploads/2022/06/25_app_runner_correct.png 1584w, https://blog.codersee.com/wp-content/uploads/2022/06/25_app_runner_correct-300x148.png 300w, https://blog.codersee.com/wp-content/uploads/2022/06/25_app_runner_correct-1024x507.png 1024w, https://blog.codersee.com/wp-content/uploads/2022/06/25_app_runner_correct-768x380.png 768w, https://blog.codersee.com/wp-content/uploads/2022/06/25_app_runner_correct-1536x760.png 1536w" sizes="auto, (max-width: 1584px) 100vw, 1584px" /></figure>



<h2 class="wp-block-heading" id="h-7-testing">7. Testing</h2>



<p>Finally, let&#8217;s test that everything is working, as expected:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl https://{your-default-domain}.awsapprunner.com/api/users/non-db

# Result:
[
  {
    "id": 1,
    "email": "piotr@wolak.com"
  }
]

curl https://{your-default-domain}.awsapprunner.com/api/users

# Result:
[
  {
    "id": 1,
    "email": "john@doe.com"
  }
]</pre>



<p>As we can see, both endpoints returned expected data, which indicates that everything is working fine.</p>



<h2 class="wp-block-heading" id="h-8-summary">8 Summary</h2>



<p>And that would be all for this a bit long article. Together, we have learned how to deploy a <strong>Spring Boot</strong> application With <strong>AWS App Runner</strong> and <strong>Amazon RDS </strong>(using <strong>PostgreSQL</strong>).</p>



<p>I hope you enjoyed it and I will be thankful if you would like to leave a comment if you liked it. Thanks!</p>
<p>The post <a href="https://blog.codersee.com/spring-boot-app-aws-app-runner-amazon-rds/">Deploy Spring Boot App With AWS App Runner And Amazon RDS</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/spring-boot-app-aws-app-runner-amazon-rds/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Flyway Migrations With Spring WebFlux (R2DBC)</title>
		<link>https://blog.codersee.com/flyway-spring-webflux/</link>
					<comments>https://blog.codersee.com/flyway-spring-webflux/#comments</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Tue, 31 May 2022 05:41:27 +0000</pubDate>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Spring]]></category>
		<category><![CDATA[Flyway]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<category><![CDATA[Spring WebFlux]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=2502876</guid>

					<description><![CDATA[<p>In this article I will show you how to configure Flyway migrations when working with Spring WebFlux and Spring Data R2DBC.</p>
<p>The post <a href="https://blog.codersee.com/flyway-spring-webflux/">Flyway Migrations With Spring WebFlux (R2DBC)</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2 class="article-heading-introduction">1. Introduction</h2>
<p>In this short article, I would like to show you how to configure <strong>Flyway</strong> when working with<strong> Spring WebFlux and Spring Data R2DBC</strong>.</p>
<p>In my <a href="https://blog.codersee.com/flyway-migrations-with-spring-boot/" target="_blank" rel="noopener">previous tutorial</a>, we&#8217;ve learned a lot about Flyway itself and how to set it up correctly, when working with Spring Boot. Nevertheless, if we would like to apply this knowledge when building a reactive web application with Spring WebFlux and Spring Data R2DBC, we would have to take a different approach. And that&#8217;s what we are going to focus on in this tutorial.</p>
<h2 class="article-heading-introduction">2. Imports</h2>
<h3 class="article-heading-introduction">2.1. Create Spring WebFlux With R2DBC Project</h3>
<p>As the first step, let&#8217;s create a skeleton project. We can do so by simply going to the <a href="https://start.spring.io/" target="_blank" rel="noopener">Spring Initializr</a> page and selecting the following dependencies:</p>
<ul>
<li>Spring Reactive Web</li>
<li>Spring Data R2DBC</li>
<li>PostgreSQL Driver</li>
</ul>
<p>After that, we should see the following in our <code class="EnlighterJSRAW" data-enlighter-language="kotlin">build.gradle.kts</code> file:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">implementation("org.springframework.boot:spring-boot-starter-data-r2dbc")
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
runtimeOnly("org.postgresql:postgresql")
runtimeOnly("org.postgresql:r2dbc-postgresql")

// other dependencies</pre>
<h3 class="article-heading-introduction">2.2. Add Flyway Dependency</h3>
<p>Unfortunately, we can&#8217;t add the Flyway dependency to our Spring WebFlux project on the same page, so we have to do it manually:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">implementation("org.flywaydb:flyway-core:8.5.11")</pre>
<p>With that being done, we have to reload Gradle changes and wait till the additional dependency is fetched to our environment.</p>
<p>&nbsp;</p>
<p><a href="https://codersee.com/newsletter/"><img loading="lazy" decoding="async" class="aligncenter wp-image-3002956 size-large" src="http://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-1024x576.png" alt="Image shows two ebooks people can get for free after joining newsletter" width="800" height="419" /></a></p>
<p>&nbsp;</p>
<h2 class="article-heading-introduction">3. Configure application.properties (or YAML) File</h2>
<p>As the next step, let&#8217;s head to the <code class="EnlighterJSRAW" data-enlighter-language="kotlin">application.properties</code> file (or <code class="EnlighterJSRAW" data-enlighter-language="kotlin">application.yaml</code> in my case) and configure database connection along with Flyway:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="yaml">spring:
  r2dbc:
    url: r2dbc:postgresql://localhost:5432/
    username: postgres
    password: password
  flyway:
    url: jdbc:postgresql://localhost:5432/
    user: postgres
    password: password</pre>
<p>Please keep in mind that the values used for port, username, and password may be different in your case. Moreover, in the case of the Flyway connection, we have to use <strong>JDBC</strong>.</p>
<h2 class="article-heading-introduction">4. Implement Flyway Migration</h2>
<p>Nextly, let&#8217;s prepare an SQL file, which we would like to run as the first migration. Let&#8217;s call it <strong>V1__My_First_Migration.sql</strong>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="sql">CREATE TABLE person(
  id SERIAL NOT NULL PRIMARY KEY,
  email TEXT NOT NULL
);</pre>
<p>As we can see, this simple script will create a new table called <code class="EnlighterJSRAW" data-enlighter-language="raw">person</code>. Please remember to put this file in the correct directory-<strong> resources/db/migration/</strong> in our case.</p>
<h2 class="article-heading-introduction">5. Add Flyway Configuration For R2DBC</h2>
<p>As the next step, let&#8217;s run our application to see if our Flyway migration is working. Technically, the app started successfully. Nevertheless, logs do not contain anything about applied migration and when we check the database we can clearly see, that <strong>migration was not run</strong>.</p>
<p>To change that, let&#8217;s add a <code class="EnlighterJSRAW" data-enlighter-language="kotlin">FlywayConfiguration</code> file to our project:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Configuration
class FlywayConfiguration(private val env: Environment) {

    @Bean(initMethod = "migrate")
    fun flyway(): Flyway {
        return Flyway(Flyway.configure()
            .dataSource(
                env.getRequiredProperty("spring.flyway.url"),
                env.getRequiredProperty("spring.flyway.user"),
                env.getRequiredProperty("spring.flyway.password"))
        )
    }
}</pre>
<p>Alternatively, we can use <code class="EnlighterJSRAW" data-enlighter-language="kotlin">@Value</code> annotation, as well:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Configuration
class FlywayConfiguration(
    @Value("\${spring.flyway.url}") private val url: String,
    @Value("\${spring.flyway.user}") private val user: String,
    @Value("\${spring.flyway.password}") private val password: String
) {

    @Bean(initMethod = "migrate")
    fun flyway(): Flyway {
        return Flyway(
            Flyway.configure()
                .dataSource(url, user, password)
        )
    }
}</pre>
<p>As we can see, this configuration is responsible for creating a new Flyway bean with the URL, username, and password obtained from our <code class="EnlighterJSRAW" data-enlighter-language="kotlin">application.yaml</code> file.</p>
<p>If we rerun our app once again, we will see the following:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">Creating Schema History table "public"."flyway_schema_history" ...
Current version of schema "public": &lt;&lt; Empty Schema &gt;&gt;
Migrating schema "public" to version "1 - My First Migration"
Successfully applied 1 migration to schema "public", now at version v1 (execution time 00:00.034s)</pre>
<p>The above message clearly indicates that the migration was run and we can expect a new table in our database.</p>
<h2 class="article-heading-introduction">6. Summary</h2>
<p>And that would be all for this short article on how to configure <strong>Flyway migrations with Spring WebFlux and R2DBC</strong>.</p>
<p>Let me know if you found this material useful in the comment sections below, or by using the <a href="https://codersee.com/contact/" target="_blank" rel="noopener">contact form</a>.</p>
<p>Finally, if you would like to see the whole source code, please refer to this <a href="https://github.com/codersee-blog/spring-webflux-flyway" target="_blank" rel="noopener">GitHub repository</a>.</p>
<p>The post <a href="https://blog.codersee.com/flyway-spring-webflux/">Flyway Migrations With Spring WebFlux (R2DBC)</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/flyway-spring-webflux/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Flyway Migrations With Spring Boot</title>
		<link>https://blog.codersee.com/flyway-migrations-with-spring-boot/</link>
					<comments>https://blog.codersee.com/flyway-migrations-with-spring-boot/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Mon, 23 May 2022 06:00:21 +0000</pubDate>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Spring]]></category>
		<category><![CDATA[Flyway]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=2502844</guid>

					<description><![CDATA[<p>In this tutorial, we will learn what exactly Flyway is and how we can configure it when working with Spring Boot.</p>
<p>The post <a href="https://blog.codersee.com/flyway-migrations-with-spring-boot/">Flyway Migrations With Spring Boot</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="h-1-introduction">1. Introduction</h2>



<p>In this blog post, I would like to show you what exactly <a href="https://flywaydb.org/" target="_blank" rel="noopener">Flyway</a> is and how to configure it, when working with<strong> Spring Boot.</strong></p>



<p>Together, we will spend some time to figure out:</p>



<ul class="wp-block-list">
<li>why do we even need it in our project?</li>



<li>how can we quickly configure Flyway with Spring Boot?</li>



<li>finally, how can we customize its behavior?</li>
</ul>



<h2 class="wp-block-heading" id="h-2-what-is-flyway-and-what-problems-does-it-solve">2. What is Flyway and What Problems Does It Solve?</h2>



<p>To put it simply, Flyway is a <strong>database versioning tool</strong>. Just like Git is a version control system for our codebase, Flyway takes care of<strong> database schema management</strong>.</p>



<p>Let&#8217;s imagine that we&#8217;ve started a Spring Boot project, which communicates with the PostgreSQL database. We&#8217;ve been working on our own, implementing new features, adding new tables, and changing the schema many times. At some point, a new programmer joins our team, so we simply export our local or remote database and send him the script, so that he will be able to set up a local environment.</p>



<p>So far so good, but what will happen when more people join our team? Or we would like to create more testing environments? Of course, we could save the script and update it each time, we change anything, but this can introduce plenty of problems, for example:</p>



<ul class="wp-block-list">
<li>when multiple people are editing schema asynchronously, how can we make sure that all their scripts are applied in a specific environment?</li>



<li>how can we be sure that all developers have exactly the same (or a specific) version on their local?</li>



<li>how can we determine the correct order of scripts, so that nothing breaks when applying them?</li>
</ul>



<p>If you are not convinced after reading the above points, then imagine working without Git when cooperating with others and exchanging written code manually 🙂 Technically- doable- nevertheless definitely not recommended.</p>



<p>Of course, Flyway is not the only tool we could use with Spring Boot, however, it is definitely<strong> easy to work with</strong>.</p>


<p>[elementor-template id=&#8221;9007393&#8243;]</p>



<h2 class="wp-block-heading" id="h-3-setup-flyway-with-spring-boot">3. Setup Flyway With Spring Boot</h2>



<p>With all of that being said, let&#8217;s switch to the practice part. If you would like to simply download the skeleton for this tutorial, I&#8217;ve uploaded it to <a href="https://github.com/codersee-blog/spring-boot-flyway-skeleton" target="_blank" rel="noopener">this GitHub repository</a>. Nevertheless, I highly encourage you to set up the Spring Boot project on your own and follow below steps. This way, you&#8217;re definitely gonna learn more.</p>



<h3 class="wp-block-heading" id="h-3-1-imports">3.1. Imports</h3>



<p>As the first step, let&#8217;s add the necessary imports:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">implementation("org.flywaydb:flyway-core:10.13.0")
runtimeOnly("org.flywaydb:flyway-database-postgresql:10.13.0")
implementation("org.postgresql:postgresql:42.7.3")
implementation("org.springframework.boot:spring-boot-starter-data-jdbc") //alternatively, we could use spring-boot-starter-data-jpa</pre>



<p>The above 4 are the bare minimum required to work with Flyway and Spring Boot.</p>



<h3 class="wp-block-heading" id="h-3-2-edit-application-properties">3.2. Edit Application Properties</h3>



<p>As the next step, we have to make sure that we&#8217;ve set up the database connection properly.</p>



<p>The easiest way to do so is via <strong>application.yaml</strong> (or <strong>application.properties</strong>) file:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">spring:
  datasource:
    url: "jdbc:postgresql://localhost:5432/your-db-name"
    username: db-username
    password: db-password</pre>



<p>By default, Flyway will use the <strong>@Primary Data Source</strong> (which we&#8217;ve configured above).</p>



<h3 class="wp-block-heading" id="h-3-3-add-migrations-directory">3.3. Add Migrations Directory</h3>



<p>Nextly, we have to add <strong>/db/migration</strong> directory to the <strong>/resources</strong>. If we don&#8217;t do so and try to run the application, it will fail with the following error:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Flyway failed to initialize: none of the following migration scripts locations could be found:</p>
</blockquote>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>classpath:db/migration</p>
</blockquote>



<p>Of course, we can customize that, and we will learn how to do that later.</p>



<h3 class="wp-block-heading" id="h-3-4-validation">3.4. Validation</h3>



<p>For now, let&#8217;s check if our setup is working correctly (please make sure that you have a database with an empty schema <strong>up and running</strong>).</p>



<p>After we run the application, we should see the following in the logs:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Flyway Community Edition 8.5.11 by Redgate
...
Successfully validated 0 migrations (execution time 00:00.009s)
No migrations found. Are your locations set up correctly?
Creating Schema History table "public"."flyway_schema_history" ...
Schema "public" is up to date. No migration necessary.
</pre>



<p>Moreover, we can see that a new table called <strong>flyway_schema_history</strong> was added to our schema.</p>



<p>In brief, Flyway uses this table to manage versions and control applied, modified or added scripts. If you would like to see more theory, then <a href="https://flywaydb.org/documentation/getstarted/how" target="_blank" rel="noopener">this article</a> from their documentation is a great source for that.</p>



<h3 class="wp-block-heading" id="h-3-5-add-a-migration">3.5. Add a Migration</h3>



<p>Finally, let&#8217;s add our first migration file called <strong>V1__Create_User_Table.sql</strong>. This &#8220;strange&#8221; filename is the<strong> default Flyway naming convention</strong>, which consists of:</p>



<ul class="wp-block-list">
<li><strong>a prefix</strong>&#8211; <strong>B</strong> for baseline, <strong>R</strong> for repeatable,  <strong>U</strong> for undo, and <strong>V</strong> for versioned migrations</li>



<li><strong>migration number</strong>&#8211; number <strong>1</strong> in our case. If we would like it to be <strong>1.1</strong>, then we should start the name with <strong>V1_1</strong></li>



<li><strong>double underscore</strong>&#8211; separates version from migration description</li>



<li><strong>description</strong>&#8211; underscores are translated to spaces, so our migration will become <strong>Create User Table </strong></li>
</ul>



<p>With that being said, let&#8217;s add an example SQL script:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="sql" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">CREATE TABLE IF NOT EXISTS app_user (
  id SERIAL NOT NULL PRIMARY KEY,
  name TEXT NOT NULL
);
</pre>



<p>After that, let&#8217;s run our Spring Boot application once again and check out printed logs:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Migrating schema "public" to version "1 - Create User Table"</pre>



<p>As can be seen, migration was applied successfully and a new table was added to our database.</p>



<h2 class="wp-block-heading" id="h-4-flyway-customization-with-application-properties">4. Flyway Customization With application.properties</h2>



<h3 class="wp-block-heading" id="h-4-1-enable-disable-flyway">4.1. Enable/Disable Flyway</h3>



<p>By default, Flyway is enabled when added to Spring Boot. Nevertheless, if we would like to change that, we can set the <strong>enabled</strong> flag:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">spring:
  flyway:
    enabled: false</pre>



<h3 class="wp-block-heading" id="h-4-2-set-custom-migrations-directory">4.2. Set Custom Migrations Directory</h3>



<p>As I&#8217;ve mentioned earlier, the default directory is <strong>/db/migration</strong>, but it can be customized with <strong>spring.flyway.locations</strong> property:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">spring:
  flyway:
    locations: "classpath:my-custom-dir,classpath:my-custom-dir-2"</pre>



<p>As we can see, we can provide multiple directories. When working with Spring Boot, a <strong>classpath</strong> means <strong>resources</strong> directory. Alternatively, we can refer to any directory in the system using <strong>filesystem</strong> instead.</p>



<p>It&#8217;s worth mentioning that when we specify multiple directories, then at least one of them has to exist (but not all of them). Moreover, we can&#8217;t duplicate migration versions among our directories.</p>



<p>So, in practice:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">/resources
    /my-custom-dir
        V2__one.sql
    /my-custom-dir-2
        V1__one.sql
</pre>



<p>These two directories are treated, just like one and if we would specify V1 version twice, then the <strong>FlywayException</strong> would be thrown:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Found more than one migration with version 1</p>
</blockquote>



<h3 class="wp-block-heading" id="h-4-3-vendor-aware-directory">4.3. Vendor Aware Directory</h3>



<p>Additionally, Flyway allows us not only to specify hard-coded paths, but we can use a <strong>vendor</strong> placeholder, which will be resolved on the fly:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">spring:
  flyway:
    locations: "classpath:my-custom-dir/{vendor}"</pre>



<p>With the above config, Flyway will look for migrations files inside the <strong>/resources/my-custom-dir/postgresql</strong> directory. You can find a full list of supported vendors <a href="https://github.com/spring-projects/spring-boot/blob/main/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jdbc/DatabaseDriver.java" target="_blank" rel="noopener">right here</a>.</p>



<h2 class="wp-block-heading" id="h-5-flyway-java-kotlin-configuration">5. Flyway Java (Kotlin) Configuration</h2>



<p>It&#8217;s worth mentioning that Flyway allows us to <strong>apply migrations written in Java</strong>. When working with Spring Boot, they will be auto-configured with any bean implementing a <strong>JavaMigration</strong> interface. However, this interface requires us to implement a few methods, so the recommended way is to extend the <strong>BaseJavaMigration</strong> abstract class instead (and sufficient in most cases).</p>



<p>Given that, let&#8217;s have a look at the example written in Kotlin (which technically does not differ too much from its Java counterpart):</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Component
class V1__Create_User_Table : BaseJavaMigration() {
    override fun migrate(context: Context) {
        val update = context.connection.createStatement()
        update.execute(
            """
            CREATE TABLE IF NOT EXISTS app_user (
                id SERIAL NOT NULL PRIMARY KEY, 
                name TEXT NOT NULL
            );
            """.trimIndent()
        )
    }
}

@Component
class V2__Drop_User_Table : BaseJavaMigration() {
    override fun migrate(context: Context) {
        val update = context.connection.createStatement()
        update.execute("DROP TABLE app_user;")
    }
}</pre>



<p>As we can see, the only thing we have to do is to override the <strong>migrate</strong> method, and that class names should follow the default Flyway naming convention.</p>



<p>If you are wondering whether we can <strong>mix SQL files with Spring Beans configuration</strong>, then the answer is <strong>yes</strong> (however we should be really cautious about it).</p>



<h2 class="wp-block-heading" id="h-6-flyway-with-spring-boot-summary">6. Flyway With Spring Boot Summary</h2>



<p>And that would be all for this tutorial about <strong>Flyway With Spring Boot</strong>. I believe the knowledge presented here will be sufficient to easily incorporate this tool into any <a href="https://blog.codersee.com/category/spring/" target="_blank" rel="noopener">Spring Boot</a> project. Moreover, if you&#8217;d like to learn how to incorporate Flyway to an existing Spring Boot project, or how to use it with Spring WebFlux, <strong>then check out the <a href="https://blog.codersee.com/tag/flyway/" target="_blank" rel="noopener">flyway tag</a></strong>.</p>



<p>Let me know what you think about database version control tools. Do you like Flyway, or maybe you prefer some other tool?</p>



<p>Have a great day and see you in the next articles! 🙂</p>
<p>The post <a href="https://blog.codersee.com/flyway-migrations-with-spring-boot/">Flyway Migrations With Spring Boot</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/flyway-migrations-with-spring-boot/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>REST API With Ktor, Ktorm and PostgreSQL</title>
		<link>https://blog.codersee.com/rest-api-ktor-ktorm-postgresql/</link>
					<comments>https://blog.codersee.com/rest-api-ktor-ktorm-postgresql/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Sun, 23 Jan 2022 07:00:52 +0000</pubDate>
				<category><![CDATA[Ktor]]></category>
		<category><![CDATA[Ktorm]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=1002001</guid>

					<description><![CDATA[<p>In this step by step tutorial I will show you how to implement a simple REST API with Ktor, Ktorm and PostgreSQL.</p>
<p>The post <a href="https://blog.codersee.com/rest-api-ktor-ktorm-postgresql/">REST API With Ktor, Ktorm and PostgreSQL</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="h-1-introduction">1. Introduction</h2>



<p>In this step-by-step tutorial, I would like to show you how to create a simple <strong>REST API</strong> using <a href="https://ktor.io/" target="_blank" rel="noopener">Ktor</a>, <a href="https://www.ktorm.org/" target="_blank" rel="noopener">Ktorm</a>, and <a href="https://www.postgresql.org/" target="_blank" rel="noopener">PostgreSQL (aka Postgres)</a>.</p>



<p><strong>Ktor</strong> is a free, open-source framework, which allows us to easily build connected applications. When it comes to server-side applications, we can treat it as a lightweight and powerful alternative to Spring Boot. Additionally, it was written in Kotlin programming language by its&#8217; creators- the JetBrains team.</p>



<p><strong>Ktorm</strong>, on the other hand, is a lightweight and efficient <strong>ORM Framework for Kotlin</strong> directly based on pure JDBC. Furthermore, combined with <strong>PostgreSQL</strong> makes an interesting combination for a REST API tech stack.</p>



<h2 class="wp-block-heading" id="h-2-generate-ktor-project">2. Generate Ktor Project</h2>



<p>With all of that being said, let&#8217;s create the Ktor project (and in the next chapter, I will show you how to add Ktorm and PostgreSQL dependencies).</p>



<p>If you are the owner of <em>IntelliJ Ultimate Edition</em>, then it ships with Ktor support out of the box. Nevertheless, in this tutorial, we&#8217;ll focus on the alternative: <a href="https://start.ktor.io" target="_blank" rel="noopener">https://start.ktor.io</a>, which is pretty similar to the <em>Spring Initializr</em> page.</p>



<p>As the first step, let&#8217;s specify the project name along with other, necessary data:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="730" height="868" src="http://blog.codersee.com/wp-content/uploads/2022/01/ktor-ktorm-postgresql-1.png" alt="Image shows project generator page used to generate our ktor with ktorm and postgresql project" class="wp-image-1002002" srcset="https://blog.codersee.com/wp-content/uploads/2022/01/ktor-ktorm-postgresql-1.png 730w, https://blog.codersee.com/wp-content/uploads/2022/01/ktor-ktorm-postgresql-1-252x300.png 252w" sizes="auto, (max-width: 730px) 100vw, 730px" /></figure>



<p>Please remember about selecting the same Ktor version, so that the code from this tutorial will work for sure. Don&#8217;t worry about a sample code generated, we will take care of it later.</p>



<p>As the next step, let&#8217;s click the<strong> Add plugins</strong> button and add the <strong>kotlinx.serialization</strong>&#8211; we&#8217;ll use it to serialize our objects into JSON format:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="646" height="642" src="http://blog.codersee.com/wp-content/uploads/2022/01/ktor-ktorm-postgresql-2.png" alt="Image shows ktor project generator page" class="wp-image-1002003" srcset="https://blog.codersee.com/wp-content/uploads/2022/01/ktor-ktorm-postgresql-2.png 646w, https://blog.codersee.com/wp-content/uploads/2022/01/ktor-ktorm-postgresql-2-300x298.png 300w, https://blog.codersee.com/wp-content/uploads/2022/01/ktor-ktorm-postgresql-2-150x150.png 150w" sizes="auto, (max-width: 646px) 100vw, 646px" /></figure>



<p>As we can see, two other plugins have been added automatically: <strong>ContentNegotiation</strong> and <strong>Routing</strong>. Please do not remove them, we will need them later.</p>


<p>[elementor-template id=&#8221;9007393&#8243;]</p>



<h2 class="wp-block-heading" id="h-2-add-ktorm-and-postgresql-to-ktor">2. Add Ktorm and PostgreSQL To Ktor</h2>



<p>Nextly, let&#8217;s open up our Ktor Postgres project with IntelliJ and go to the <strong>gradle.properties</strong> file.</p>



<p>Unfortunately, the Project Generator does not allow us to add all of the necessary imports, so we will have to do it manually:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">postgresql_driver_version=42.3.1
ktorm_version=3.4.1</pre>



<p>As the next step, let&#8217;s add a few lines within the <strong>build.gradle.kts</strong> file and <strong>reload the Gradle project</strong>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">val ktorm_version: String by project
val postgresql_driver_version: String by project

dependencies {

// other dependencies

implementation("org.ktorm:ktorm-core:$ktorm_version")
implementation("org.ktorm:ktorm-support-postgresql:$ktorm_version")
implementation("org.postgresql:postgresql:$postgresql_driver_version")

}</pre>



<p>As we can see, these dependencies are necessary in order to connect our Ktor application with the Postgres database.</p>



<h2 class="wp-block-heading" id="h-3-clean-up-the-code">3. Clean Up The Code</h2>



<p>After that, let&#8217;s clean up the project a bit and delete the <strong>Routing.kt</strong> along with its invocation within the <strong>Application.kt</strong>.</p>



<p>The second file should now be looking, as follows:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun main() {
  embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
    configureSerialization()
  }.start(wait = true)
}</pre>



<p>Then, let&#8217;s go to the <strong>Serialization.kt</strong> file and remove unnecessary code, as well:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun Application.configureSerialization() {
  install(ContentNegotiation) {
    json()
  }
}</pre>



<p>The above code will be necessary for JSON serialization.</p>



<h2 class="wp-block-heading" id="h-4-prepare-postgresql-with-docker-compose">4. Prepare PostgreSQL With Docker Compose</h2>



<p>Basically, if you already have a PostgreSQL instance running on your machine, you can just skip the docker-compose part and just run the below SQL script.</p>



<p>Either way, let&#8217;s create the <strong>sql</strong> folder in the root directory and add the <strong>init-db.sql</strong> file:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="sql" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">CREATE TABLE book
(
  id   SERIAL NOT NULL,
  name varchar NOT NULL
);

INSERT INTO book(name) VALUES ('Book #1');
INSERT INTO book(name) VALUES ('Book #2');</pre>



<p>We can clearly see, that after the script is run, a new table will be added and populated with test data.</p>



<p>Following, let&#8217;s go to the root of the Ktor project and add the <strong>docker-compose.yaml</strong> file with Postgres definition:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="yaml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">version: "3.9"
services:
  postgres-sandbox:
    image: postgres:14
    ports:
      - '5438:5432'
    volumes:
      - ./sandbox-db:/var/lib/postgresql/data
      - ./sql/init-db.sql:/docker-entrypoint-initdb.d/init-db.sql
    environment:
      - POSTGRES_NAME=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres</pre>



<p>The above code is responsible for a few things:</p>



<ul class="wp-block-list">
<li>pulling the Docker image for PostgreSQL</li>



<li>publishing the container&#8217;s <strong>5432</strong> port to the host&#8217;s <strong>5438 </strong>(to put it simply- we will connect from our application using port <strong>5438</strong>)</li>



<li>running initialization script prepared above</li>



<li>and finally, setting credentials through environment variables</li>
</ul>



<p>To start our PostgreSQL instance, let&#8217;s run the below command:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">docker-compose up

# To validate:
docker ps
CONTAINER ID IMAGE       COMMAND                CREATED       STATUS       PORTS                  NAMES
2f5716235039 postgres:14 "docker-entrypoint.s…" 6 minutes ago Up 6 minutes 0.0.0.0:5438->5432/tcp ktor-ktorm-postgresql_postgres-sandbox_1

</pre>



<p>As we can see, everything is working fine and now we will be able to connect our Ktor app with Postgres.</p>



<h2 class="wp-block-heading" id="h-5-implement-dtos">5. Implement DTOs</h2>



<p>Following, let&#8217;s prepare request and response classes, which will be used in our REST API. Technically, we don&#8217;t have to do that, but it&#8217;s a good practice to keep them separate.</p>



<p>Let&#8217;s start with the <strong>BookRequest</strong>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Serializable
data class BookRequest(
  val name: String
)</pre>



<p>Similarly, let&#8217;s implement the <strong>BookResponse </strong>and <strong>ErrorResponse</strong>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Serializable
data class BookResponse(
  val id: Long,
  val name: String
)

@Serializable
data class ErrorResponse(val message: String)</pre>



<p>You might have noticed, that both classes are annotated with <strong>@Serializable</strong> (from <strong>kotlinx.serialization </strong>library). According to the documentation:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Applying Serializable to the Kotlin class instructs the serialization plugin to automatically generate implementation of KSerializer for the current class, that can be used to serialize and deserialize the class.</p>
</blockquote>



<p>To put it simply, this annotation is necessary if we want to operate on these two classes in our REST API.</p>



<h2 class="wp-block-heading" id="h-6-add-entity-and-columns-bindings">6. Add Entity And Columns Bindings</h2>



<p>With all of that being done, let&#8217;s add the code responsible for <strong>object-relational mapping</strong>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">interface Book : Entity&lt;Book> {
  companion object : Entity.Factory&lt;Book>()

  val id: Long?
  var name: String
}

object Books : Table&lt;Book>("book") {
  val id = long("id").primaryKey().bindTo(Book::id)
  val name = varchar("name").bindTo(Book::name)
}</pre>



<p>It may seem a bit counterintuitive in the beginning, so let&#8217;s spend some time understanding it better.</p>



<p>The first one, <strong>Book</strong>, is a representation of the entity class, which will be used to operate on it. You might have already noticed, that it&#8217;s been declared as an interface- <strong>that’s a design requirement of Ktorm</strong>. (Ktorm 2.5 introduced the possibility to declare them as classes, but this approach has some limitations, which you can read more about<a href="https://www.ktorm.org/en/define-entities-as-any-kind-of-classes.html" target="_blank" rel="noopener"> here</a>).</p>



<p>Additionally, you might be curious, what exactly does this line do?</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">companion object : Entity.Factory&lt;Book>()
</pre>



<p>Basically, interfaces cannot be instantiated, but to help us with that, Ktorm provides an abstract class <strong>Entity.Factory</strong>. It overloads the <strong>invoke</strong> operator so that we will be able to create Book instances and initialize its properties pretty easily:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">val newBook = Book {
  name = bookRequest.name
}
</pre>



<p>Finally, the second part, an object called <strong>Books</strong> is responsible for binding our entity with the database table.</p>



<p>It allows us to specify columns types, and names and wire them with our entity through the <strong>.bindTo()</strong> method. Whatsoever, we will use this object later, when working with our Ktor Postgres database connection.</p>



<h2 class="wp-block-heading" id="h-7-connect-to-postgresql">7. Connect To PostgreSQL</h2>



<p>As the next step, let&#8217;s implement the <strong>BookService</strong> and connect to the database:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">class BookService {

  private val database = Database.connect(
    url = "jdbc:postgresql://localhost:5438/postgres",
    driver = "org.postgresql.Driver",
    user = "postgres",
    password = "postgres"
  )
}</pre>



<p>As we can see, the connect function allows us to specify Ktor Postgres connection details. Please make sure that these values match the actual state in your case. As a result, we receive a new database object, which we will use to perform DB operations.</p>



<h2 class="wp-block-heading" id="h-8-implement-service-methods">8. Implement Service Methods</h2>



<p>With all of that being done, we can finally add logic responsible for books management.</p>



<p>Let&#8217;s go to the <strong>BookService</strong> and add 4 methods:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun createBook(bookRequest: BookRequest): Boolean {
  val newBook = Book {
    name = bookRequest.name
  }

  val affectedRecordsNumber = 
    database.sequenceOf(Books)
      .add(newBook)

  return affectedRecordsNumber == 1
}

fun findAllBooks(): Set&lt;Book> =
  database.sequenceOf(Books).toSet()

fun findBookById(bookId: Long): Book? =
  database.sequenceOf(Books)
    .find { book -> book.id eq bookId }

fun updateBookById(bookId: Long, bookRequest: BookRequest): Boolean {
  val foundBook = findBookById(bookId)
    foundBook?.name = bookRequest.name

  val affectedRecordsNumber = foundBook?.flushChanges()

  return affectedRecordsNumber == 1
}
fun deleteBookById(bookId: Long): Boolean {
  val foundBook = findBookById(bookId)

  val affectedRecordsNumber = foundBook?.delete()

  return affectedRecordsNumber == 1
}</pre>



<h3 class="wp-block-heading" id="h-8-1-create-book">8.1. Create Book</h3>



<p>Let&#8217;s take a while to analyze each method:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun createBook(bookRequest: BookRequest): Boolean {
  val newBook = Book {
    name = bookRequest.name
  }

  val affectedRecordsNumber = 
    database.sequenceOf(Books)
      .add(newBook)

  return affectedRecordsNumber == 1
}
</pre>



<p>As we can see, the <strong>createBook() </strong>creates a new Book instance (thanks to the companion object) and inserts it into the table using the <strong>database</strong> object.</p>



<p>The <strong>sequenceOf()</strong>, instantiates a new <strong>EntitySequence</strong> object. It exposes a sequence API, which we can use to perform DB operations, like querying or inserting data.</p>



<p>Finally, the <strong>.add()</strong> method inserts our entity into the database and returns the number of affected records.</p>



<p>The last line of this function simply validates, whether the number of affected records is equal to one (any other number indicates that something went wrong).</p>



<h3 class="wp-block-heading" id="h-8-2-fetching-books">8.2. Fetching Books</h3>



<p>Nextly, let&#8217;s check the querying functions:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun findAllBooks(): Set&lt;Book> =
  database.sequenceOf(Books).toSet()

fun findBookById(bookId: Long): Book? =
  database.sequenceOf(Books)
    .find { book -> book.id eq bookId }
</pre>



<p>They are pretty much the same, except for the fact, that in the second case the <strong>.find{}</strong> function will be translated to the WHERE clause in the generated SQL.</p>



<h3 class="wp-block-heading" id="h-8-3-update-book">8.3. Update Book</h3>



<p>As the next one, let&#8217;s check out the <strong>updateBookById()</strong>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun updateBookById(bookId: Long, bookRequest: BookRequest): Boolean {
  val foundBook = findBookById(bookId)
  foundBook?.name = bookRequest.name

  val affectedRecordsNumber = foundBook?.flushChanges()

  return affectedRecordsNumber == 1
}</pre>



<p>We can clearly see, that we can operate on the fetched entity, just like on any object in Kotlin. Nevertheless, for the changes to take effect, we must invoke the <strong>.flushChanges()</strong> function, which returns the affected records number.</p>



<h3 class="wp-block-heading" id="h-8-4-delete-book">8.4. Delete Book</h3>



<p>Finally, the <strong>deleteBookById</strong>():</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun deleteBookById(bookId: Long): Boolean {
  val foundBook = findBookById(bookId)

  val affectedRecordsNumber = foundBook?.delete()

  return affectedRecordsNumber == 1
}</pre>



<p>Although it&#8217;s performing a different action, the mechanism behind it remains the same.</p>



<h2 class="wp-block-heading" id="h-9-implement-book-routes">9. Implement Book Routes</h2>



<p>With all of that being done, we can finally add the necessary routes.</p>



<p>In Ktor, <strong>Routing</strong> is a core plugin responsible for handling incoming requests. If you&#8217;ve ever been working with Spring Boot before, then you may find it similar to the Controller concept.</p>



<h3 class="wp-block-heading" id="h-9-1-edit-application-kt">9.1. Edit Application.kt</h3>



<p>As the first step, let&#8217;s get back to the <strong>Application.kt</strong> and add <strong>configureBookRoutes()</strong> invocation:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun main() {
  embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
    configureBookRoutes()
    configureSerialization()
  }.start(wait = true)
}</pre>



<h3 class="wp-block-heading" id="h-9-2-create-bookroutes-kt">9.2. Create BookRoutes.kt</h3>



<p>Secondly, let&#8217;s add the <strong>BookRoutes.kt</strong> file with the necessary function:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun Application.configureBookRoutes() {
  routing {
    route("/book") {
      val bookService = BookService()
      createBook(bookService)
      getAllBooksRoute(bookService)
      getBookByIdRoute(bookService)
      updateBookByIdRoute(bookService)
      deleteBookByIdRoute(bookService)
    }
  }
}

private fun Book?.toBookResponse(): BookResponse? =
  this?.let { BookResponse(it.id!!, it.name) }</pre>



<p>To put it simply, the <strong>routing</strong> block installs a Routing feature in our application. On the other hand, the <strong>route </strong>builds a route to match the specified path. This way, we can keep relevant functions together and avoid duplicating the <em>/book</em> path for each of them.</p>



<p>When it comes to the <strong>toBookResponse()</strong>, this extension function will help us to convert <strong>Book</strong> entities into <strong>BookResponses</strong>.</p>



<h3 class="wp-block-heading" id="h-9-3-add-create-book-route">9.3. Add Create Book Route</h3>



<p>With this in mind, let&#8217;s implement the rest of the code step by step. As the first one, let&#8217;s add the <strong>createBook()</strong>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun Route.createBook(bookService: BookService) {
  post {
    val request = call.receive&lt;BookRequest>()

    val success = bookService.createBook(bookRequest = request)

    if (success)
      call.respond(HttpStatusCode.Created)
    else
      call.respond(HttpStatusCode.BadRequest, ErrorResponse("Cannot create book"))
  }
}</pre>



<p>The above code will be responsible for handling <em>POST /book</em> requests. As a word of explanation, the <strong>call.receive&lt;BookRequest&gt;()</strong> deserializes the content of the request (JSON) into the BookRequest instance. When the content cannot be translated, it throws <strong>ContentTransformationException</strong> (it&#8217;s worth to remember in real-life scenarios).</p>



<p>On the contrary, the <strong><span class="enlighter-text"> call.</span><span class="enlighter-m3">respond</span>()</strong> is responsible for sending responses to the client. We can clearly see that if creation is successful, the <strong>201 Created</strong> is returned. However, if this is not the case, then the <strong>400 Bad Request</strong> with an appropriate message is returned.</p>



<h3 class="wp-block-heading" id="h-9-4-implement-get-all-books-route">9.4. Implement Get All Books Route</h3>



<p>As the next step, let&#8217;s add the <strong>getAllBooksRounte()</strong>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun Route.getAllBooksRoute(bookService: BookService) {
  get {
    val books = bookService.findAllBooks()
      .map(Book::toBookResponse)

    call.respond(message = books)
  }
}</pre>



<p>This one will be triggered on <em>GET /book</em> requests returning the list of books.</p>



<h3 class="wp-block-heading" id="h-9-5-add-get-book-by-id-route">9.5. Add Get Book By Id Route</h3>



<p>With that being done, let&#8217;s add the <strong>getBookByIdRoute()</strong> responsible for handling GET /book/{id} requests:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun Route.getBookByIdRoute(bookService: BookService) {
  get("/{id}") {
    val id: Long = call.parameters["id"]?.toLongOrNull()
      ?: return@get call.respond(HttpStatusCode.BadRequest, ErrorResponse("Invalid id"))

    bookService.findBookById(id)
      ?.let { foundBook -> foundBook.toBookResponse() }
      ?.let { response -> call.respond(response) }
      ?: return@get call.respond(HttpStatusCode.BadRequest, ErrorResponse("Book with id [$id] not found"))
  }
}</pre>



<p>This time, let&#8217;s focus on the <strong>call.parameters[&#8220;id&#8221;]</strong>. The Parameters property of the call is simply a map of case-insensitive names to a collection of String values. That&#8217;s why we have to use a safe <strong>?.toLongOrNull()</strong> to obtain the value correctly. If the id is invalid, then the <strong>400 Bad Request</strong> is returned.</p>



<p>The rest of the code is pretty straightforward- either the found book is mapped to the <strong>BookResponse</strong> instance and returned to the client, or a <strong>400 Bad Reques</strong>t informing that the book with a given id was not found.</p>



<h3 class="wp-block-heading" id="h-9-6-insert-update-and-delete-routes">9.6. Insert Update and Delete Routes</h3>



<p>Finally, let&#8217;s add the two routes responsible for handling <em>PATCH /book/{id}</em> and <em>DELETE /book/{id}</em> requests:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun Route.updateBookByIdRoute(bookService: BookService) {
  patch("/{id}") {
    val id: Long = call.parameters["id"]?.toLongOrNull()
      ?: return@patch call.respond(HttpStatusCode.BadRequest, ErrorResponse("Invalid id"))

    val request = call.receive&lt;BookRequest>()
    val success = bookService.updateBookById(id, request)

    if (success)
      call.respond(HttpStatusCode.NoContent)
    else
      call.respond(HttpStatusCode.BadRequest, ErrorResponse("Cannot update book with id [$id]"))
  }
}

fun Route.deleteBookByIdRoute(bookService: BookService) {
  delete("/{id}") {
    val id: Long = call.parameters["id"]?.toLongOrNull()
      ?: return@delete call.respond(HttpStatusCode.BadRequest, ErrorResponse("Invalid id"))

    val success = bookService.deleteBookById(id)

    if (success)
      call.respond(HttpStatusCode.NoContent)
    else
      call.respond(HttpStatusCode.BadRequest, ErrorResponse("Cannot delete book with id [$id]"))
  }
}</pre>



<p>As we can see, these functions use already described concepts, so we won&#8217;t go into much detail.</p>



<h2 class="wp-block-heading" id="h-10-test-with-curl">10. Test With cURL</h2>



<p>Finally, let&#8217;s run the application and test it manually. For simplicity, we will use the <strong>cURL</strong> command. Alternatively, we could do that through Postman, or Insomnia.</p>



<h3 class="wp-block-heading" id="h-10-1-get-book">10.1. GET /book</h3>



<p>Let&#8217;s start by listing all books to validate both the endpoint and SQL init script:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl localhost:8080/book

# Output
[
  {
    "id":1,
    "name": "Book #1"
  },
  {
    "id":2,
    "name":"Book #2"
  }
]</pre>



<p>As we can see, everything is working correctly.</p>



<h3 class="wp-block-heading" id="h-10-2-post-book">10.2. POST /book</h3>



<p>As the second step, let&#8217;s create a new book:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl -X POST 'localhost:8080/book' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "name": "Book #3"
  }'

# Then again: 
curl localhost:8080/book

# Output
[
  {
    "id":1,
    "name": "Book #1"
  },
  {
    "id":2,
    "name":"Book #2"
  },
  {
    "id":3,
    "name":"Book #3"
  }
]</pre>



<h3 class="wp-block-heading" id="h-10-3-get-book-id">10.3. GET /book/{id}</h3>



<p>As the third step, let&#8217;s check whether fetching book details works:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl localhost:8080/book/3

# Output:
{
  "id":3,
  "name":"Book #3"
}

curl localhost:8080/book/1234

# Output:
{
  "message":"Book with id [1234] not found"}
}</pre>



<h3 class="wp-block-heading" id="h-10-4-patch-book-id">10.4. PATCH /book/{id}</h3>



<p>After that, let&#8217;s validate the PATCH endpoint:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl -X PATCH 'localhost:8080/book/3' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "name": "Book #3- edited"
  }'

# Then, let's check with GET:
curl localhost:8080/book/3

# Output:
{
  "id": 3,
  "name": "Book #3- edited"
}</pre>



<h3 class="wp-block-heading" id="h-10-5-delete-book-id">10.5. DELETE /book/{id}</h3>



<p>Finally, let&#8217;s delete created book:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl -X DELETE 'localhost:8080/book/3'

# Then, let's check with GET:
curl localhost:8080/book/3

# Output:
{
  "message":"Book with id [3] not found"
}</pre>



<h2 class="wp-block-heading" id="h-11-ktor-with-ktorm-and-postgresql-summary">11. Ktor With Ktorm And PostgreSQL Summary</h2>



<p>And that would be all for this step-by-step guide on <strong>how to create REST API with Ktor, Ktorm, and PostgreSQL</strong>. I really hope you will find this material useful when working with <strong>Ktor</strong>. If you would like to see the source code, please refer to <a href="https://github.com/codersee-blog/ktor-ktorm-postgresql" target="_blank" rel="noopener">this GitHub repository</a>.</p>



<p>If you find this one useful, then you may enjoy my newer article about <a href="https://blog.codersee.com/a-guide-to-ktor-with-mongodb/">Ktor and MongoDB</a>.</p>



<p>As always- if you would like to ask me about anything, please do that in the comments section below, or through the <a href="https://codersee.com/contact/" target="_blank" rel="noopener">contact form</a>.</p>



<h4 class="wp-block-heading" id="h-previous-articles-you-might-be-interested-in">Previous articles, you might be interested in:</h4>



<ul class="wp-block-list">
<li><a href="https://blog.codersee.com/exception-handling-with-restcontrolleradvice-and-exceptionhandler/" target="_blank" rel="noopener">Exception Handling with @RestControllerAdvice and @ExceptionHandler</a></li>



<li><a href="https://blog.codersee.com/16-intellij-shortcuts-that-will-boost-your-productivity/" target="_blank" rel="noopener">16 IntelliJ Shortcuts That Will Boost Your Productivity</a></li>



<li><a href="https://blog.codersee.com/solid-principles-with-kotlin-examples/" target="_blank" rel="noopener">SOLID Principles with Kotlin Examples</a></li>
</ul>
<p>The post <a href="https://blog.codersee.com/rest-api-ktor-ktorm-postgresql/">REST API With Ktor, Ktorm and PostgreSQL</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/rest-api-ktor-ktorm-postgresql/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 

Served from: blog.codersee.com @ 2026-05-15 14:03:35 by W3 Total Cache
-->