<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Databases on Soldier's 5</title><link>https://4lex.nz/tags/databases/</link><description>Recent content in Databases on Soldier's 5</description><image><title>Soldier's 5</title><url>https://4lex.nz/img/404-bg.jpg</url><link>https://4lex.nz/img/404-bg.jpg</link></image><generator>Hugo</generator><language>en-us</language><lastBuildDate>Sun, 11 Oct 2020 00:00:00 +0000</lastBuildDate><atom:link href="https://4lex.nz/tags/databases/index.xml" rel="self" type="application/rss+xml"/><item><title>SQL Joins with Sequelize</title><link>https://4lex.nz/posts/sql-joins-with-sequelize/</link><pubDate>Sun, 11 Oct 2020 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/sql-joins-with-sequelize/</guid><description>&lt;p&gt;While working on an API for my day job last week, I needed to do a SQL Inner Join with Sequelize and Typescript in a
web API. Here&amp;rsquo;s how I achieved it.&lt;/p&gt;
&lt;h4 id="environment"&gt;Environment:&lt;/h4&gt;
&lt;p&gt;Things move quickly in the JS ecosystem. Here are the library versions used in the subject API at the time of writing:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;dependencies&amp;#34;&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;@types/express&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;~4.0.39&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;@types/sequelize&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;^4.27.21&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;express&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;~4.16.2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;sequelize&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;4.38.0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="context"&gt;Context:&lt;/h4&gt;
&lt;p&gt;I have an identifier in one system that I need to correlate with an identifier in a second system, but I
could only do that via an intermediate identifier, also in the second system. A good case for a SQL Join.&lt;/p&gt;</description><content:encoded><![CDATA[<p>While working on an API for my day job last week, I needed to do a SQL Inner Join with Sequelize and Typescript in a
web API. Here&rsquo;s how I achieved it.</p>
<h4 id="environment">Environment:</h4>
<p>Things move quickly in the JS ecosystem. Here are the library versions used in the subject API at the time of writing:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-json" data-lang="json"><span style="display:flex;"><span><span style="color:#e6db74">&#34;dependencies&#34;</span><span style="color:#960050;background-color:#1e0010">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;@types/express&#34;</span>: <span style="color:#e6db74">&#34;~4.0.39&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;@types/sequelize&#34;</span>: <span style="color:#e6db74">&#34;^4.27.21&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;express&#34;</span>: <span style="color:#e6db74">&#34;~4.16.2&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;sequelize&#34;</span>: <span style="color:#e6db74">&#34;4.38.0&#34;</span>,
</span></span><span style="display:flex;"><span>  }
</span></span></code></pre></div><h4 id="context">Context:</h4>
<p>I have an identifier in one system that I need to correlate with an identifier in a second system, but I
could only do that via an intermediate identifier, also in the second system. A good case for a SQL Join.</p>
<table>
  <thead>
      <tr>
          <th>System 1</th>
          <th>System 2</th>
          <th>System 2</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Electronic Identifier</td>
          <td>Animal Identifier</td>
          <td>Legacy Animal Identifier</td>
      </tr>
      <tr>
          <td>ABCD</td>
          <td>555 WXYZ</td>
          <td>1234</td>
      </tr>
  </tbody>
</table>
<p>In System 1, I have enough information to send this request:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;items&#34;</span>: [
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;ABC&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;DEF&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;GHI&#34;</span>
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>And in response I want:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;links&#34;</span>: [
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;rel&#34;</span>: <span style="color:#e6db74">&#34;self&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;href&#34;</span>: <span style="color:#e6db74">&#34;https://api.com/123-i-am-a-uuid&#34;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    ],
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;reference&#34;</span>: <span style="color:#e6db74">&#34;123-i-am-a-uuid&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;items&#34;</span>: [
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;legacyId&#34;</span>: <span style="color:#ae81ff">123</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;electronicId&#34;</span>: <span style="color:#e6db74">&#34;ABC&#34;</span>
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;legacyId&#34;</span>: <span style="color:#ae81ff">456</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;electronicId&#34;</span>: <span style="color:#e6db74">&#34;DEF&#34;</span>
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;legacyId&#34;</span>: <span style="color:#ae81ff">789</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;electronicId&#34;</span>: <span style="color:#e6db74">&#34;GHI&#34;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The initial approach was to make three repository queries to get the data I needed, and then go through three
javascript map operations to splice the data into the above format. Using a series of javascript maps is inefficient, particularly as the data set grows.</p>
<p>A better way is to get SQL to do the grunt work, as it&rsquo;s far more time and memory efficient than the javascript I would have needed to write.</p>
<blockquote>
<p>As an aside, I&rsquo;m not too fond of Javascript on the back end. I don&rsquo;t feel it&rsquo;s performant enough and as your codebase
grows the lack of a robust type system hurts more and more. Typescript helps address the latter (assuming your libraries support it) but does nothing for the former.
A typed language like C# or Java is preferable for web programming in a large enterprise due to better performance, more coherent ecosystem (see: <code>left-pad</code>) and robust type system.</p>
</blockquote>
<p>I need to make a map of identifiers from an <code>electronicIdEvent</code> entity
(that deals with electronic identifiers) and an <code>animal</code> entity that deals with animal information.</p>
<table>
  <thead>
      <tr>
          <th></th>
          <th>electronicId</th>
          <th>animalId</th>
          <th>legacyAnimalId</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>electronicIdEvent</td>
          <td>✓</td>
          <td>✓</td>
          <td></td>
      </tr>
      <tr>
          <td>animal</td>
          <td></td>
          <td>✓</td>
          <td>✓</td>
      </tr>
  </tbody>
</table>
<p>I need the <code>legacyAnimalId</code> field. To find a <code>legacyAnimalId</code> given an <code>electronicIdEvent</code>, I have to:</p>
<ol>
<li>Find all <code>electronicIdEvent</code> rows that match an <code>electronicId</code>.</li>
<li>Find all <code>animal</code> rows that match the <code>animalId</code> of the <code>electronicIdEvent</code> where an <code>electronicId</code> is present.</li>
<li>Return all <code>legacyAnimalId</code> rows that match the <code>animalId</code> of both the <code>animal</code> and the <code>electronicIdEvent</code>.</li>
</ol>
<p>Hopefully, you can see why using javascript maps, and SQL selects for this would be tedious and inefficient.</p>
<p>Here&rsquo;s a SQL query that demonstrates what I want if you&rsquo;re more familiar with SQL then Javascript:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">select</span> event.eid, event.animalId, animal.id, animal.animalKey
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">from</span> <span style="color:#e6db74">&#34;electronicIdEvent&#34;</span> event
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">inner</span> <span style="color:#66d9ef">join</span> <span style="color:#e6db74">&#34;animal&#34;</span> animal
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">on</span> event.<span style="color:#e6db74">&#34;animalId&#34;</span> <span style="color:#f92672">=</span> animal.id
</span></span></code></pre></div><h3 id="1-create-associations-for-sequzelize-between-the-data-you-need-to-link">1. Create associations for Sequzelize, between the data you need to link.</h3>
<p>For Sequelize to generate the right SQL, it needs to know about the relationships between your data.</p>
<p>Here&rsquo;s the code that creates associations in Sequelize:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">animal</span>.<span style="color:#a6e22e">hasMany</span>(<span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">electronicIdEvent</span>, {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">foreignKeyConstraint</span>: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">foreignKey</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;animalId&#34;</span>,
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">electronicIdEvent</span>.<span style="color:#a6e22e">belongsTo</span>(<span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">animal</span>, {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">foreignKeyConstraint</span>: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">foreignKey</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;animalId&#34;</span>,
</span></span><span style="display:flex;"><span>});
</span></span></code></pre></div><p>Associations allow the ORM to understand your relations and map <code>One To One</code>, <code>One To Many</code> and <code>Many To Many</code> relationships to appropriate SQL code. The docs describe this <a href="https://sequelize.org/master/manual/assocs.html" target="_blank" rel="noopener noreferrer">here</a>

</p>
<h3 id="2-create-a-sequelize-repository-method-that-uses-this-association-to-form-a-sql-query-that-returns-the-correct-data">2. Create a Sequelize repository method that uses this association to form a SQL query that returns the correct data:</h3>
<p>There is some stuff in here that&rsquo;s not great, but it works well enough. If I had more time, I&rsquo;d figure out
a way of using typescript&rsquo;s not-null override (<code>!</code>), and ideally find a way of making <code>event</code> a typed response.</p>
<p>The pseudocode I want is:</p>
<ul>
<li>First return all the rows for the <code>electronicId</code> and <code>animalId</code> columns in <code>electronicIdEvent</code>, where the <code>electronicId</code> is present in my <code>electronicIdList</code></li>
<li>Next join the rows for those columns with the rows for the <code>legacyAnimalId</code> column, where the <code>animalId</code> is the same in both the <code>electronicIdEvent</code> and <code>animal</code> entities.</li>
<li>When the data comes back, drop the <code>animalId</code> column. I don&rsquo;t need it once it has done the job of being the glue between the other entities and columns.</li>
</ul>
<p>Or, as Javascript:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#75715e">// imports omitted for brevity
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">AnimalRepository</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">constructor</span>(
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> <span style="color:#a6e22e">db</span>: <span style="color:#66d9ef">AnimalTimelineDbContext</span>,
</span></span><span style="display:flex;"><span>  ) {}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">getLegacyAnimalIdentifierByElectronicIdentifier</span>(<span style="color:#a6e22e">electronicIdList</span>: <span style="color:#66d9ef">Array</span>&lt;<span style="color:#f92672">string</span>&gt;)<span style="color:#f92672">:</span> <span style="color:#a6e22e">Promise</span>&lt;<span style="color:#f92672">Array</span>&lt;<span style="color:#f92672">IdentifierMap</span>&gt;&gt; {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">db</span>.<span style="color:#a6e22e">electronicIdEvent</span>
</span></span><span style="display:flex;"><span>      .<span style="color:#a6e22e">findAll</span>({
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">attributes</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#34;electronicId&#34;</span>, <span style="color:#e6db74">&#34;animalId&#34;</span>],
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">where</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>          <span style="color:#a6e22e">electronicId</span>: <span style="color:#66d9ef">electronicIdList</span>,
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">include</span><span style="color:#f92672">:</span> [ 
</span></span><span style="display:flex;"><span>          {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">model</span>: <span style="color:#66d9ef">this.db.animal</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">required</span>: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">attributes</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#34;legacyAnimalId&#34;</span>],
</span></span><span style="display:flex;"><span>          },
</span></span><span style="display:flex;"><span>        ],
</span></span><span style="display:flex;"><span>      })
</span></span><span style="display:flex;"><span>      .<span style="color:#a6e22e">then</span>((<span style="color:#a6e22e">events</span>) <span style="color:#f92672">=&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">events</span>.<span style="color:#a6e22e">map</span>((<span style="color:#a6e22e">event</span>) <span style="color:#f92672">=&gt;</span> ({
</span></span><span style="display:flex;"><span>          <span style="color:#a6e22e">eid</span>: <span style="color:#66d9ef">event.electronicId</span>,
</span></span><span style="display:flex;"><span>          <span style="color:#a6e22e">legacyAnimalId</span>: <span style="color:#66d9ef">event.Animal</span><span style="color:#f92672">!</span>.<span style="color:#a6e22e">legacyAnimalId</span>,
</span></span><span style="display:flex;"><span>        }))
</span></span><span style="display:flex;"><span>      );
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><blockquote>
<p>Note: Setting <code>required: true</code> in the <code>include</code> block forces the query to only return records which have an associated model, this converts the query from an OUTER JOIN to an INNER JOIN.</p>
</blockquote>
<blockquote>
<p>Because we only return complete rows (no partials) <code>event.Animal</code> cannot be null, I can override the typescript hint, i.e. <code>event.Animal!.legacyAnimalId</code>.</p>
</blockquote>
<p>I&rsquo;m done at this point. We&rsquo;ve submitted a list of electronic identifiers and gotten back an <code>Array&lt;IdentifierMap&gt;</code>, which is syntactic sugar for <code>Array&lt;[string, integer]&gt;</code>.</p>
<h3 id="3-convert-the-raw-data-into-my-map-object-and-fire-the-response-back">3. Convert the raw data into my map object, and fire the response back.</h3>
<p>Because Sequelize is doing most of the grunt work in formatting the data, all I need in my web layer is to add the UUID
that the user-submitted and save that map. Once I&rsquo;ve saved the map and returned the reference to the calling system,
the calling system can ask for that map and get the data (this is the most RESTful way of handling the data exchange
and I&rsquo;m omitting that setup for a follow-up post).</p>
]]></content:encoded></item><item><title>Spring Boot - Comparing Liquibase and Flyway</title><link>https://4lex.nz/posts/spring-boot-database-migrations/</link><pubDate>Thu, 23 Apr 2020 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/spring-boot-database-migrations/</guid><description>&lt;h3 id="what-are-spring-boot-liquibase-and-flyway"&gt;What are Spring Boot, Liquibase and Flyway?&lt;/h3&gt;
&lt;h4 id="the-spring-ecosystem"&gt;The Spring Ecosystem&lt;/h4&gt;
&lt;p&gt;When you need to create a web application or an API in Java, be that RESTful, SOAP or GraphQL; whether you&amp;rsquo;re looking at synchronous HTTP, Asynchronous or Reactive, Messages on Queues or Event Sourced with Kafka, it&amp;rsquo;s hard to go past the Spring Ecosystem. If nobody ever got fired for buying IBM, you could probably say that nobody ever got fired for creating a Spring-based application.&lt;/p&gt;</description><content:encoded><![CDATA[<h3 id="what-are-spring-boot-liquibase-and-flyway">What are Spring Boot, Liquibase and Flyway?</h3>
<h4 id="the-spring-ecosystem">The Spring Ecosystem</h4>
<p>When you need to create a web application or an API in Java, be that RESTful, SOAP or GraphQL; whether you&rsquo;re looking at synchronous HTTP, Asynchronous or Reactive, Messages on Queues or Event Sourced with Kafka, it&rsquo;s hard to go past the Spring Ecosystem. If nobody ever got fired for buying IBM, you could probably say that nobody ever got fired for creating a Spring-based application.</p>
<p>Spring is an extremely popular, well maintained and well known Java Web Application Framework. Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can &ldquo;just run&rdquo;.</p>
<p>Spring Boot is to Spring and Java as Ruby on Rails is to Ruby. So if you&rsquo;re working in a Java environment, there&rsquo;s a pretty good chance you&rsquo;re using Spring or Spring Boot. Even if you&rsquo;re using another framework, it&rsquo;s worth learning about these - they&rsquo;re so ubiquitous they&rsquo;ll be around for a long time!</p>
<h4 id="database-migrations">Database Migrations</h4>
<p>If you&rsquo;re using Spring, there&rsquo;s a chance you&rsquo;re using persistence tech like <a href="https://hibernate.org/" target="_blank" rel="noopener noreferrer">Hibernate</a>

, <a href="https://www.jooq.org/" target="_blank" rel="noopener noreferrer">Jooq</a>

 or <a href="https://ebean.io/" target="_blank" rel="noopener noreferrer">Ebean</a>

 to get data out of your database.</p>
<p>Features come and go; data models change; what happens when you need to change your data model? You run a database migration to add or remove columns or make other changes.</p>
<p>It used to be that this would happen manually; someone would log on to a database, run a bunch of SQL and rely on the fact that you&rsquo;ve made the same changes in your application. There is a bunch of room for human error here, though. So we improve on this by trying to make our database changes safer. Version control, repeatability, and testability become applicable to more parts of the stack. Databases included.</p>
<p>Both Liquibase and Flyway help us with these migrations. Spring helps us with Liquibase and Flyway in turn. The result is a database changeset that does what we have specified every single time (of course, we might still specify the wrong thing - computers remain computers). All we need to do is write a migration and dump it into a folder in our Spring project to achieve that result. Spring handles the rest.</p>
<h4 id="liquibase">Liquibase</h4>
<p>Liquibase is offered free and paid. They don&rsquo;t share pricing on their website, though, which makes me suspicious. They don&rsquo;t do an excellent job of describing why I would want to pay either. Liquibase offers migrations both through XML and SQL. The basic concept is that you have a master file that describes your database configuration and the changesets you want to include in your run. Spring comes in at that point, parses your config and your included changesets, and manages them appropriately.</p>
<h4 id="flyway">Flyway</h4>
<p>Flyway is offered free and paid, the same as liquibase. The pricing and feature breakdown is available <a href="https://flywaydb.org/download/" target="_blank" rel="noopener noreferrer">here</a>

 - and is a lot more detailed than anything I could find on liquibase.</p>
<p>Flyway focuses on your migrations as a first-class concept. You write SQL scripts, place them in a folder in your Spring project, add some config to your <code>application.yml</code> files and then Spring runs the migrations as per your config.</p>
<h4 id="links">Links:</h4>
<ul>
<li><a href="https://spring.io/projects/spring-boot" target="_blank" rel="noopener noreferrer">Spring Boot</a>

</li>
<li><a href="https://www.liquibase.org/get_started/index.html" target="_blank" rel="noopener noreferrer">Liquibase</a>


<ul>
<li><a href="https://www.liquibase.org/documentation/spring.html" target="_blank" rel="noopener noreferrer">Using Liquibase with Spring Boot</a>

</li>
</ul>
</li>
<li><a href="https://flywaydb.org/getstarted/" target="_blank" rel="noopener noreferrer">Flyway</a>


<ul>
<li><a href="https://flywaydb.org/documentation/plugins/springboot" target="_blank" rel="noopener noreferrer">Using Flyway with Spring Boot</a>

</li>
</ul>
</li>
</ul>
<h3 id="using-liquibase">Using Liquibase</h3>
<p>Note: I&rsquo;m using Spring Boot 2.2.6 with Gradle here, but you can do the equivalent with a dependency block in Maven.</p>
<p>Step One is adding your dependencies to <code>build.gradle</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span>    <span style="color:#75715e">// Persistence
</span></span></span><span style="display:flex;"><span>    implementation<span style="color:#f92672">(</span><span style="color:#e6db74">&#39;com.h2database:h2&#39;</span><span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>    implementation <span style="color:#e6db74">&#34;org.liquibase:liquibase-core&#34;</span>
</span></span><span style="display:flex;"><span>    liquibaseRuntime <span style="color:#e6db74">&#34;org.liquibase:liquibase-core&#34;</span>
</span></span><span style="display:flex;"><span>    liquibaseRuntime sourceSets<span style="color:#f92672">.</span><span style="color:#a6e22e">main</span><span style="color:#f92672">.</span><span style="color:#a6e22e">compileClasspath</span>
</span></span><span style="display:flex;"><span>    liquibaseRuntime <span style="color:#e6db74">&#34;org.postgresql:postgresql&#34;</span>
</span></span><span style="display:flex;"><span>    liquibaseRuntime <span style="color:#e6db74">&#34;com.h2database:h2&#34;</span>
</span></span><span style="display:flex;"><span>    implementation<span style="color:#f92672">(</span><span style="color:#e6db74">&#39;org.postgresql:postgresql&#39;</span><span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>    implementation<span style="color:#f92672">(</span><span style="color:#e6db74">&#39;org.springframework.boot:spring-boot-starter-jdbc&#39;</span><span style="color:#f92672">)</span>
</span></span></code></pre></div><p>Next we need to make new folders in our project:</p>
<ul>
<li>In <code>src/main/resources</code> add a <code>liquibase</code> folder</li>
<li>Add two subfolders, <code>changelog</code> and <code>fake-data</code></li>
</ul>
<p>Add some minimal configuration to your <code>application.yml</code> file and you&rsquo;re good to go.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>  <span style="color:#f92672">liquibase</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">contexts</span>: <span style="color:#ae81ff">dev, faker</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">change-log</span>: <span style="color:#ae81ff">classpath:liquibase/master.xml</span>
</span></span></code></pre></div><p>At this point, you should be able to start writing some XML.</p>
<p>You&rsquo;ll need a <code>master.xml</code> that describes your environment setup and the migrations that you want to run. Here&rsquo;s an example:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#75715e">&lt;?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34;?&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">&lt;databaseChangeLog</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">xmlns:xsi=</span><span style="color:#e6db74">&#34;http://www.w3.org/2001/XMLSchema-instance&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">xmlns=</span><span style="color:#e6db74">&#34;http://www.liquibase.org/xml/ns/dbchangelog&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">xsi:schemaLocation=</span><span style="color:#e6db74">&#34;http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;property</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;now&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;now()&#34;</span> <span style="color:#a6e22e">dbms=</span><span style="color:#e6db74">&#34;h2&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;property</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;now&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;current_timestamp&#34;</span> <span style="color:#a6e22e">dbms=</span><span style="color:#e6db74">&#34;postgresql&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;property</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;floatType&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;float4&#34;</span> <span style="color:#a6e22e">dbms=</span><span style="color:#e6db74">&#34;postgresql, h2&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;property</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;clobType&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;longvarchar&#34;</span> <span style="color:#a6e22e">dbms=</span><span style="color:#e6db74">&#34;h2&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;property</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;clobType&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;clob&#34;</span> <span style="color:#a6e22e">dbms=</span><span style="color:#e6db74">&#34;postgresql&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;property</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;uuidType&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;uuid&#34;</span> <span style="color:#a6e22e">dbms=</span><span style="color:#e6db74">&#34;h2, postgresql&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;changeSet</span> <span style="color:#a6e22e">id=</span><span style="color:#e6db74">&#34;00000000000000&#34;</span> <span style="color:#a6e22e">author=</span><span style="color:#e6db74">&#34;alex&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;createSequence</span> <span style="color:#a6e22e">sequenceName=</span><span style="color:#e6db74">&#34;sequence_generator&#34;</span> <span style="color:#a6e22e">startValue=</span><span style="color:#e6db74">&#34;1050&#34;</span> <span style="color:#a6e22e">incrementBy=</span><span style="color:#e6db74">&#34;1&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;/changeSet&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;include</span> <span style="color:#a6e22e">file=</span><span style="color:#e6db74">&#34;liquibase/changelog/20191024203226_added_entity_Company.xml&#34;</span> <span style="color:#a6e22e">relativeToChangelogFile=</span><span style="color:#e6db74">&#34;false&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;include</span> <span style="color:#a6e22e">file=</span><span style="color:#e6db74">&#34;liquibase/changelog/20191024203227_added_entity_Team.xml&#34;</span> <span style="color:#a6e22e">relativeToChangelogFile=</span><span style="color:#e6db74">&#34;false&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;include</span> <span style="color:#a6e22e">file=</span><span style="color:#e6db74">&#34;liquibase/changelog/20191024203234_added_entity_Project.xml&#34;</span> <span style="color:#a6e22e">relativeToChangelogFile=</span><span style="color:#e6db74">&#34;false&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">&lt;/databaseChangeLog&gt;</span>
</span></span></code></pre></div><p>It looks a bit busy but let&rsquo;s break it down. First, we need to tell Liquibase about our database setup, and what values it should use for various data types:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span>    <span style="color:#f92672">&lt;property</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;now&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;now()&#34;</span> <span style="color:#a6e22e">dbms=</span><span style="color:#e6db74">&#34;h2&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;property</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;now&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;current_timestamp&#34;</span> <span style="color:#a6e22e">dbms=</span><span style="color:#e6db74">&#34;postgresql&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;property</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;floatType&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;float4&#34;</span> <span style="color:#a6e22e">dbms=</span><span style="color:#e6db74">&#34;postgresql, h2&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;property</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;clobType&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;longvarchar&#34;</span> <span style="color:#a6e22e">dbms=</span><span style="color:#e6db74">&#34;h2&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;property</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;clobType&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;clob&#34;</span> <span style="color:#a6e22e">dbms=</span><span style="color:#e6db74">&#34;postgresql&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;property</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;uuidType&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;uuid&#34;</span> <span style="color:#a6e22e">dbms=</span><span style="color:#e6db74">&#34;h2, postgresql&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span></code></pre></div><p>Next, we need to describe the changes we want Liquibase to make; in our case, we want Liquibase to create a sequence and run three migrations. Note that if you write migrations but don&rsquo;t include them in <code>master.xml</code>, they won&rsquo;t run.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span>    <span style="color:#f92672">&lt;changeSet</span> <span style="color:#a6e22e">id=</span><span style="color:#e6db74">&#34;00000000000000&#34;</span> <span style="color:#a6e22e">author=</span><span style="color:#e6db74">&#34;alex&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;createSequence</span> <span style="color:#a6e22e">sequenceName=</span><span style="color:#e6db74">&#34;sequence_generator&#34;</span> <span style="color:#a6e22e">startValue=</span><span style="color:#e6db74">&#34;1050&#34;</span> <span style="color:#a6e22e">incrementBy=</span><span style="color:#e6db74">&#34;1&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;/changeSet&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;include</span> <span style="color:#a6e22e">file=</span><span style="color:#e6db74">&#34;liquibase/changelog/20191024203226_added_entity_Company.xml&#34;</span> <span style="color:#a6e22e">relativeToChangelogFile=</span><span style="color:#e6db74">&#34;false&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;include</span> <span style="color:#a6e22e">file=</span><span style="color:#e6db74">&#34;liquibase/changelog/20191024203227_added_entity_Team.xml&#34;</span> <span style="color:#a6e22e">relativeToChangelogFile=</span><span style="color:#e6db74">&#34;false&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;include</span> <span style="color:#a6e22e">file=</span><span style="color:#e6db74">&#34;liquibase/changelog/20191024203234_added_entity_Project.xml&#34;</span> <span style="color:#a6e22e">relativeToChangelogFile=</span><span style="color:#e6db74">&#34;false&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span></code></pre></div><p>Here&rsquo;s the company migration:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#75715e">&lt;?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34;?&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">&lt;databaseChangeLog</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">xmlns:xsi=</span><span style="color:#e6db74">&#34;http://www.w3.org/2001/XMLSchema-instance&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">xmlns=</span><span style="color:#e6db74">&#34;http://www.liquibase.org/xml/ns/dbchangelog&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">xsi:schemaLocation=</span><span style="color:#e6db74">&#34;http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;changeSet</span> <span style="color:#a6e22e">id=</span><span style="color:#e6db74">&#34;20191024203226-1&#34;</span> <span style="color:#a6e22e">author=</span><span style="color:#e6db74">&#34;alex&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;createTable</span> <span style="color:#a6e22e">tableName=</span><span style="color:#e6db74">&#34;company&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;column</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;id&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;bigint&#34;</span> <span style="color:#a6e22e">autoIncrement=</span><span style="color:#e6db74">&#34;${autoIncrement}&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">&lt;constraints</span> <span style="color:#a6e22e">primaryKey=</span><span style="color:#e6db74">&#34;true&#34;</span> <span style="color:#a6e22e">nullable=</span><span style="color:#e6db74">&#34;false&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;/column&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;column</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;name&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;varchar(255)&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">&lt;constraints</span> <span style="color:#a6e22e">nullable=</span><span style="color:#e6db74">&#34;false&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;/column&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;column</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;web_page_address&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;varchar(255)&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">&lt;constraints</span> <span style="color:#a6e22e">nullable=</span><span style="color:#e6db74">&#34;true&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;/column&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;column</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;billing_contact_email_address&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;varchar(255)&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">&lt;constraints</span> <span style="color:#a6e22e">nullable=</span><span style="color:#e6db74">&#34;true&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;/column&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;column</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;primary_contact_email_address&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;varchar(255)&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">&lt;constraints</span> <span style="color:#a6e22e">nullable=</span><span style="color:#e6db74">&#34;true&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;/column&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;/createTable&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;/changeSet&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;changeSet</span> <span style="color:#a6e22e">id=</span><span style="color:#e6db74">&#34;20191024203226-1-data&#34;</span> <span style="color:#a6e22e">author=</span><span style="color:#e6db74">&#34;alex&#34;</span> <span style="color:#a6e22e">context=</span><span style="color:#e6db74">&#34;faker&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;loadData</span>
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">file=</span><span style="color:#e6db74">&#34;liquibase/fake-data/company.csv&#34;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">separator=</span><span style="color:#e6db74">&#34;;&#34;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">tableName=</span><span style="color:#e6db74">&#34;company&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;column</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;id&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;numeric&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;column</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;name&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;string&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;column</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;web_page_address&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;string&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;column</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;billing_contact_email_address&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;string&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;column</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;primary_contact_email_address&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;string&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;/loadData&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;/changeSet&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">&lt;/databaseChangeLog&gt;</span>
</span></span></code></pre></div><p>The first thing this migration creates is a table called Company. Since Liquibase uses XML as an abstraction on top of SQL, we don&rsquo;t need to worry too much about compatibility, Liquibase will look at the XML file and translate our request to the appropriate SQL dialect.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span>    <span style="color:#f92672">&lt;changeSet</span> <span style="color:#a6e22e">id=</span><span style="color:#e6db74">&#34;20191024203226-1&#34;</span> <span style="color:#a6e22e">author=</span><span style="color:#e6db74">&#34;alex&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;createTable</span> <span style="color:#a6e22e">tableName=</span><span style="color:#e6db74">&#34;company&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;column</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;id&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;bigint&#34;</span> <span style="color:#a6e22e">autoIncrement=</span><span style="color:#e6db74">&#34;${autoIncrement}&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">&lt;constraints</span> <span style="color:#a6e22e">primaryKey=</span><span style="color:#e6db74">&#34;true&#34;</span> <span style="color:#a6e22e">nullable=</span><span style="color:#e6db74">&#34;false&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;/column&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;column</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;name&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;varchar(255)&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">&lt;constraints</span> <span style="color:#a6e22e">nullable=</span><span style="color:#e6db74">&#34;false&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;/column&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;column</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;web_page_address&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;varchar(255)&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">&lt;constraints</span> <span style="color:#a6e22e">nullable=</span><span style="color:#e6db74">&#34;true&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;/column&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;column</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;billing_contact_email_address&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;varchar(255)&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">&lt;constraints</span> <span style="color:#a6e22e">nullable=</span><span style="color:#e6db74">&#34;true&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;/column&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;column</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;primary_contact_email_address&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;varchar(255)&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">&lt;constraints</span> <span style="color:#a6e22e">nullable=</span><span style="color:#e6db74">&#34;true&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;/column&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;/createTable&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;/changeSet&gt;</span>
</span></span></code></pre></div><p>Having defined our table, we can take advantage of one of the neat functions Liquibase can give us - Liquibase will load data into our database from CSV:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span>    <span style="color:#f92672">&lt;changeSet</span> <span style="color:#a6e22e">id=</span><span style="color:#e6db74">&#34;20191024203226-1-data&#34;</span> <span style="color:#a6e22e">author=</span><span style="color:#e6db74">&#34;alex&#34;</span> <span style="color:#a6e22e">context=</span><span style="color:#e6db74">&#34;faker&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;loadData</span>
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">file=</span><span style="color:#e6db74">&#34;liquibase/fake-data/company.csv&#34;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">separator=</span><span style="color:#e6db74">&#34;;&#34;</span>
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">tableName=</span><span style="color:#e6db74">&#34;company&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;column</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;id&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;numeric&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;column</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;name&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;string&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;column</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;web_page_address&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;string&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;column</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;billing_contact_email_address&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;string&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;column</span> <span style="color:#a6e22e">name=</span><span style="color:#e6db74">&#34;primary_contact_email_address&#34;</span> <span style="color:#a6e22e">type=</span><span style="color:#e6db74">&#34;string&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;/loadData&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;/changeSet&gt;</span>
</span></span></code></pre></div><p>The loading of data is a changeset in its own right; this means you can flexibly load data when you create the table, or at a later stage. Loading data in this way can be useful if you&rsquo;ve got complex relationships to model. Another trick here is the <code>context=&quot;faker&quot;</code> statement above. If you want data to be loaded, make sure your <code>application.yml</code>&rsquo;s <code>liquibase</code> entry contains that tag. If you remove that tag, the data won&rsquo;t be loaded.</p>
<p>Here&rsquo;s the CSV referenced above with some sample data in it:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-csv" data-lang="csv"><span style="display:flex;"><span><span style="color:#e6db74">id</span>,<span style="color:#e6db74">name</span>,<span style="color:#e6db74">web_page_address</span>,<span style="color:#e6db74">billing_contact_email_address</span>,<span style="color:#e6db74">primary_contact_email_address</span>
</span></span><span style="display:flex;"><span><span style="color:#e6db74">1</span>,<span style="color:#e6db74">mobileFish</span>,<span style="color:#e6db74">HomeLoanAccountTableComputer</span>,<span style="color:#e6db74">Trace</span>,<span style="color:#e6db74">SavingsAccountalarm</span>
</span></span><span style="display:flex;"><span><span style="color:#e6db74">2</span>,<span style="color:#e6db74">TastyMetalBacon</span>,<span style="color:#e6db74">redChipsSoap</span>,<span style="color:#e6db74">HomeLoanAccount</span>,<span style="color:#e6db74">web-readiness</span>
</span></span></code></pre></div><p>That&rsquo;s it. When you run your spring boot application, during startup Liquibase will apply this migration, and when your app connects to the database, the tables and data will be ready immediately.</p>
<p>How does liquibase know the database credentials? I&rsquo;m not sure! It&rsquo;s magic. I assume it hooks into your spring database config, an example of which might be:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yml" data-lang="yml"><span style="display:flex;"><span><span style="color:#f92672">datasource</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">type</span>: <span style="color:#ae81ff">com.zaxxer.hikari.HikariDataSource</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">url</span>: <span style="color:#ae81ff">jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">username</span>: <span style="color:#ae81ff">sa</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">password</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">hikari</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">poolName</span>: <span style="color:#ae81ff">Hikari</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">auto-commit</span>: <span style="color:#66d9ef">false</span>
</span></span></code></pre></div><h3 id="using-flyway">Using Flyway</h3>
<p>Flyway is a little bit different. The focus on SQL tidies things up, in my opinion.</p>
<p>First, you add the relevant dependencies:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-groovy" data-lang="groovy"><span style="display:flex;"><span><span style="color:#75715e">// Persistence
</span></span></span><span style="display:flex;"><span>    implementation<span style="color:#f92672">(</span><span style="color:#e6db74">&#39;com.h2database:h2&#39;</span><span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>    implementation<span style="color:#f92672">(</span><span style="color:#e6db74">&#39;org.postgresql:postgresql&#39;</span><span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>    implementation<span style="color:#f92672">(</span><span style="color:#e6db74">&#39;org.springframework.boot:spring-boot-starter-jdbc&#39;</span><span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>    implementation <span style="color:#e6db74">&#34;org.flywaydb:flyway-core&#34;</span>
</span></span></code></pre></div><p>Tell Flyway how to connect to your database (in my case, via <code>application.yml</code>):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yml" data-lang="yml"><span style="display:flex;"><span>  <span style="color:#f92672">flyway</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">locations</span>: <span style="color:#ae81ff">classpath:db/migration/dev</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">url</span>: <span style="color:#ae81ff">jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">user</span>: <span style="color:#ae81ff">sa</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">password</span>:
</span></span></code></pre></div><p>Make some folders, again in <code>src/main/resources</code>:</p>
<ul>
<li>Create <code>db/migration</code></li>
<li>In my case, I have H2 in dev and PostgreSQL in test and prod, so I have two sibling folders, <code>dev</code> and <code>prod</code> (test and prod scripts both live in prod as I want those to be the same, and they are the same SQL dialect).</li>
</ul>
<p>The next step is to write some migrations in the appropriate SQL dialect.
Where before our Company migration was in XML, now we&rsquo;re in SQL, note that there is a naming convention here that allows us to cut down on boiler-plate. You have to name files like so <code>V1.0__create_company.sql</code> where the VX.x will define the order in which migrations are run. Anyway, here&rsquo;s the file:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">create</span> sequence hibernate_sequence <span style="color:#66d9ef">start</span> <span style="color:#66d9ef">with</span> <span style="color:#ae81ff">1050</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">create</span> <span style="color:#66d9ef">table</span> company
</span></span><span style="display:flex;"><span>(
</span></span><span style="display:flex;"><span>    id                            uuid         <span style="color:#66d9ef">not</span> <span style="color:#66d9ef">null</span>,
</span></span><span style="display:flex;"><span>    name                          varchar(<span style="color:#ae81ff">255</span>) <span style="color:#66d9ef">not</span> <span style="color:#66d9ef">null</span>,
</span></span><span style="display:flex;"><span>    web_page_address              varchar(<span style="color:#ae81ff">255</span>),
</span></span><span style="display:flex;"><span>    billing_contact_email_address varchar(<span style="color:#ae81ff">255</span>),
</span></span><span style="display:flex;"><span>    primary_contact_email_address varchar(<span style="color:#ae81ff">255</span>),
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">constraint</span> pk_company
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">primary</span> <span style="color:#66d9ef">key</span> (id)
</span></span><span style="display:flex;"><span>);
</span></span></code></pre></div><p>Dropping the XML leaves things a lot cleaner and readable, in my opinion.
We load data as well, but this is just a SQL script rather than CSV:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">INSERT</span> <span style="color:#66d9ef">INTO</span> <span style="color:#66d9ef">PUBLIC</span>.COMPANY (ID, NAME, WEB_PAGE_ADDRESS, BILLING_CONTACT_EMAIL_ADDRESS, PRIMARY_CONTACT_EMAIL_ADDRESS)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">VALUES</span> (<span style="color:#e6db74">&#39;1a689e52-f35b-4bda-934c-ea4f076bdc2c&#39;</span>, <span style="color:#e6db74">&#39;Blue Fish Software Inc&#39;</span>, <span style="color:#e6db74">&#39;bluefish.io&#39;</span>, <span style="color:#e6db74">&#39;bills@bluefish.io&#39;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;hi@bluefish.io&#39;</span>);
</span></span></code></pre></div><p>That&rsquo;s it. When the application runs, it&rsquo;ll load those SQL scripts and manage migrations and state for us. The disadvantage here is that we need two sets of scripts, one for H2 and one for PostgreSQL - this could be because my SQL is super weak and I can combine them but don&rsquo;t realise it.</p>
<h3 id="comparing-liquibase-and-flyway">Comparing Liquibase and Flyway</h3>
<p>I found Flyway to be a lot easier to use, despite having used Liquibase for a lot longer.</p>
<p>I like that Flyway is cleaner and less repetitive, and assuming I fix my dialect issue, it&rsquo;ll be a case of 1000+ fewer lines of code with Flyway than with the same setup in Liquibase. I also found the docs Flyway provides to be a lot better, and I encountered far fewer undocumented tricks needed with Flyway. If crucial information needed to get going is easier to find on Stack Overflow than on your docs pages, you&rsquo;ve done something wrong. Liquibase is bad for this.</p>
<p>Moving from <code>bigint</code> to <code>uuid</code> primary keys on all of my entities was impossible with Liquibase due to a bug around mis-detecting specific UUID values as strings.</p>
<p>Flyway had no such issues, and where I did make a mistake with Flyway, I got printed SQL exceptions rather than SQL exceptions that have abstracted by Liquibase. I encountered far less friction with Flyway.</p>
<p><a href="https://www.reddit.com/r/java/comments/g7fge5/i_wrote_about_flyway_and_liquibase_for_database/fohih2m" target="_blank" rel="noopener noreferrer">u/hooba_stank</a>

 via Reddit, makes the point that liquibase&rsquo;s profiles grant it excellent flexibility. Using contexts in different environments, composing profiles for different testing requirements and conditional change sets and rollbacks make the free Liquibase offering compelling. It&rsquo;s a great point.</p>
<p>Flyway&rsquo;s requirement for sequential ordering of SQL files can lead to sprawl if you prefer small changes per file. Encountering failures becuase you&rsquo;ve fat-fingered a SQL script version number is annoying too. <a href="https://www.reddit.com/r/java/comments/g7fge5/i_wrote_about_flyway_and_liquibase_for_database/fohkebl" target="_blank" rel="noopener noreferrer">u/koreth</a>

 adds that this problem can be avoided by configuring a pre-commit hook and CI check to detect conflicting sequence numbers so this issue can be automated to a degree, though ideally it&rsquo;s not something you have to deal with at all.</p>
<h3 id="wrap-up">Wrap Up</h3>
<p>Both Liquibase and Flyway are better than nothing by far.
My personal preference is Flyway. Flyway is cleaner to implement and easier to use on an ongoing basis and more readable. That being said, Liquibase is a powerful tool. Chosing either of these tools to use in your development will give you good gains in productivity.</p>
]]></content:encoded></item></channel></rss>