<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.2">Jekyll</generator><link href="https://asdhammu.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://asdhammu.com/" rel="alternate" type="text/html" /><updated>2024-09-03T21:43:28+00:00</updated><id>https://asdhammu.com/feed.xml</id><title type="html">Amandeep’s Blog</title><subtitle>Software Engineer</subtitle><author><name>Amandeep</name><email>asdhammu@pm.me</email></author><entry><title type="html">Integration testing with Liquibase h2 (postgresql mode) in spring boot</title><link href="https://asdhammu.com/technology/integration-testing-postgresql-h2-spring-boot.html" rel="alternate" type="text/html" title="Integration testing with Liquibase h2 (postgresql mode) in spring boot" /><published>2024-02-11T15:31:06+00:00</published><updated>2024-02-11T15:31:06+00:00</updated><id>https://asdhammu.com/technology/integration-testing-postgresql-h2-spring-boot</id><content type="html" xml:base="https://asdhammu.com/technology/integration-testing-postgresql-h2-spring-boot.html"><![CDATA[<p>Testing is integral part of application building. It’s very helpful to check for regressions and makes the application
more robust. To check how the values are being saved into databases and test out those values, integration testing is 
required. Here we saw <a href="/technology/liquibase-for-postgresql-with-springboot.html">Liquibase for Postgresql with Spring Boot</a>
how to integrate liquibase with spring boot. In this blog we will learn how to perform integration testing on tables and entities
built in the linked article.</p>

<h3 id="h2-database">H2 database</h3>

<p>To perform integration testing we will be using in memory database H2. First add dependency of h2 in pom.xml</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;dependency&gt;</span>
    <span class="nt">&lt;groupId&gt;</span>com.h2database<span class="nt">&lt;/groupId&gt;</span>
    <span class="nt">&lt;artifactId&gt;</span>h2<span class="nt">&lt;/artifactId&gt;</span>
    <span class="nt">&lt;scope&gt;</span>test<span class="nt">&lt;/scope&gt;</span>
    <span class="nt">&lt;version&gt;</span>2.2.220<span class="nt">&lt;/version&gt;</span>
<span class="nt">&lt;/dependency&gt;</span>
</code></pre></div></div>

<p>H2 configurations for integration testing. Add this to <strong>application.yml</strong> to <strong>application.properties</strong>
under <strong>src/test/resources</strong> folder. Here we notify Spring to use h2 database with postgresql
mode for running integration tests</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">spring</span><span class="pi">:</span>
  <span class="na">jpa</span><span class="pi">:</span>
    <span class="na">generate-ddl</span><span class="pi">:</span> <span class="no">false</span>
    <span class="na">hibernate.ddl-auto</span><span class="pi">:</span> <span class="s">none</span>    
  <span class="na">sql.init.platform</span><span class="pi">:</span> <span class="s">h2</span>
  <span class="na">datasource</span><span class="pi">:</span>
    <span class="na">driverClassName</span><span class="pi">:</span> <span class="s">org.h2.Driver</span>
    <span class="na">url</span><span class="pi">:</span> <span class="s">jdbc:h2:mem:;MODE=PostgreSQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE</span>
    <span class="na">username</span><span class="pi">:</span> <span class="s">SA</span>
    <span class="na">password</span><span class="pi">:</span>
</code></pre></div></div>

<h3 id="liquibase">Liquibase</h3>

<p>Add Liquibase to the configuration. Here we provide the liquibase xml file path which will be read 
by Spring on running integration tests. All the liquibase migrations will go under the <strong>src/test/resources/change-log</strong>
folder. Create folder change-log under <strong>src/test/resources</strong> if it doesn’t exist.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">spring</span><span class="pi">:</span>
  <span class="na">liquibase</span><span class="pi">:</span>
    <span class="na">enabled</span><span class="pi">:</span> <span class="no">true</span>
    <span class="na">change-log</span><span class="pi">:</span> <span class="s">change-log/changelog-main.xml</span>
    <span class="na">drop-first</span><span class="pi">:</span> <span class="no">true</span>
</code></pre></div></div>

<p>If the primary key of entity is of type java.util.UUID, uuid-ossp extension has to be added to the postgresql</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>CREATE EXTENSION IF NOT EXISTS "uuid-ossp" with schema public;
</code></pre></div></div>

<p>but this extension doesn’t exist in h2 database. So that we don’t encounter this exception on running
integration tests we have to create an alias which the h2 database can refer. Create new file <strong>uuid-v4-function.xml</strong>
with alias details for uuid under <strong>src/test/resources/change-log</strong> folder</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;databaseChangeLog</span> <span class="na">xmlns=</span><span class="s">"http://www.liquibase.org/xml/ns/dbchangelog"</span>
                   <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span>
                   <span class="na">xsi:schemaLocation=</span><span class="s">"http://www.liquibase.org/xml/ns/dbchangelog
                   http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;changeSet</span> <span class="na">id=</span><span class="s">"uuid-v4-function"</span> <span class="na">author=</span><span class="s">"test"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;sql&gt;</span>
            <span class="cp">&lt;![CDATA[
            CREATE ALIAS IF NOT EXISTS UUID_GENERATE_V4 FOR "org.h2.value.ValueUuid.getNewRandom";
            ]]&gt;</span>
        <span class="nt">&lt;/sql&gt;</span>
    <span class="nt">&lt;/changeSet&gt;</span>
<span class="nt">&lt;/databaseChangeLog&gt;</span>
</code></pre></div></div>

<p>Create sequences for the state and country table. Create new file sequences.xml under <strong>src/test/resources/change-log/</strong></p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="nt">&lt;databaseChangeLog</span>
        <span class="na">xmlns=</span><span class="s">"http://www.liquibase.org/xml/ns/dbchangelog"</span>
        <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span>
        <span class="na">xsi:schemaLocation=</span><span class="s">"http://www.liquibase.org/xml/ns/dbchangelog
        http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;changeSet</span> <span class="na">id=</span><span class="s">"createSequences"</span> <span class="na">author=</span><span class="s">"test"</span><span class="nt">&gt;</span>        
        <span class="nt">&lt;createSequence</span> <span class="na">sequenceName=</span><span class="s">"country_country_id_seq"</span> <span class="na">startValue=</span><span class="s">"10"</span> <span class="na">incrementBy=</span><span class="s">"50"</span><span class="nt">/&gt;</span>        
        <span class="nt">&lt;createSequence</span> <span class="na">sequenceName=</span><span class="s">"state_state_id_seq"</span> <span class="na">startValue=</span><span class="s">"10"</span> <span class="na">incrementBy=</span><span class="s">"50"</span><span class="nt">/&gt;</span>        
    <span class="nt">&lt;/changeSet&gt;</span>
<span class="nt">&lt;/databaseChangeLog&gt;</span>
</code></pre></div></div>

<p>Let’s add test data to state and country tables. Create new file test-data.xml under <strong>src/test/resources/change-log/</strong></p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="nt">&lt;databaseChangeLog</span>
        <span class="na">xmlns=</span><span class="s">"http://www.liquibase.org/xml/ns/dbchangelog"</span>
        <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span>
        <span class="na">xsi:schemaLocation=</span><span class="s">"http://www.liquibase.org/xml/ns/dbchangelog
        http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;changeSet</span> <span class="na">id=</span><span class="s">"test-data"</span> <span class="na">author=</span><span class="s">"test"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;sql&gt;</span>            
            insert into country(country_id, created, updated, name)  values (1, current_date, current_date, 'USA');
            insert into country(country_id, created, updated, name) values (2, current_date, current_date, 'India');

            insert into state(state_id, abbreviation, code, name, country_id) values (1, 'TX', 'TX', 'Texas', 1);
            insert into state(state_id, abbreviation, code, name, country_id) values (2, 'AK', 'AK', 'Arkansas', 1);
            insert into state(state_id, abbreviation, code, name, country_id) values (3, 'CA', 'CA', 'California', 1);
            insert into state(state_id, abbreviation, code, name, country_id) values (4, 'PA', 'PA', 'Punjab', 2);
            insert into state(state_id, abbreviation, code, name, country_id) values (5, 'HR', 'HR', 'Haryana', 2);
        <span class="nt">&lt;/sql&gt;</span>
    <span class="nt">&lt;/changeSet&gt;</span>
<span class="nt">&lt;/databaseChangeLog&gt;</span>
</code></pre></div></div>

<p>Add existing migrations to the <strong>changelog-main.xml</strong> (create file if it doesn’t exist) file. Here we will refer the main file under the 
<strong>src/main/resources/changelog/main.xml</strong> which was created in the previous article 
[Liquibase for Postgresql with Spring Boot] (/technology/liquibase-for-postgresql-with-springboot.html)
which contains all the migrations, so we don’t have to rewrite migrations for tests.</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nt">&lt;databaseChangeLog</span> <span class="na">xmlns=</span><span class="s">"http://www.liquibase.org/xml/ns/dbchangelog"</span>
                   <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span>
                   <span class="na">xsi:schemaLocation=</span><span class="s">"http://www.liquibase.org/xml/ns/dbchangelog
                   http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;include</span> <span class="na">file=</span><span class="s">"change-log/uuid-v4-function.xml"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;include</span> <span class="na">file=</span><span class="s">"change-log/sequences.xml"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;include</span> <span class="na">file=</span><span class="s">"changelog/main.xml"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;include</span> <span class="na">file=</span><span class="s">"change-log/test-data.xml"</span><span class="nt">/&gt;</span>    
<span class="nt">&lt;/databaseChangeLog&gt;</span>
</code></pre></div></div>

<p>With all these configuration, on running integration test Spring will run changelog-main.xml. Here is the flow of migrations</p>

<p>create alias -&gt; create sequences -&gt; apply all existing migrations -&gt; add test data</p>

<h3 id="integration-test">Integration test</h3>

<p>We will be testing the StateService, if it doesn’t exist create the following two files.</p>

<p>src/main/java/com/demo/service/StateService.java</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>public interface StateService {    
    List&lt;StateDTO&gt; getStatesByCountry(String name);
}
</code></pre></div></div>

<p>src/main/java/com/demo/service/impl/StateServiceImp.java</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@Service
@Transactional
public class StateServiceImpl implements StateService {

    private final StateRepository stateRepository;

    public StateServiceImpl(StateRepository stateRepository) {
        this.stateRepository = stateRepository;
    }
    
    @Override
    public List&lt;StateDTO&gt; getStatesByCountry(String name) {
        List&lt;State&gt; states = this.stateRepository.findStatesByCountryIsoCode2(name);
        return states.stream().map(state -&gt; new StateDTO(state.getStateId(), state.getName())).toList();
    }
}

</code></pre></div></div>

<p>Now we have all the configuration wired, we are ready to write the integration test</p>

<p>Create file StateServiceIT.java under src/test/java/com/demo</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@RunWith(SpringRunner.class)
@SpringBootTest
public class StateServiceIT {

    @Autowired
    private StateService stateService;

    @Test
    public void getStatesByName() {
        List&lt;StateDTO&gt; stateDTO = stateService.getStatesByCountry("US");
        assertEquals(stateDTO.size(), 3);
    }
}
</code></pre></div></div>
<p>Size is 3 as we have added 3 states in the test-data.xml. So, we don’t need to write code to push
data for testing, as it’s much easier using queries mentioned in the test-data.xml</p>

<h3 id="conclusion">Conclusion</h3>

<p>Here we saw how to write integration tests with liquibase migrations. How it’s easier to set up the test data using the sql queries.</p>

<p>For complete working solution please refer this <a href="https://github.com/asdhammu/Restaurant" target="_blank">GitHub Repo</a></p>]]></content><author><name>Amandeep</name><email>asdhammu@pm.me</email></author><category term="Technology" /><category term="spring-boot" /><category term="java" /><category term="h2" /><category term="integration-testing" /><category term="postgresql" /><summary type="html"><![CDATA[Testing is integral part of application building. It’s very helpful to check for regressions and makes the application more robust. To check how the values are being saved into databases and test out those values, integration testing is required. Here we saw Liquibase for Postgresql with Spring Boot how to integrate liquibase with spring boot. In this blog we will learn how to perform integration testing on tables and entities built in the linked article.]]></summary></entry><entry><title type="html">Liquibase for Postgresql with Spring Boot</title><link href="https://asdhammu.com/technology/liquibase-for-postgresql-with-springboot.html" rel="alternate" type="text/html" title="Liquibase for Postgresql with Spring Boot" /><published>2024-02-10T15:31:06+00:00</published><updated>2024-02-10T15:31:06+00:00</updated><id>https://asdhammu.com/technology/liquibase-for-postgresql-with-springboot</id><content type="html" xml:base="https://asdhammu.com/technology/liquibase-for-postgresql-with-springboot.html"><![CDATA[<p>Databases are required in every application to store and index data. It becomes hassle if the
database changes are not maintained well enough. As the application starts to grow database changes grow
too. To maintain databases changes Liquibase is one tool to solve it.</p>

<p>NOTE: This post assumes that you have basic spring boot (v3.0) project setup with all the required dependencies and POSTGRESQl.</p>

<h3 id="liquibase-configuration-for-spring-boot">Liquibase Configuration for Spring boot</h3>

<p>Add following dependency to pom.xml</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="nt">&lt;dependency&gt;</span>
        <span class="nt">&lt;groupId&gt;</span>org.liquibase<span class="nt">&lt;/groupId&gt;</span>
        <span class="nt">&lt;artifactId&gt;</span>liquibase-core<span class="nt">&lt;/artifactId&gt;</span>
        <span class="nt">&lt;version&gt;</span>4.23.2<span class="nt">&lt;/version&gt;</span>
    <span class="nt">&lt;/dependency&gt;</span>
</code></pre></div></div>

<p>Configure liquibase properties in application.yml or application.properties file</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="na">spring</span><span class="pi">:</span>
   <span class="na">liquibase</span><span class="pi">:</span>
       <span class="na">change-log</span><span class="pi">:</span> <span class="s">classpath:changelog/main.xml</span>
       <span class="na">enabled</span><span class="pi">:</span> <span class="no">true</span>
</code></pre></div></div>
<p>change-log tells the spring that all the database migrations are in the src/main/resources/changelog/main.xml file</p>

<p>enabled flag if true will run the liquibase configuration when the spring starts</p>

<p>main.xml will contain all the database migrations.</p>

<h3 id="database-migration">Database Migration</h3>
<p>Create file with name ‘1690137730-init-schema.xml’ under src/main/resources/changelog folder and include it in main.xml.<br />
You can name however you want, but it’s good to append timestamp to the file name so it easier to track the migrations.</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;databaseChangeLog</span> <span class="na">xmlns=</span><span class="s">"http://www.liquibase.org/xml/ns/dbchangelog"</span>
                   <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span>
                   <span class="na">xsi:schemaLocation=</span><span class="s">"http://www.liquibase.org/xml/ns/dbchangelog
                   http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;include</span> <span class="na">file=</span><span class="s">"changelog/1690137730-init-schema.xml"</span><span class="nt">/&gt;</span>    
<span class="nt">&lt;/databaseChangeLog&gt;</span>
</code></pre></div></div>

<p>Below are the details of 1690137730-init-schema.xml. Here we create two tables country and state and then 
add foreign key of country in the state table.</p>

<p>Few things to consider</p>
<ul>
  <li>add unique changeSet id ( usually name of the file should suffice)</li>
  <li>add name of the author</li>
  <li>multiple changesets can be added into single file</li>
</ul>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="cp">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="nt">&lt;databaseChangeLog</span>
        <span class="na">xmlns=</span><span class="s">"http://www.liquibase.org/xml/ns/dbchangelog"</span>
        <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span>
        <span class="na">xsi:schemaLocation=</span><span class="s">"http://www.liquibase.org/xml/ns/dbchangelog
        http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd"</span><span class="nt">&gt;</span>

    <span class="nt">&lt;changeSet</span> <span class="na">id=</span><span class="s">"1690137730-init-sequences"</span> <span class="na">author=</span><span class="s">"my-restaurant"</span> <span class="na">dbms=</span><span class="s">"postgresql"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;createSequence</span> <span class="na">sequenceName=</span><span class="s">"country_country_id_seq"</span> <span class="na">startValue=</span><span class="s">"1"</span> <span class="nt">/&gt;</span>
        <span class="nt">&lt;createSequence</span> <span class="na">sequenceName=</span><span class="s">"state_state_id_seq"</span> <span class="na">startValue=</span><span class="s">"1"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;/changeSet&gt;</span>
    <span class="nt">&lt;changeSet</span> <span class="na">id=</span><span class="s">"1690137730-init-schema"</span> <span class="na">author=</span><span class="s">"dev"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;createTable</span> <span class="na">tableName=</span><span class="s">"country"</span><span class="nt">&gt;</span>
            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">"country_id"</span> <span class="na">type=</span><span class="s">"bigint"</span> <span class="na">autoIncrement=</span><span class="s">"true"</span><span class="nt">&gt;</span>
                <span class="nt">&lt;constraints</span> <span class="na">primaryKey=</span><span class="s">"true"</span> <span class="na">nullable=</span><span class="s">"false"</span><span class="nt">/&gt;</span>
            <span class="nt">&lt;/column&gt;</span>
            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">"name"</span> <span class="na">type=</span><span class="s">"varchar2(100)"</span><span class="nt">&gt;</span>
                <span class="nt">&lt;constraints</span> <span class="na">nullable=</span><span class="s">"false"</span><span class="nt">/&gt;</span>
            <span class="nt">&lt;/column&gt;</span>            
            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">"updated"</span> <span class="na">type=</span><span class="s">"timestamp"</span> <span class="na">defaultValueComputed=</span><span class="s">"CURRENT_TIMESTAMP"</span><span class="nt">/&gt;</span>
            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">"created"</span> <span class="na">type=</span><span class="s">"timestamp"</span> <span class="na">defaultValueComputed=</span><span class="s">"CURRENT_TIMESTAMP"</span><span class="nt">/&gt;</span>
        <span class="nt">&lt;/createTable&gt;</span>

        <span class="nt">&lt;createTable</span> <span class="na">tableName=</span><span class="s">"state"</span><span class="nt">&gt;</span>
            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">"state_id"</span> <span class="na">type=</span><span class="s">"bigint"</span> <span class="na">autoIncrement=</span><span class="s">"true"</span><span class="nt">&gt;</span>
                <span class="nt">&lt;constraints</span> <span class="na">primaryKey=</span><span class="s">"true"</span> <span class="na">nullable=</span><span class="s">"false"</span><span class="nt">/&gt;</span>
            <span class="nt">&lt;/column&gt;</span>
            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">"name"</span> <span class="na">type=</span><span class="s">"varchar2(100)"</span><span class="nt">&gt;</span>
                <span class="nt">&lt;constraints</span> <span class="na">nullable=</span><span class="s">"false"</span><span class="nt">/&gt;</span>
            <span class="nt">&lt;/column&gt;</span>
            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">"code"</span> <span class="na">type=</span><span class="s">"varchar2(100)"</span><span class="nt">/&gt;</span>            
            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">"country_id"</span> <span class="na">type=</span><span class="s">"bigint"</span><span class="nt">&gt;</span>
                <span class="nt">&lt;constraints</span> <span class="na">nullable=</span><span class="s">"false"</span><span class="nt">/&gt;</span>
            <span class="nt">&lt;/column&gt;</span>
            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">"updated"</span> <span class="na">type=</span><span class="s">"timestamp"</span> <span class="na">defaultValueComputed=</span><span class="s">"CURRENT_TIMESTAMP"</span><span class="nt">/&gt;</span>
            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">"created"</span> <span class="na">type=</span><span class="s">"timestamp"</span> <span class="na">defaultValueComputed=</span><span class="s">"CURRENT_TIMESTAMP"</span><span class="nt">/&gt;</span>
        <span class="nt">&lt;/createTable&gt;</span>
        <span class="nt">&lt;addForeignKeyConstraint</span> <span class="na">baseTableName=</span><span class="s">"state"</span> <span class="na">baseColumnNames=</span><span class="s">"country_id"</span> <span class="na">constraintName=</span><span class="s">"fK_state_country_id"</span>
                                 <span class="na">referencedTableName=</span><span class="s">"country"</span>
                                 <span class="na">referencedColumnNames=</span><span class="s">"country_id"</span><span class="nt">/&gt;</span>        
    <span class="nt">&lt;/changeSet&gt;</span>    
<span class="nt">&lt;/databaseChangeLog&gt;</span>

</code></pre></div></div>

<h3 id="apply-migrations">Apply Migrations</h3>

<p>Assuming that you have valid postgresql connection string in the spring.datasource.url.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">spring</span><span class="pi">:</span>
  <span class="na">datasource</span><span class="pi">:</span>
    <span class="na">url</span><span class="pi">:</span> <span class="s">jdbc:postgresql://localhost:5432/demodb?currentSchema=myapp</span>
</code></pre></div></div>

<p>Run the application, this will run the migrations for the above datasource.url connection string.
After the application runs successfully sequences and tables should be created under demodb/myapp schema.</p>

<p>Liquibase also creates two extra tables databasechangelog and databasechangeloglock to keep track
which migrations have been applied, so that on next turn it won’t apply the migrations again which have been applied.</p>

<h3 id="entity-reference">Entity Reference</h3>

<p>Create State and Country entities. These entities will be used by the repositories for spring JPA</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@Table(name = "state")
@Entity
@Getter
@Setter
public class State {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "stateSeq")
    @SequenceGenerator(name = "stateSeq", sequenceName = "state_state_id_seq", allocationSize = 1)
    @Column(name = "state_id")
    private Long stateId;

    @Column(name = "name", nullable = false)
    @NotNull
    private String name;

    @Column(name = "code")
    private String code;    

    @ManyToOne
    @JoinColumn(name = "country_id")
    @NotNull
    private Country country;
    
    @Column(name = "created", updatable = false, columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
    @CreatedDate
    private LocalDateTime created;

    @Column(name = "updated", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
    @LastModifiedDate
    private LocalDateTime updated;

}
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@Entity
@Table(name = "country")
@Getter
@Setter
public class Country {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "countrySeq")
    @SequenceGenerator(name = "countrySeq", sequenceName = "country_country_id_seq", allocationSize = 1)
    @Column(name = "country_id")
    private Long countryId;
    
    @Column(name = "name", nullable = false)
    @NotNull
    private String name;
        
    @OneToMany(mappedBy = "country")
    private List&lt;State&gt; states = new ArrayList&lt;&gt;();
    
    @Column(name = "created", updatable = false, columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
    @CreatedDate
    private LocalDateTime created;

    @Column(name = "updated", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
    @LastModifiedDate
    private LocalDateTime updated;
}

</code></pre></div></div>

<h3 id="update-migrations">Update Migrations</h3>

<p>To add another migration, create new file under the changelog folder and include that in the main.xml.
Add your changes with new changeset and run the application. On successful start of application new migrations should be
applied too.</p>

<h3 id="conclusion">Conclusion</h3>

<p>In this post we saw how keep track of the database migrations using the liquibase tool and integration with spring boot.</p>]]></content><author><name>Amandeep</name><email>asdhammu@pm.me</email></author><category term="Technology" /><category term="postgresql" /><category term="liquibase" /><category term="spring-boot" /><category term="java" /><summary type="html"><![CDATA[Databases are required in every application to store and index data. It becomes hassle if the database changes are not maintained well enough. As the application starts to grow database changes grow too. To maintain databases changes Liquibase is one tool to solve it.]]></summary></entry><entry><title type="html">How to deploy Jekyll site to AWS with GitHub actions</title><link href="https://asdhammu.com/technology/how-to-deploy-jekyll-to-aws-with-github-actions.html" rel="alternate" type="text/html" title="How to deploy Jekyll site to AWS with GitHub actions" /><published>2023-10-14T19:31:06+00:00</published><updated>2023-10-14T19:31:06+00:00</updated><id>https://asdhammu.com/technology/how-to-deploy-jekyll-to-aws-with-github-actions</id><content type="html" xml:base="https://asdhammu.com/technology/how-to-deploy-jekyll-to-aws-with-github-actions.html"><![CDATA[<p>In this post we will see how to deploy the blog we created here <a href="/technology/how-to-build-blog-using-jekyll.html">How to build blog with Jekyll</a>.</p>

<h3 id="aws-s3">AWS S3</h3>
<ol>
  <li>
    <p>Create S3 bucket with your domain name. e.g. If your domain is mypersonaldomain.com, then create bucket in S3 with name mypersonaldomain.com</p>

    <p><img src="../assets/images/2023-10-14/bucket_name.png" alt="bucket_name.png" /></p>
  </li>
  <li>
    <p>Allow all public access for this bucket and select the acknowledgement. Hit Create bucket</p>

    <p><img src="../assets/images/2023-10-14/bucket_public_access.png" alt="public_access.png" /></p>
  </li>
  <li>
    <p>Click on the bucket created and go to Properties, at the bottom of the page edit the static website hosting.
 Enable the static website hosting and enter index.html in the index document and 404.html in the error document. Hit Save Changes.
 This will create a website endpoint.</p>

    <p><img src="../assets/images/2023-10-14/static_hosting.png" alt="static_hosting.png" /></p>
  </li>
  <li>
    <p>Click the permission tabs, and edit the bucket policy. Add the following policy</p>
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> {
     "Version": "2012-10-17",
     "Statement": [
         {
             "Sid": "PublicReadGetObject",
             "Effect": "Allow",
             "Principal": "*",
             "Action": [
                 "s3:GetObject",
                 "s3:PutObjectAcl",
                 "s3:GetObjectAcl"
             ],
             "Resource": "arn:aws:s3:::mypersonaldomain.com/*"
         }
     ]
 }
            
</code></pre></div>    </div>
    <p>Now our bucket can be accessed publicly and data can be pushed from GitHub action</p>
  </li>
</ol>

<h3 id="aws-iam-user">AWS IAM User</h3>

<ol>
  <li>Create an IAM user with S3 and Cloudfront permissions. This user will be used by the GitHub actions to copy content from repository to S3 bucket.</li>
  <li>
    <p>Create an access key for this user. Note down the access key and access secret (it won’t be generated again)</p>

    <p><img src="../assets/images/2023-10-14/iam_access_key.png" alt="iam_access_key.png" /></p>
  </li>
</ol>

<h3 id="domain-certificate">Domain Certificate</h3>

<p>We will create certificate for our domain which will used by the cloud distribution</p>

<ol>
  <li>
    <p>Request public certificate in AWS certificate manager.</p>

    <p><img src="../assets/images/2023-10-14/public_certificate.png" alt="public_certificate.png" /></p>
  </li>
  <li>Choose DNS verification. ( You can choose any verification)</li>
  <li>
    <p>Click on the certificate and copy the CNAME name and values and paste in your Domain DNS to verify.</p>

    <p><img src="../assets/images/2023-10-14/cname_validation.png" alt="cname_verification.png" /></p>
  </li>
  <li>Once verified will use this certificate in the cloud distribution</li>
</ol>

<h3 id="aws-cloudfront">AWS Cloudfront</h3>
<p>Now we will create cloud front for our static S3.</p>
<ol>
  <li>Create distribution, choose your S3 in the origin domain.</li>
  <li>Choose your SSL certificate which was created in the previous step</li>
  <li>Hit Create distribution.</li>
  <li>Add the distribution domain name in the DNS
    <ol>
      <li>Add Alias with value of distribution domain name</li>
      <li>Add CNAME for www with value of distribution domain name</li>
    </ol>
  </li>
  <li>Edit the cloud distribution, and add alternate domains
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mypersonaldomian.com
www.mypersonaldomain.com
</code></pre></div>    </div>
  </li>
  <li>After successful save, your domain is now connected to this distribution which is connected to the S3 bucket.</li>
</ol>

<h3 id="github-action">GitHub Action</h3>

<ol>
  <li>Create a repository and push the code created in previous article to GitHub.</li>
  <li>Copy values for access id, access secret, bucket name and cloud distribution id</li>
  <li>
    <p>Create secrets for the repository with following names</p>

    <p><img src="../assets/images/2023-10-14/github_secrets.png" alt="github_secret.png" /></p>
  </li>
  <li>
    <p>Create a new GitHub action which will build the code and push the code to AWS S3.</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>      
 name: CI / CD
    
 # Controls when the action will run. 
 on:
   # Triggers the workflow on push for the main branch
   push:
     branches: [ main ]
    
   # Allows you to run this workflow manually from the Actions tab
   workflow_dispatch:
      
 env:
   AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
   AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
   AWS_DEFAULT_REGION: 'us-west-2'   
    
 # A workflow run is made up of one or more jobs that can run sequentially or in parallel
 jobs:
   build:
     runs-on: ubuntu-latest
     steps:
     - uses: actions/checkout@v4
     - name: Set up Ruby
       uses: ruby/setup-ruby@v1
       with:
         ruby-version: "3.2.2" # Not needed with a .ruby-version file
         bundler-cache: true
     - name: "Build Site"
       run: bundle exec jekyll build
       env:
         JEKYLL_ENV: production
     - name: "Deploy to AWS S3"
       run: aws s3 sync ./_site/ s3://${{ secrets.AWS_S3_BUCKET_NAME }} --acl public-read --delete --cache-control max-age=604800
     - name: "Invalidate CloudFront Cache"
       run: aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_CLOUDFRONT_DISTRIBUTION_ID }} --paths "/*"
    
</code></pre></div>    </div>
  </li>
  <li>Run the action, files should be copied to the S3 bucket and the domain should now serve the blog.</li>
  <li>Any new data pushed to the main branch should automatically build the solution and push the latest to our S3.</li>
</ol>

<h3 id="conclusion">Conclusion</h3>
<p>In this article we created S3 bucket with our domain name and cloud distribution using amazon managed certificate. And then created
the GitHub action which will copy data to our S3 bucket with any new pushed to main branch.</p>]]></content><author><name>Amandeep</name><email>asdhammu@pm.me</email></author><category term="Technology" /><category term="jekyll" /><category term="aws" /><category term="s3" /><category term="aws-cloud-front" /><category term="github-actions" /><summary type="html"><![CDATA[In this post we will see how to deploy the blog we created here How to build blog with Jekyll.]]></summary></entry><entry><title type="html">How to build blog with Jekyll</title><link href="https://asdhammu.com/technology/how-to-build-blog-using-jekyll.html" rel="alternate" type="text/html" title="How to build blog with Jekyll" /><published>2023-10-13T18:31:06+00:00</published><updated>2023-10-13T18:31:06+00:00</updated><id>https://asdhammu.com/technology/how-to-build-blog-using-jekyll</id><content type="html" xml:base="https://asdhammu.com/technology/how-to-build-blog-using-jekyll.html"><![CDATA[<h3 id="installation">Installation</h3>
<ol>
  <li>First step is to set up the environment with ruby (2.5.0 or higher). Depending on your OS there are couple of
ways to install ruby on your local machine. For this article we will be focusing on windows.
For different operations systems refer to this <a href="https://www.ruby-lang.org/en/documentation/installation/" target="_blank">Ruby Installation</a></li>
  <li>Install RubyGems, GCC and Make</li>
  <li>
    <p>Once the ruby in installed next step is to install jekyll</p>

    <p><code class="language-plaintext highlighter-rouge">gem install bundler jekyll</code></p>
  </li>
  <li>
    <p>Once jekyll is installed, we will use the jekyll cli to create our blog site.</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>jekyll new my-blog   
</code></pre></div>    </div>
  </li>
  <li>This will create the folder my-blog in the current working directory. In the my-blog folder you
will see autogenerated directories and files which is the skeleton of the jekyll site.
<img src="../assets/images/2023-10-13/jekyll-skeleton.png" alt="jekyll-skeleton.png" /></li>
  <li>
    <p>Next step is to build all the dependencies for jekyll which can be found in the Gemfile. Run the terminal in admin mode.</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd my-blog
bundle install
bundle exec jekyll serve
</code></pre></div>    </div>
    <p>This will start serving the files are http://localhost:4000. This will create _site folder which will contain all the compiled files to serve</p>
  </li>
</ol>

<h3 id="minima-theme-update">Minima theme update</h3>
<p>As of writing Jekyll uses minima theme (v2.5.1). We will use the latest version of minima theme.
As it provides pagination, dark theme, disqus, google analytics and couple of other changes.
As the latest version has not been released, so we will fetch the latest from GitHub. Update the 
minima dependency in Gemfile with following</p>

<p><code class="language-plaintext highlighter-rouge">gem "minima", git: "https://github.com/jekyll/minima"</code></p>

<h3 id="understanding-the-structure">Understanding the structure</h3>
<ol>
  <li>
    <p>_post folder contains all the posts. All the posts should follow the naming convention.
YYYY-MM-DD-<post_name>.markdown. e.g. 2023-03-14-welcome-to-jekyll.markdown.</post_name></p>
  </li>
  <li>
    <p>_site folder contains all the compiled pages. By default all the posts are categorized via 
category/YYYY/MM/DD/name_of_file. This is also the URL for the post. Auto generated post under the _posts folder have 
2 categories i.e. jekyll and update. On expanding the _site folder we can see the path of our post</p>

    <p><img src="../assets/images/2023-10-13/site_jekyll.png" alt="site_jekyll.png" /></p>
  </li>
</ol>

<p>This path can be configured using _config.yml file.</p>

<h3 id="configuration">Configuration</h3>
<p>All the configurations are handled by _config.yml file.
Remove twitter_username and github_username as they are deprecated in the latest minima theme.</p>
<ol>
  <li>Enable dark mode and social links
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>   minima:
     skin: auto
     date_format: "%b %-d, %Y"
     social_links:
       - { platform: github,  user_url: "&lt;github_profile_page&gt;" }
       - { platform: twitter, user_url: "&lt;x_profile_page&gt;" }
</code></pre></div>    </div>
  </li>
  <li>Add author details
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>   author:
     name: Your Name
     email: youremail@email.com
</code></pre></div>    </div>
  </li>
</ol>

<h3 id="create-posts">Create Posts</h3>
<ol>
  <li>Create new markdown file under _posts folder and add front matter to top of the file with following name 2023-10-14-my-first-post.markdown.
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>   ---
   layout: post
   title:  "How to build blog with Jekyll"
   date:   2023-10-14 13:31:06 -0500      
   summary: How to build blog with Jekyll
   categories: jekyll blog
   ---
</code></pre></div>    </div>
  </li>
  <li>Below the front matter write your post.</li>
  <li>New post will be created under jekyll/blog/2023/10/14/my-first-post.html under the _sites folder.</li>
  <li>Hit refresh and new post will show up on localhost:4000.</li>
</ol>

<h3 id="draft-posts">Draft Posts</h3>

<p>To create a draft post, create _drafts folder at root level. Create a post under _drafts
folder. Draft posts won’t display unless we specify to serve drafts posts. To serve 
the drafts append –drafts with the serve or build command</p>

<p><code class="language-plaintext highlighter-rouge">bundle exec jekyll serve --drafts</code></p>

<p>This command will serve drafts and now the draft post will be displayed on the home page.</p>

<h3 id="conclusion">Conclusion</h3>
<p>We saw how to create blog site using jekyll and how to use configuration for dark mode and create new posts and draft posts.
In next post we will see how to host the jekyll site to aws using GitHub action <a href="/technology/how-to-deploy-jekyll-to-aws-with-github-actions.html">How to deploy Jekyll to AWS with Github actions</a></p>]]></content><author><name>Amandeep</name><email>asdhammu@pm.me</email></author><category term="Technology" /><category term="jekyll" /><category term="ruby" /><category term="minima" /><summary type="html"><![CDATA[Installation First step is to set up the environment with ruby (2.5.0 or higher). Depending on your OS there are couple of ways to install ruby on your local machine. For this article we will be focusing on windows. For different operations systems refer to this Ruby Installation Install RubyGems, GCC and Make Once the ruby in installed next step is to install jekyll]]></summary></entry><entry><title type="html">Postgresql Full Text Search with Spring Boot - Part 2</title><link href="https://asdhammu.com/technology/full-text-search-postgresql-spring-boot-part-2.html" rel="alternate" type="text/html" title="Postgresql Full Text Search with Spring Boot - Part 2" /><published>2023-09-16T23:31:06+00:00</published><updated>2023-09-16T23:31:06+00:00</updated><id>https://asdhammu.com/technology/full-text-search-postgresql-spring-boot-part-2</id><content type="html" xml:base="https://asdhammu.com/technology/full-text-search-postgresql-spring-boot-part-2.html"><![CDATA[<h3 id="spring-boot-integration-with-postgresql-full-text-search">Spring Boot integration with Postgresql Full Text Search</h3>
<p>In the previous blog (<a href="/technology/full-text-search-postgresql-spring-boot.html">POSTGRESQL Full Text Search with Spring Boot - Part 1</a>) we saw the basic setup of full text search in postgresql. In this we will integrate the full text with spring boot using spring jpa.</p>

<h3 id="prerequisite">Prerequisite</h3>
<ul>
  <li>Postgresql</li>
  <li>Spring Boot</li>
</ul>

<h3 id="integration">Integration</h3>
<ol>
  <li>Create Spring boot project. There are couple ways to initialize the spring boot.
    <ol>
      <li>Spring initializer https://start.spring.io/</li>
      <li>Create a spring project from IDE ( IntelliJ or Eclipse).</li>
    </ol>
  </li>
  <li>Add Spring JPA dependency
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;dependency&gt;
   &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
   &lt;artifactId&gt;spring-boot-starter-data-jpa&lt;/artifactId&gt;
&lt;/dependency&gt;   
</code></pre></div>    </div>
  </li>
  <li>Create repository for Product.
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@Repository
public interface ProductRepository extends JpaRepository&lt;Product, UUID&gt; {    
    @Query(value = "select * from product where to_tsvector('english', name) @@ to_tsquery('english', :query) " +
            "or to_tsvector('english', description) @@ to_tsquery('english', :query)", nativeQuery = true)
    List&lt;Product&gt; searchProduct(String query);
}
</code></pre></div>    </div>
  </li>
  <li>SearchProduct method will return the result using the indexes created in the previous part.</li>
</ol>

<h3 id="conclusion">Conclusion</h3>
<p>Assuming that our connections are set up with Postgresql, this way we can query the full text indexes in postgresql from spring boot</p>]]></content><author><name>Amandeep</name><email>asdhammu@pm.me</email></author><category term="Technology" /><category term="postgresql" /><category term="spring-boot" /><category term="java" /><category term="full-text-search" /><summary type="html"><![CDATA[Spring Boot integration with Postgresql Full Text Search In the previous blog (POSTGRESQL Full Text Search with Spring Boot - Part 1) we saw the basic setup of full text search in postgresql. In this we will integrate the full text with spring boot using spring jpa.]]></summary></entry><entry><title type="html">Postgresql Full Text Search with Spring Boot - Part 1</title><link href="https://asdhammu.com/technology/full-text-search-postgresql-spring-boot.html" rel="alternate" type="text/html" title="Postgresql Full Text Search with Spring Boot - Part 1" /><published>2023-09-16T22:31:06+00:00</published><updated>2023-09-16T22:31:06+00:00</updated><id>https://asdhammu.com/technology/full-text-search-postgresql-spring-boot</id><content type="html" xml:base="https://asdhammu.com/technology/full-text-search-postgresql-spring-boot.html"><![CDATA[<h2 id="full-text-search">Full Text search</h2>

<p>As the data in the database table starts to grow into millions of rows LIKE query doesn’t perform well. As it can take more than few seconds to minutes in some cases ( depending on the volumn of data in the table) for a single query. To solve this issue it’s best to index data in the column using full text search. Full text search enables faster retreival for search keyword. Most of the databases (eg. MySQL, Postgresql, sql server) come loaded with full text search feature. In this post we will be focusing on Postgresql full text search.</p>

<h3 id="prerequisite">Prerequisite</h3>
<ul>
  <li>Postgresql</li>
  <li>Spring Boot</li>
</ul>

<h3 id="full-text-search-setup">Full Text search setup</h3>

<ol>
  <li>
    <p>Create a table PRODUCT with columns product_id, name and description.</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> create table product
 (
     product_id      uuid      default uuid_generate_v4() not null
         primary key,
     name            varchar(255)                         not null,
     description     varchar(1000)                        not null
 )
</code></pre></div>    </div>

    <p>Run the above query, this will create a table with name of PRODUCT.</p>

    <p>NOTE - if uuid gives error you might have to install the following extension</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>     CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
</code></pre></div>    </div>
  </li>
  <li>
    <p>Add data to column</p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> insert into product(description, name)
 VALUEs ('description 1','Butter Chicken');
    
 insert into product(description, name)
 VALUEs ('description 2','Samosa');
    
 insert into product(description, name)
 VALUEs ('description 3', 'Mango Lassi');
    
 insert into product(description, name)
 VALUEs ('description 4','Mutton Curry');
    
 insert into product(description, name)
 VALUEs ('description 5', 'Tandoori murg');
</code></pre></div>    </div>
  </li>
  <li>add full text index to name and description columns
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>     CREATE INDEX idx_products_name ON product USING GIN (to_tsvector('english', name));
     CREATE INDEX idx_products_description ON product USING GIN (to_tsvector('english', description));
</code></pre></div>    </div>
    <p>This will create full text search indexes on the column name and description. English in to_tsvector instructs that the values in the particular column are in English language. So the values in the columns will be tokenized based on the english tokenizer.</p>
  </li>
  <li>
    <p>Query on full text index column</p>

    <p>NOTE: queries are performed on the above 5 row data in step 2</p>

    <ul>
      <li>The following query will return the result based on the search input
        <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> select * from product where to_tsvector('english', name) @@ to_tsquery('english', 'mutton');
</code></pre></div>        </div>
      </li>
      <li>Typing substring of the query won’t yield any result. e.g. The following query will return 0 rows
        <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> select * from product where to_tsvector('english', name) @@ to_tsquery('english', 'mu');
</code></pre></div>        </div>
      </li>
      <li>To make this scenario work append :* at end of the query. The following query should return 2 rows.
        <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> select * from product where to_tsvector('english', name) @@ to_tsquery('english', 'mu:*');
</code></pre></div>        </div>
      </li>
      <li>To search within multiple columns, use the following query. This should return 5 rows
        <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> select * from product where to_tsvector('english', name) @@ to_tsquery('english', 'de:*')
           or to_tsvector('english', description) @@ to_tsquery('english', 'de:*');
</code></pre></div>        </div>
      </li>
    </ul>
  </li>
</ol>

<h3 id="conclusion">Conclusion</h3>
<p>In this we created product table and added full text search to name and description columns and also fetched data using the full text search syntax. In the next article we will see how to integrate this into spring boot using spring jpa.
<a href="/technology/full-text-search-postgresql-spring-boot-part-2.html">POSTGRESQL Full Text Search with Spring Boot - Part 2</a></p>]]></content><author><name>Amandeep</name><email>asdhammu@pm.me</email></author><category term="Technology" /><category term="postgresql" /><category term="spring-boot" /><category term="java" /><category term="full-text-search" /><summary type="html"><![CDATA[Full Text search]]></summary></entry></feed>