<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Reza Pambudi]]></title><description><![CDATA[Reza Pambudi]]></description><link>https://rezapambudi.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 16 Apr 2026 07:06:17 GMT</lastBuildDate><atom:link href="https://rezapambudi.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Shadowing variable di Rust]]></title><description><![CDATA[Apa itu shadowing variable, shadowing variable adalah di mana variabel yang dideklarasikan dalam lingkup (scope) lokal memiliki nama yang sama dengan variabel di lingkup global, untuk saya yang lebih sering menggunakan Java shadowing variable ini seo...]]></description><link>https://rezapambudi.dev/shadowing-variable-di-rust</link><guid isPermaLink="true">https://rezapambudi.dev/shadowing-variable-di-rust</guid><category><![CDATA[Rust]]></category><category><![CDATA[variable shadowing]]></category><dc:creator><![CDATA[Reza Agung Pambudi]]></dc:creator><pubDate>Wed, 04 Feb 2026 15:51:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/0eqgB57xMeA/upload/574f7fefb2299d04f729154b9b7bb0c8.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Apa itu shadowing variable, shadowing variable adalah di mana variabel yang dideklarasikan dalam lingkup (scope) lokal memiliki nama yang sama dengan variabel di lingkup global, untuk saya yang lebih sering menggunakan Java shadowing variable ini seolah - olah, jarang kelihatan karena oleh linter ketika kita membuat variable yang sama pada scope yang sama maka akan di highlight merah dan compiler akan memberitahu kalau variable ini sudah di declare sebagai contoh, ini bukan shadowing ini</p>
<pre><code class="lang-java"><span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">sayName</span><span class="hljs-params">(String name)</span></span>{
    <span class="hljs-comment">// compiler akan memberitahu jika variable ini sudah di deklaraskikan</span>
    String name = <span class="hljs-string">"Hello world"</span>;
    System.out.println(name)
}
</code></pre>
<p>contoh lain shadowing variable adalah variable antara inner class dan outerclass</p>
<pre><code class="lang-java"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OuterClass</span> </span>{
    String name = <span class="hljs-string">"outer name"</span>;

    Class InnerClass {
        String name = <span class="hljs-string">"inner name"</span>

        <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">print</span><span class="hljs-params">(String name)</span></span>{
            System.out.println(name)
            System.out.println(<span class="hljs-keyword">this</span>.name);
            System.out.println(OutterClass.<span class="hljs-keyword">this</span>.name);
        }
    }
}
</code></pre>
<p>Jika kita lihat disana sebenarnya pada Java ada shadowing variable tapi untuk menghilangkan ke ambiguan ini pada Java ada keyword <code>this</code> , yang mana ini membuat kita lebih mudah membaca program di Java, terus bagaimana shadowing variable di Rust?  </p>
<p>Shadowing variable pada Rust kurang lebih agak mirip-mirip dengan shadowing variable di JavaScript, jadi kita bisa declare variable dengan nama yang sama untuk tipe data juga boleh beda, yang perlu di ingat adalah value terakhir yang akan digunakan adalah value terakhir yang di deklarasikan contoh</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>(){
    <span class="hljs-keyword">let</span> a = <span class="hljs-number">10</span>;
    <span class="hljs-keyword">let</span> a = <span class="hljs-built_in">String</span>::from(<span class="hljs-string">"hello"</span>);
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, a);
}
</code></pre>
<p>Mungkin kita akan mikir ulang, ini kenapa perlu ada shadowing karena kalau keseringan dipakai bikin pusing bacanya, yup pertanyaan ini benar jika di implemntasikan di Java tapi jika di Rust mungkin akan menjadi keunggulan kalau dipikir, contohnya</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">increment</span></span>(num: <span class="hljs-built_in">i32</span>) -&gt; <span class="hljs-built_in">i32</span> {
    <span class="hljs-keyword">return</span> num + <span class="hljs-number">1</span>;
}

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> input_str = <span class="hljs-string">"42"</span>;
    <span class="hljs-keyword">let</span> input_parsed = input_str.parse::&lt;<span class="hljs-built_in">i32</span>&gt;().unwrap();
    <span class="hljs-keyword">let</span> input_incremented = increment(input_parsed);
    <span class="hljs-keyword">let</span> input_final = input_incremented + <span class="hljs-number">3</span>;
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, input_final);
}
</code></pre>
<p>kalau dilihat diatas kita bakalan sibuk mikirin nama variable untuk setiap proses, coba kita lihat ketika kita meleverage manfaat shadowing</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">increment</span></span>(num: <span class="hljs-built_in">i32</span>) -&gt; <span class="hljs-built_in">i32</span> {
    <span class="hljs-keyword">return</span> num + <span class="hljs-number">1</span>;
}

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> input = <span class="hljs-string">"42"</span>;

    <span class="hljs-keyword">let</span> input = input.parse::&lt;<span class="hljs-built_in">i32</span>&gt;().unwrap();
    <span class="hljs-keyword">let</span> input = increment(input);
    <span class="hljs-keyword">let</span> input = input + <span class="hljs-number">3</span>;

    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, input);
}
</code></pre>
<p>lebih clean bukan dan kita bisa lebih fokus ke proses apa yang sebenarnya terjadi, namum perlu di note shadowing ini kaya pedang bermata dua kalau tidak wise menggunakannya ya pusing juga.</p>
]]></content:encoded></item><item><title><![CDATA[[Config] Konfigurasi VPS Linux]]></title><description><![CDATA[Spertinya ini sudah ke-sekian kalinya setup VPS. Note basic setup simple yang bisa dilakukan
Membuat User
ssh root@your-server-ip
adduser newuser
usermod -aG sudo newuser
su - newuser #Test user

Setup SSH
ssh-keygen -t ed25519 -C "your_email@example...]]></description><link>https://rezapambudi.dev/config-konfigurasi-vps-linux</link><guid isPermaLink="true">https://rezapambudi.dev/config-konfigurasi-vps-linux</guid><category><![CDATA[vps]]></category><category><![CDATA[hosting]]></category><dc:creator><![CDATA[Reza Agung Pambudi]]></dc:creator><pubDate>Tue, 04 Feb 2025 16:15:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/40XgDxBfYXM/upload/4abd040c2310d933545447e36e0aa765.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Spertinya ini sudah ke-sekian kalinya setup VPS. Note basic setup simple yang bisa dilakukan</p>
<h3 id="heading-membuat-user">Membuat User</h3>
<pre><code class="lang-bash">ssh root@your-server-ip
adduser newuser
usermod -aG sudo newuser
su - newuser <span class="hljs-comment">#Test user</span>
</code></pre>
<h3 id="heading-setup-ssh">Setup SSH</h3>
<pre><code class="lang-bash">ssh-keygen -t ed25519 -C <span class="hljs-string">"your_email@example.com"</span>
ssh-copy-id -i ~/.ssh/id_ed25519.pub newuser@your-server-ip
ssh newuser@your-server-ip
</code></pre>
<h3 id="heading-harden-ssh-dikonfigruasi-pada-vps">Harden SSH (Dikonfigruasi pada VPS)</h3>
<pre><code class="lang-bash">sudo vim /etc/ssh/sshd_config

<span class="hljs-comment"># ubah line berikut</span>
PermitRootLogin no <span class="hljs-comment"># Disable root login</span>
PasswordAuthentication no  <span class="hljs-comment"># Disable password based auth</span>

<span class="hljs-comment"># Restart SSH dan Test SSH dari local</span>
sudo systemctl restart ssh
ssh newuser@your-server-ip
</code></pre>
<h3 id="heading-install-uncomplicated-firewall-ufw">Install Uncomplicated Firewall (UFW)</h3>
<pre><code class="lang-bash">sudo apt install ufw

sudo ufw allow OpenSSH    <span class="hljs-comment"># SSH</span>
sudo ufw allow 80/tcp     <span class="hljs-comment"># HTTP</span>
sudo ufw allow 443/tcp    <span class="hljs-comment"># HTTPS</span>

<span class="hljs-comment"># enable ufw dan cek status</span>
sudo ufw <span class="hljs-built_in">enable</span>
sudo ufw status
</code></pre>
<h3 id="heading-setup-fail2ban">Setup Fail2ban</h3>
<pre><code class="lang-bash"><span class="hljs-comment"># Install Fail2Ban</span>
sudo apt install fail2ban

<span class="hljs-comment"># Copy default ke local untuk dirubah nantinya</span>
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo vim /etc/fail2ban/jail.local

<span class="hljs-comment"># Pastikan configurasi ssh untuk akeses dikonfigurasi sebagai berikut </span>
<span class="hljs-comment"># [sshd]</span>
<span class="hljs-comment"># enabled = true</span>
<span class="hljs-comment"># port = 22 # sesuaikan dengan port ssh di vps</span>
<span class="hljs-comment"># maxretry = 5</span>
<span class="hljs-comment"># bantime = 3600</span>

<span class="hljs-comment"># Restart Fail2Ban service</span>
sudo systemctl restart fail2ban

<span class="hljs-comment"># Check Fail2Ban status</span>
sudo fail2ban-client status
sudo fail2ban-client status sshd
</code></pre>
]]></content:encoded></item><item><title><![CDATA[[Error] Intellij 2022.1.4 Eslint]]></title><description><![CDATA[TypeError: this.libOptions.parse is not a function

TypeError: this.libOptions.parse is not a function
    at ESLint8Plugin.<anonymous> (/Applications/IntelliJ IDEA.app/Contents/plugins/JavaScriptLanguage/languageService/eslint/bin/eslint8-plugin.js:...]]></description><link>https://rezapambudi.dev/error-intellij-202214-eslint</link><guid isPermaLink="true">https://rezapambudi.dev/error-intellij-202214-eslint</guid><category><![CDATA[error]]></category><dc:creator><![CDATA[Reza Agung Pambudi]]></dc:creator><pubDate>Tue, 21 Jan 2025 16:36:37 GMT</pubDate><content:encoded><![CDATA[<pre><code class="lang-bash">TypeError: this.libOptions.parse is not a <span class="hljs-keyword">function</span>

TypeError: this.libOptions.parse is not a <span class="hljs-keyword">function</span>
    at ESLint8Plugin.&lt;anonymous&gt; (/Applications/IntelliJ IDEA.app/Contents/plugins/JavaScriptLanguage/languageService/eslint/bin/eslint8-plugin.js:139:64)
    at step (/Applications/IntelliJ IDEA.app/Contents/plugins/JavaScriptLanguage/languageService/eslint/bin/eslint8-plugin.js:44:23)
    at Object.next (/Applications/IntelliJ IDEA.app/Contents/plugins/JavaScriptLanguage/languageService/eslint/bin/eslint8-plugin.js:25:53)
    at /Applications/IntelliJ IDEA.app/Contents/plugins/JavaScriptLanguage/languageService/eslint/bin/eslint8-plugin.js:19:71
    at new Promise (&lt;anonymous&gt;)
    at __awaiter (/Applications/IntelliJ IDEA.app/Contents/plugins/JavaScriptLanguage/languageService/eslint/bin/eslint8-plugin.js:15:12)
    at ESLint8Plugin.invokeESLint (/Applications/IntelliJ IDEA.app/Contents/plugins/JavaScriptLanguage/languageService/eslint/bin/eslint8-plugin.js:133:16)
    at ESLint8Plugin.&lt;anonymous&gt; (/Applications/IntelliJ IDEA.app/Contents/plugins/JavaScriptLanguage/languageService/eslint/bin/eslint8-plugin.js:120:44)
    at step (/Applications/IntelliJ IDEA.app/Contents/plugins/JavaScriptLanguage/languageService/eslint/bin/eslint8-plugin.js:44:23)
    at Object.next (/Applications/IntelliJ IDEA.app/Contents/plugins/JavaScriptLanguage/languageService/eslint/bin/eslint8-plugin.js:25:53)
Process finished with <span class="hljs-built_in">exit</span> code -1
</code></pre>
<p>Versi Intellij</p>
<p><code>IntelliJ IDEA 2022.1.4 (Ultimate Edition)</code></p>
<p>Issue ini dikarenakan adanya update pada Eslint pada versi <code>8.23</code> .  </p>
<p>Solusi:</p>
<ul>
<li><p>Downgrade <code>Eslint</code> pada <code>package.json</code> ke versi <code>8.22</code></p>
</li>
<li><p>Update Intellij (Saya tidak mau memperpanjang lisensi 😅)</p>
</li>
</ul>
<p><a target="_blank" href="https://youtrack.jetbrains.com/issue/WEB-57089/ESLint8.23-TypeError-this.libOptions.parse-is-not-a-function">https://youtrack.jetbrains.com/issue/WEB-57089/ESLint8.23-TypeError-this.libOptions.parse-is-not-a-function</a></p>
]]></content:encoded></item><item><title><![CDATA[[Error] npx create-react-app]]></title><description><![CDATA[npx create-react-app frontend --template typescript

Installing packages. This might take a couple of minutes.
Installing react, react-dom, and react-scripts with cra-template-typescript...


added 1323 packages in 1m

267 packages are looking for fu...]]></description><link>https://rezapambudi.dev/error-npx-create-react-app</link><guid isPermaLink="true">https://rezapambudi.dev/error-npx-create-react-app</guid><category><![CDATA[error]]></category><dc:creator><![CDATA[Reza Agung Pambudi]]></dc:creator><pubDate>Tue, 21 Jan 2025 15:36:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/O_Xy25Dj7Mo/upload/857d641313b8e971d5d750198efa52d0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<pre><code class="lang-bash">npx create-react-app frontend --template typescript

Installing packages. This might take a couple of minutes.
Installing react, react-dom, and react-scripts with cra-template-typescript...


added 1323 packages <span class="hljs-keyword">in</span> 1m

267 packages are looking <span class="hljs-keyword">for</span> funding
  run `npm fund` <span class="hljs-keyword">for</span> details

Initialized a git repository.

Installing template dependencies using npm...
npm error code ERESOLVE
npm error ERESOLVE unable to resolve dependency tree
npm error
npm error While resolving: frontend@0.1.0
npm error Found: react@19.0.0
npm error node_modules/react
npm error   react@<span class="hljs-string">"^19.0.0"</span> from the root project
npm error
npm error Could not resolve dependency:
npm error peer react@<span class="hljs-string">"^18.0.0"</span> from @testing-library/react@13.4.0
npm error node_modules/@testing-library/react
npm error   @testing-library/react@<span class="hljs-string">"^13.0.0"</span> from the root project
npm error
npm error Fix the upstream dependency conflict, or retry
npm error this <span class="hljs-built_in">command</span> with --force or --legacy-peer-deps
npm error to accept an incorrect (and potentially broken) dependency resolution.
</code></pre>
<p>Ini dikarenakan ada dependency issue dari <code>testing-library/react@13.4.0</code> yang mana library ini memerlukan React versi 18 sedangkan project yang akan dibuat menggunakan React 19. Secara dokumentasi pada React.js 19 pembuatan project dengan menggunakan <code>npx create-react-app</code> sudah tidak direkomendasikan dan menyaranakan penggunakan React menggunakan framework seperti <code>Next.js</code> atau <code>Remix</code>. Jika masih ingin membuat project React tanpa menggunakan framework beberapa cara yang dapat dilakukan</p>
<p>Install menggunakan <code>Vite</code></p>
<p><code>npm create vite@latest frontend -- --template react</code> atau<br /><code>npm create vite@latest frontend -- --template react-ts</code> (React dengan Typescript)</p>
]]></content:encoded></item><item><title><![CDATA[Database partisi]]></title><description><![CDATA[Database Partisi adalah proses pemecahan data pada table di database menjadi bagian - bagian table yang lebih kecil. Partisi data dapat dilakukan dengan menggunakan dua cara

Partisi Vertikal

Partisi Horizontal


Partisi table dapat dilakukan dengan...]]></description><link>https://rezapambudi.dev/database-partisi</link><guid isPermaLink="true">https://rezapambudi.dev/database-partisi</guid><dc:creator><![CDATA[Reza Agung Pambudi]]></dc:creator><pubDate>Sat, 12 Oct 2024 08:41:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/lRoX0shwjUQ/upload/346c1aa9b5bb81a778bc72d7a0548d7c.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Database Partisi adalah proses pemecahan data pada table di database menjadi bagian - bagian table yang lebih kecil. Partisi data dapat dilakukan dengan menggunakan dua cara</p>
<ul>
<li><p>Partisi Vertikal</p>
</li>
<li><p>Partisi Horizontal</p>
</li>
</ul>
<p>Partisi table dapat dilakukan dengan beberapa pendekatan</p>
<ul>
<li><p>Partisi by range (contohnya berdasarkan tanggal atau id)</p>
</li>
<li><p>Partisi by List (umutnya digunakan unutk nilai diskrit)</p>
</li>
<li><p>Partisi by hash (menggunakan nilai hash namun hashingnya harus konsisten)</p>
</li>
</ul>
<p>anggaplah terdapat sebuah table bernama student pada sebuah database yang berisi 10.000.000 baris yang strukturnya sebagai berikut</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Nama Kolom</td><td>Tipe data</td><td>Primary Key</td><td>Keterangan</td></tr>
</thead>
<tbody>
<tr>
<td>id</td><td>number</td><td>Yes</td><td>id pelajar</td></tr>
<tr>
<td>name</td><td>text</td><td>No</td><td>nama pelajar</td></tr>
<tr>
<td>grade</td><td>int</td><td>No</td><td>nilai pelajar (0 - 100)</td></tr>
</tbody>
</table>
</div><pre><code class="lang-sql"><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> students(<span class="hljs-keyword">name</span>, grade)  
<span class="hljs-keyword">select</span>  
    random_string(<span class="hljs-number">10</span>) <span class="hljs-keyword">as</span> <span class="hljs-keyword">name</span>,  
    <span class="hljs-keyword">floor</span>(random() * <span class="hljs-number">100</span>) <span class="hljs-keyword">as</span> grade  
<span class="hljs-keyword">from</span>  
    generate_series(<span class="hljs-number">0</span>, <span class="hljs-number">10000000</span>);
</code></pre>
<p>kemudian akan dilaukan partisi berdasarkan nilai pelajar, yang mana partisi ini dibagi menjadi 5 dengan kelipatan nilai 20</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Nilai</td><td>partisi</td></tr>
</thead>
<tbody>
<tr>
<td>1 - 20</td><td>Partisi 1</td></tr>
<tr>
<td>21 - 40</td><td>Partisi 2</td></tr>
<tr>
<td>41 - 60</td><td>Partisi 3</td></tr>
<tr>
<td>61 - 80</td><td>Partisi 4</td></tr>
<tr>
<td>81 - 100</td><td>Partisi 5</td></tr>
</tbody>
</table>
</div><p>untuk membuat table dengan partisi, sama halnya dengan pembuatan table pada umunya yang membedakan adalah penambahan keyword <code>partition by</code>.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> students_grade_partitions  
(  
    <span class="hljs-keyword">id</span>    <span class="hljs-built_in">serial</span>,  
    <span class="hljs-keyword">name</span>  <span class="hljs-built_in">text</span>,  
    grade <span class="hljs-built_in">int</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">null</span>  
) <span class="hljs-keyword">partition</span> <span class="hljs-keyword">by</span> <span class="hljs-keyword">range</span> (grade);
</code></pre>
<p>perlu diingat jika menambahkan primary key maka kolom grade harus ditambahkan sebagai komposite. jika tidak maka akan menghasilkan error</p>
<pre><code class="lang-sql">[0A000] ERROR: unique constraint on partitioned table must include all partitioning columns Detail: PRIMARY KEY constraint on table "students_grade_partitions" lacks column "grade" which is part of the partition key.
</code></pre>
<p>Setelah table utama dibuat, proses selanjutnya pembuatan table partisi sebanyak total paritsi yang diinginkan</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> students_grade0020 (<span class="hljs-keyword">like</span> students_grade_partitions <span class="hljs-keyword">including</span> <span class="hljs-keyword">indexes</span> );  
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> students_grade2140 (<span class="hljs-keyword">like</span> students_grade_partitions <span class="hljs-keyword">including</span> <span class="hljs-keyword">indexes</span> );  
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> students_grade4160 (<span class="hljs-keyword">like</span> students_grade_partitions <span class="hljs-keyword">including</span> <span class="hljs-keyword">indexes</span> );  
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> students_grade6180 (<span class="hljs-keyword">like</span> students_grade_partitions <span class="hljs-keyword">including</span> <span class="hljs-keyword">indexes</span> );  
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> students_grade81100 (<span class="hljs-keyword">like</span> students_grade_partitions <span class="hljs-keyword">including</span> <span class="hljs-keyword">indexes</span>);
</code></pre>
<p>command <code>including indexes</code> ini sangat penting ketika membuat paritisi karena partisi bergantung dengan index, jadi jika ada perubahan index pada tabel utama akan berpengaruh juga pada tabel partisi. Table partisi yang telah dibuat perlu di assign ke table utama. Perlu diingat untuk nilai <code>to</code> sifatnya adalah exclusive</p>
<pre><code class="lang-sql"><span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> students_grade_partitions attach <span class="hljs-keyword">PARTITION</span> students_grade0020 <span class="hljs-keyword">FOR</span> <span class="hljs-keyword">VALUES</span> <span class="hljs-keyword">FROM</span> (<span class="hljs-number">0</span>) <span class="hljs-keyword">TO</span> (<span class="hljs-number">21</span>);  
<span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> students_grade_partitions attach <span class="hljs-keyword">PARTITION</span> students_grade2140 <span class="hljs-keyword">FOR</span> <span class="hljs-keyword">VALUES</span> <span class="hljs-keyword">FROM</span> (<span class="hljs-number">21</span>) <span class="hljs-keyword">TO</span> (<span class="hljs-number">41</span>);  
<span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> students_grade_partitions attach <span class="hljs-keyword">PARTITION</span> students_grade4160 <span class="hljs-keyword">FOR</span> <span class="hljs-keyword">VALUES</span> <span class="hljs-keyword">FROM</span> (<span class="hljs-number">41</span>) <span class="hljs-keyword">TO</span> (<span class="hljs-number">61</span>);  
<span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> students_grade_partitions attach <span class="hljs-keyword">PARTITION</span> students_grade6180 <span class="hljs-keyword">FOR</span> <span class="hljs-keyword">VALUES</span> <span class="hljs-keyword">FROM</span> (<span class="hljs-number">61</span>) <span class="hljs-keyword">TO</span> (<span class="hljs-number">81</span>);  
<span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> students_grade_partitions attach <span class="hljs-keyword">PARTITION</span> students_grade81100 <span class="hljs-keyword">FOR</span> <span class="hljs-keyword">VALUES</span> <span class="hljs-keyword">FROM</span> (<span class="hljs-number">81</span>) <span class="hljs-keyword">TO</span> (<span class="hljs-number">100</span>);
</code></pre>
<p>Jika dilihat lihat secara visual table partisi tersebut akan menjadi bagian dari table utama</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728722434498/6357ace4-52fe-47fc-ba3f-e2ed3f89a67d.png" alt /></p>
<h2 id="heading-partisi-vs-non-partisi">Partisi vs non partisi</h2>
<p>untuk hasil perbandingan query table dengan partisi dan non partisi adalah sebagai berikut.</p>
<h3 id="heading-query-range">Query range</h3>
<pre><code class="lang-sql"><span class="hljs-keyword">explain</span> <span class="hljs-keyword">analyze</span> <span class="hljs-keyword">select</span> * <span class="hljs-keyword">from</span> students_grade_partitions <span class="hljs-keyword">where</span> grade &gt; <span class="hljs-number">40</span> <span class="hljs-keyword">and</span> grade &lt; <span class="hljs-number">61</span>;

<span class="hljs-comment">/* OUTPUT
Seq Scan on students_grade4160 students_grade_partitions  (cost=0.00..41441.50 rows=1803866 width=14) (actual time=0.133..140.940 rows=1802569 loops=1)  
  Filter: ((grade &gt; 41) AND (grade &lt; 60))  
  Rows Removed by Filter: 199798  
Planning Time: 0.302 ms  
Execution Time: 186.098 ms
*/</span>

<span class="hljs-keyword">explain</span> <span class="hljs-keyword">analyze</span> <span class="hljs-keyword">select</span> * <span class="hljs-keyword">from</span> students <span class="hljs-keyword">where</span> grade &gt; <span class="hljs-number">41</span> <span class="hljs-keyword">and</span> grade &lt; <span class="hljs-number">60</span>;
<span class="hljs-comment">/* OUTPUT
Bitmap Heap Scan on students  (cost=24437.02..108267.03 rows=1791667 width=14) (actual time=70.474..524.556 rows=1802569 loops=1)  
  Recheck Cond: ((grade &gt; 41) AND (grade &lt; 60))  
  Heap Blocks: exact=56955  
  -&gt;  Bitmap Index Scan on students_grade  (cost=0.00..23989.11 rows=1791667 width=0) (actual time=63.083..63.083 rows=1802569 loops=1)  
        Index Cond: ((grade &gt; 41) AND (grade &lt; 60))  
Planning Time: 0.638 ms  
JIT:  
  Functions: 2  
"  Options: Inlining false, Optimization false, Expressions true, Deforming true"  
"  Timing: Generation 1.441 ms, Inlining 0.000 ms, Optimization 0.000 ms, Emission 0.000 ms, Total 1.441 ms"  
Execution Time: 574.170 ms
*/</span>
</code></pre>
<p>Perbandingan eksekusi waktu diatas cukup jelas data yang dipartisi menghasilkan eksekusi lebih cepat karena query rangenya <code>grade &gt; 40 and grade &lt; 61</code>, Postgress hanya perlu melakukan scaning index pada satu partisi <code>students_grade4160</code> dan tidak mengalami overhead dari membangun bitmap. Sedangkan query kedua karena data yang di index cukup besar maka akan dibuat bitmap yang menyebabkan query jadi lambat.</p>
<p>Apa yang terjadi jika kita menscan seluruh partisi dengan menggunakan query untuk grade yang lebih dari <code>5</code>?</p>
<pre><code class="lang-sql"><span class="hljs-keyword">explain</span> <span class="hljs-keyword">analyze</span> <span class="hljs-keyword">select</span> * <span class="hljs-keyword">from</span> students_grade_partitions <span class="hljs-keyword">where</span> grade &gt; <span class="hljs-number">7</span>;
<span class="hljs-comment">/* OUTPUT
Append  (cost=0.00..227967.39 rows=9202076 width=14) (actual time=9.562..1413.924 rows=9199930 loops=1)  
  -&gt;  Seq Scan on students_grade0020 students_grade_partitions_1  (cost=0.00..38217.45 rows=1302431 width=14) (actual time=9.561..224.284 rows=1300285 loops=1)  
        Filter: (grade &gt; 7)  
        Rows Removed by Filter: 800071  
  -&gt;  Seq Scan on students_grade2140 students_grade_partitions_2  (cost=0.00..36408.10 rows=2000968 width=14) (actual time=0.196..211.621 rows=2000968 loops=1)  
        Filter: (grade &gt; 7)  
  -&gt;  Seq Scan on students_grade4160 students_grade_partitions_3  (cost=0.00..36435.59 rows=2002367 width=14) (actual time=0.149..196.123 rows=2002367 loops=1)  
        Filter: (grade &gt; 7)  
  -&gt;  Seq Scan on students_grade6180 students_grade_partitions_4  (cost=0.00..36358.74 rows=1998219 width=14) (actual time=0.110..179.299 rows=1998219 loops=1)  
        Filter: (grade &gt; 7)  
  -&gt;  Seq Scan on students_grade81100 students_grade_partitions_5  (cost=0.00..34537.14 rows=1898091 width=14) (actual time=0.105..170.764 rows=1898091 loops=1)  
        Filter: (grade &gt; 7)  
Planning Time: 6.056 ms  
JIT:  
  Functions: 10  
"  Options: Inlining false, Optimization false, Expressions true, Deforming true"  
"  Timing: Generation 1.445 ms, Inlining 0.000 ms, Optimization 1.203 ms, Emission 8.386 ms, Total 11.034 ms"  
Execution Time: 1634.274 ms
*/</span>

<span class="hljs-keyword">explain</span> <span class="hljs-keyword">analyze</span> <span class="hljs-keyword">select</span> * <span class="hljs-keyword">from</span> students <span class="hljs-keyword">where</span> grade &gt; <span class="hljs-number">7</span>;
<span class="hljs-comment">/* OUTPUT
Seq Scan on students  (cost=0.00..181955.01 rows=9201001 width=14) (actual time=4.328..845.234 rows=9199930 loops=1)  
  Filter: (grade &gt; 7)  
  Rows Removed by Filter: 800071  
Planning Time: 0.280 ms  
JIT:  
  Functions: 2  
"  Options: Inlining false, Optimization false, Expressions true, Deforming true"  
"  Timing: Generation 0.538 ms, Inlining 0.000 ms, Optimization 0.253 ms, Emission 3.852 ms, Total 4.642 ms"  
Execution Time: 1062.563 ms
*/</span>
</code></pre>
<p>Kita bisa lihat sekarang query kedua lebih cepat tanpa partisi, hal ini dikarenakan pada query tersebut ketika dilakukan proses pencarian keseluruh partisi akan ada prosess <code>append</code> setelah dilakukan pencarian per partisi, sedangkan jika tanpa partisi hal yang perlu dilakukan hanya squential scan index saja tidak perlu ada proses append karena tidak terpartisi hal ini menyebabkan query kedua lebih cepat.</p>
<h3 id="heading-ukuran-data">Ukuran data</h3>
<p>Apakah dengan menggunakan partisi memori akan menjadi lebih kecil sebenarnya tidak terlalu signifikan</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728705543991/d17b85c5-e8c6-4ae7-8045-241b66ed4895.png" alt /></p>
<h3 id="heading-kesimpulan">Kesimpulan</h3>
<p>secara umum manfaat partisi akan berasa jika pencarian hanya dilakukan pada satu partisi saja akan terasa sekali manfaatnya karena pencarian menjadi lebih kecil apalgi jika kolom pencarian sudah di indexing. Perlu di ingat <code>enable_partition_pruning</code> harus selalu dalam kondisi on, jika tidak maka percuma dilakukan peartisi karena pencarian akan dilakukan disemua partisi yang menyebabkan query jadi lebih lama.</p>
]]></content:encoded></item><item><title><![CDATA[Indexing]]></title><description><![CDATA[Indexing adalah proses penyusunan data sehingga mudah dalam pencarian data yang diinginkan (seperti index pada kamus) . Pada indexing dilakukan dengan cara menempatkan struktur data yang beriisi keys beserta referensinya ke data sebenarnya pada table...]]></description><link>https://rezapambudi.dev/indexing</link><guid isPermaLink="true">https://rezapambudi.dev/indexing</guid><dc:creator><![CDATA[Reza Agung Pambudi]]></dc:creator><pubDate>Sat, 21 Sep 2024 12:28:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/j2Qa8culzDY/upload/1e4c2965b59ef4fb634b12cdf69007e9.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Indexing adalah proses penyusunan data sehingga mudah dalam pencarian data yang diinginkan (seperti index pada kamus) . Pada indexing dilakukan dengan cara menempatkan struktur data yang beriisi <em>keys</em> beserta referensinya ke data sebenarnya pada table.</p>
<p>Umunya ketika membuat table dengan <code>primary key</code> secara default kolom yang menjadi <code>primary key</code> akan dibuatkan indexnya menggunakan B-Tree (Balance Tree)</p>
<p>Contoh : Anggaplah kita terdapat sebuah database (menggunakan postgres) yang memiliki table <code>employees</code> yang berisi 1000000 recrod dengan stuktur data sebagai berikut</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Kolom</td><td>data type</td><td>Primary key</td></tr>
</thead>
<tbody>
<tr>
<td>id</td><td>int</td><td>Yes</td></tr>
<tr>
<td>name</td><td>text</td><td>No</td></tr>
</tbody>
</table>
</div><pre><code class="lang-sql"><span class="hljs-keyword">explain</span> <span class="hljs-keyword">analyze</span> <span class="hljs-keyword">select</span> * <span class="hljs-keyword">from</span> emplyees <span class="hljs-keyword">where</span> <span class="hljs-keyword">name</span> = <span class="hljs-string">'Zs'</span>;
<span class="hljs-comment">/* OUTPUT
Gather  (cost=1000.00..11310.94 rows=6 width=10) (actual time=3.088..47.719 rows=25 loops=1)  
  Workers Planned: 2  
  Workers Launched: 2  
  -&gt;  Parallel Seq Scan on employees  (cost=0.00..10310.34 rows=2 width=10) (actual time=1.931..18.586 rows=8 loops=3)  
        Filter: (name = 'Zs'::text)  
        Rows Removed by Filter: 333325  
Planning Time: 0.364 ms  
Execution Time: 47.785 ms
*/</span>


<span class="hljs-keyword">explain</span> <span class="hljs-keyword">analyze</span> <span class="hljs-keyword">select</span> * <span class="hljs-keyword">from</span> employees <span class="hljs-keyword">where</span> <span class="hljs-keyword">id</span> = <span class="hljs-number">100</span>;
<span class="hljs-comment">/* OUTPUT
Index Scan using employees_pkey on employees  (cost=0.42..8.44 rows=1 width=10) (actual time=0.093..0.095 rows=1 loops=1)  
  Index Cond: (id = 100)  
Planning Time: 0.237 ms  
Execution Time: 0.178 ms
*/</span>
</code></pre>
<p>Kita bisa melihat dari dua query diatas menghasilkan waktu eksekusi yang sangat berbeda kenapa bisa seperti itu?. Untuk query pertama pencarian bedasarkan kolumn <code>name</code> tidak menggunakan indexing, sehingga pencarian dilakukan dengan melakukan pengecekan pada setiap baris pada kolom <code>name</code> pada table <code>employees</code>.</p>
<p>Sedangkan pada query kedua, pencarian menggunakan <code>id</code> (ini di indexing) proses pencarian dilakukan dengan menggunakan scanning index yang sudah dibuat sehingga tidak perlu melakukan pengecekan pada setiap baris pada table <code>employees</code>.</p>
<h3 id="heading-bagaimana-membuat-index-dan-prosesnya-pada-postgres">Bagaimana membuat index dan prosesnya pada Postgres?</h3>
<p>Untuk membuat index perlu menjalankan query</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">INDEX</span> nama_index <span class="hljs-keyword">on</span> nama_table(nama_kolumn);

<span class="hljs-comment">/* menambahakna index untuk kolom name
CREATE INDEX EPLOYEE_NAME on employees(name);
*/</span>
</code></pre>
<p>Proses yang terjadi dibalik <code>CREATE INDEX</code> adalah Postgres akan pembacaan data secara menyeluruh terlebih dahulu kemudian Postgress akan membuat B-Tree berdasarkan kolom tersebut, sehingga waktu yand dibutuhkan untuk pembuatan index berdasarkan banyaknya data yang ada pada table tersebut. Kita bisa melihat hasil perbedaan performa query pertama setelah di indexing</p>
<pre><code class="lang-sql"><span class="hljs-keyword">explain</span> <span class="hljs-keyword">analyze</span> <span class="hljs-keyword">select</span> * <span class="hljs-keyword">from</span> employees <span class="hljs-keyword">where</span> <span class="hljs-keyword">name</span> = <span class="hljs-string">'Zs'</span>;

<span class="hljs-comment">/*
Bitmap Heap Scan on employees  (cost=4.47..27.93 rows=6 width=10) (actual time=0.326..0.363 rows=25 loops=1)  
  Recheck Cond: (name = 'Zs'::text)  
  Heap Blocks: exact=25  
  -&gt;  Bitmap Index Scan on employee_name  (cost=0.00..4.47 rows=6 width=0) (actual time=0.318..0.318 rows=25 loops=1)  
        Index Cond: (name = 'Zs'::text)  
Planning Time: 4.501 ms  
Execution Time: 0.391 ms
*/</span>
</code></pre>
<p>Pembuatan index dengan penggunaan <code>CREATE INDEX</code> terlihat sangat menjanjikan namun kenapa query berikut ini tidak melakukan index scanning?</p>
<pre><code class="lang-sql"><span class="hljs-keyword">explain</span> <span class="hljs-keyword">analyze</span> <span class="hljs-keyword">select</span> * <span class="hljs-keyword">from</span> employees <span class="hljs-keyword">where</span> <span class="hljs-keyword">name</span> <span class="hljs-keyword">like</span> <span class="hljs-string">'%Zs'</span>

<span class="hljs-comment">/* OUTPUT
Gather  (cost=1000.00..11319.34 rows=90 width=10) (actual time=0.425..44.853 rows=266 loops=1)  
  Workers Planned: 2  
  Workers Launched: 2  
  -&gt;  Parallel Seq Scan on employees  (cost=0.00..10310.34 rows=38 width=10) (actual time=0.203..22.971 rows=89 loops=3)  
        Filter: (name ~~ '%Zs'::text)  
        Rows Removed by Filter: 333245  
Planning Time: 0.384 ms  
Execution Time: 44.896 ms
*/</span>
</code></pre>
<p>Hal ini bisa terjadi karena secara default index yang dibuat pada Postgress mengguakan B-Tree yang mana struktur data ini di design untuk melakukan perbandingan secara terurut seperti <code>=, &gt;, &lt;, &gt;=, &lt;=</code> dan <code>LIKE</code> tanpa pengunaan wildcard di depan yang menyebabkan Postgress tidak tahu bagaimana cara menghandle query tersebut pada <code>B-tree</code>, sehingga Postgress harus melakukan scanning secara sekuensial pada setiap baris pada table <code>employees</code>.</p>
<p>Agar query menggunakan wildcard dapat berjalan dengan lebih baik hal yang perlu dilakukan adalah merubah indexingnya menggunakan <code>trigam</code> (Trigraph). Trigram adalah sekelompok tiga karakter berurutan yang diambil dari sebuah kata, hal ini memungkinkan untuk mengukur kemiripan dua buah kata (<a target="_blank" href="https://www.postgresql.org/docs/current/pgtrgm.html">https://www.postgresql.org/docs/current/pgtrgm.html</a>)</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> EXTENSION pg_trgm;  
<span class="hljs-keyword">create</span> <span class="hljs-keyword">INDEX</span> trgm_employee_name <span class="hljs-keyword">on</span> employees <span class="hljs-keyword">USING</span> gin (<span class="hljs-keyword">name</span> gin_trgm_ops);

<span class="hljs-keyword">explain</span> <span class="hljs-keyword">analyze</span> <span class="hljs-keyword">select</span> * <span class="hljs-keyword">from</span> employees <span class="hljs-keyword">where</span> <span class="hljs-keyword">name</span> <span class="hljs-keyword">like</span> <span class="hljs-string">'%Za'</span>;
<span class="hljs-comment">/* OUTPUT
Bitmap Heap Scan on employees  (cost=17.56..342.82 rows=90 width=10) (actual time=0.478..2.449 rows=230 loops=1)  
  Recheck Cond: (name ~~ '%Za'::text)  
  Rows Removed by Index Recheck: 463  
  Heap Blocks: exact=641  
  -&gt;  Bitmap Index Scan on trgm_employee_name  (cost=0.00..17.53 rows=90 width=0) (actual time=0.288..0.288 rows=693 loops=1)  
        Index Cond: (name ~~ '%Za'::text)  
Planning Time: 3.182 ms  
Execution Time: 2.543 ms
*/</span>
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Mengexclude Code yang digenerate oleh Lombok pada coverage report]]></title><description><![CDATA[ketika memprogram dengan Java mungkin tiidak asing lagi dengan Project Lombok , yang mana merupakan salah satu library yang digunakan untuk meminimize boilerplate code pada POJO kususnya method-method setter, getter, equals, hashcode, dan antek - ant...]]></description><link>https://rezapambudi.dev/mengexclude-code-yang-digenerate-oleh-lombok-pada-coverage-report</link><guid isPermaLink="true">https://rezapambudi.dev/mengexclude-code-yang-digenerate-oleh-lombok-pada-coverage-report</guid><category><![CDATA[lombok]]></category><category><![CDATA[Java]]></category><category><![CDATA[unit testing]]></category><category><![CDATA[code coverage]]></category><dc:creator><![CDATA[Reza Agung Pambudi]]></dc:creator><pubDate>Wed, 20 Mar 2024 04:10:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/EWLHA4T-mso/upload/d4bd070954ef3f16824a82692501e45b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>ketika memprogram dengan <code>Java</code> mungkin tiidak asing lagi dengan <code>Project Lombok</code> , yang mana merupakan salah satu library yang digunakan untuk meminimize boilerplate code pada <code>POJO</code> kususnya method-method <code>setter</code>, <code>getter</code>, <code>equals</code>, <code>hashcode</code>, dan antek - anteknya.</p>
<p>Anggaplah terdapat sebuah <code>POJO</code> class <a target="_blank" href="http://Product.java"><code>Product.java</code></a></p>
<pre><code class="lang-java"><span class="hljs-meta">@Entity</span>
<span class="hljs-meta">@Builder</span>
<span class="hljs-meta">@AllArgsConstructor</span>
<span class="hljs-meta">@NoArgsConstructor</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Product</span> </span>{

  <span class="hljs-meta">@Id</span>
  <span class="hljs-meta">@GeneratedValue(strategy = GenerationType.IDENTITY)</span>
  <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> id;
  <span class="hljs-keyword">private</span> String name;
  <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> price;
  <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> quantity;

  <span class="hljs-keyword">private</span> String imageLocation;

  <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">updateProduct</span><span class="hljs-params">(Product updatedProduct)</span> </span>{
    <span class="hljs-keyword">this</span>.name = updatedProduct.name;
    <span class="hljs-keyword">this</span>.price = updatedProduct.price;
    <span class="hljs-keyword">this</span>.quantity = updatedProduct.quantity;
  }

  <span class="hljs-function"><span class="hljs-keyword">public</span> ProductDTO <span class="hljs-title">convertToDTO</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">return</span> ProductDTO.builder()
        .id(<span class="hljs-keyword">this</span>.id)
        .name(<span class="hljs-keyword">this</span>.name)
        .price(<span class="hljs-keyword">this</span>.price)
        .quantity(<span class="hljs-keyword">this</span>.quantity)
        .imageUrl(<span class="hljs-keyword">this</span>.imageLocation)
        .build();
  }
}
</code></pre>
<p>untuk dua method di atas <code>updateProduct</code> dan <code>convertToDTO</code> sudah dibuat unit testnya namun ketika cek coverage reportnya, cukup mengesankan.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710908026298/a2832d68-f928-42a4-806a-10a4f91ffc3e.jpeg" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710823224508/238d2d5e-1234-4592-8a3b-f39f9ec7809e.png" alt class="image--center mx-auto" /></p>
<p>Coveragenya tidak sampai <code>50%</code>, untuk mengatasi problem coverage ini ada beberapa cara yang bisa dilakukan</p>
<ol>
<li><p>Mengexclude class model tersebut dari coverage report dari project</p>
</li>
<li><p>Mengexclude generated code dari <code>Lombok</code></p>
</li>
</ol>
<p>Yang nomor 1 kurang disarankan karena kadang class model juga memiliki bisnis prosessnya sendiri, tidak hanya <code>setter</code>, <code>getter</code>, dan antek-anteknya saja.</p>
<p>Pada Lombok versi <code>1.14</code> diperkenalkan fitur lombok configuration system yang mana developer dapat mengkonfigurasi fitur lombok pada proyek atau workspace, biasanya dilakukan dengan menambahkan <code>lombok.config</code> pada root project, untuk apa saja yang bisa dikonfigurasi bisa dilihat di official Lombok projectnya <a target="_blank" href="https://projectlombok.org/features/configuration">https://projectlombok.org/features/configuration</a>.</p>
<p>Dari semua pilihan konfigurasi pada official websitenya, ada satu configurasi yang membatu untuk mengexclude generated code yaitu <code>lombok.addLombokGeneratedAnnotation = true</code> , dengan menambahakan configurasi ini semua code yang digenerate oleh Lombok akan memiliki annotation <code>@Generated</code> , dengan adanya annotation ini <code>Jacoco</code> akan mengabaikan code terkait pada coverage.</p>
<pre><code class="lang-java"><span class="hljs-meta">@Generated</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">hashCode</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">int</span> PRIME = <span class="hljs-keyword">true</span>;
    <span class="hljs-keyword">int</span> result = <span class="hljs-number">1</span>;
    result = result * <span class="hljs-number">59</span> + <span class="hljs-keyword">this</span>.getId();
    result = result * <span class="hljs-number">59</span> + <span class="hljs-keyword">this</span>.getPrice();
    result = result * <span class="hljs-number">59</span> + <span class="hljs-keyword">this</span>.getQuantity();
    Object $name = <span class="hljs-keyword">this</span>.getName();
    result = result * <span class="hljs-number">59</span> + ($name == <span class="hljs-keyword">null</span> ? <span class="hljs-number">43</span> : $name.hashCode());
    Object $imageLocation = <span class="hljs-keyword">this</span>.getImageLocation();
    result = result * <span class="hljs-number">59</span> + ($imageLocation == <span class="hljs-keyword">null</span> ? <span class="hljs-number">43</span> : $imageLocation.hashCode());
    <span class="hljs-keyword">return</span> result;
 }
</code></pre>
<p>tanpa configurasi <code>lombok.addLombokGeneratedAnnotation = true</code></p>
<pre><code class="lang-java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">hashCode</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">int</span> PRIME = <span class="hljs-keyword">true</span>;
    <span class="hljs-keyword">int</span> result = <span class="hljs-number">1</span>;
    result = result * <span class="hljs-number">59</span> + <span class="hljs-keyword">this</span>.getId();
    result = result * <span class="hljs-number">59</span> + <span class="hljs-keyword">this</span>.getPrice();
    result = result * <span class="hljs-number">59</span> + <span class="hljs-keyword">this</span>.getQuantity();
    Object $name = <span class="hljs-keyword">this</span>.getName();
    result = result * <span class="hljs-number">59</span> + ($name == <span class="hljs-keyword">null</span> ? <span class="hljs-number">43</span> : $name.hashCode());
    Object $imageLocation = <span class="hljs-keyword">this</span>.getImageLocation();
    result = result * <span class="hljs-number">59</span> + ($imageLocation == <span class="hljs-keyword">null</span> ? <span class="hljs-number">43</span> : $imageLocation.hashCode());
    <span class="hljs-keyword">return</span> result;
}
</code></pre>
<p>Coverage result pada <a target="_blank" href="http://Product.java"><code>Product.java</code></a> setelah menambahkan <code>lombok.addLombokGeneratedAnnotation = true</code> pada <code>lombok.config</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710823209609/63b42cb1-3f4d-4285-8183-c63263ee76b2.png" alt class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[Unit test pada spring boot kontroller]]></title><description><![CDATA[Sebelum bahas lebih jauh tentang unit test pada kontroler pada Springboot lebih enak kalau bahas apa itu kontroler, secara umum kontroler adalah bagian dari aplikasi yang menangani http routing pada aplikasi untuk menerima dan mengirim response pada ...]]></description><link>https://rezapambudi.dev/unit-test-pada-spring-boot-kontroller</link><guid isPermaLink="true">https://rezapambudi.dev/unit-test-pada-spring-boot-kontroller</guid><category><![CDATA[Springboot]]></category><category><![CDATA[Java]]></category><category><![CDATA[unit testing]]></category><category><![CDATA[controllers]]></category><dc:creator><![CDATA[Reza Agung Pambudi]]></dc:creator><pubDate>Wed, 28 Feb 2024 07:57:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/EWLHA4T-mso/upload/4ea54ec8b1b5109f58ac566292d417a8.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Sebelum bahas lebih jauh tentang unit test pada kontroler pada Springboot lebih enak kalau bahas apa itu kontroler, secara umum kontroler adalah bagian dari aplikasi yang menangani http routing pada aplikasi untuk menerima dan mengirim response pada sebuah permintaan. Pada Spring boot biasanya sebuah class yang merupakan kontroler akan memiliki anotasi <code>@RestController</code></p>
<p>Anggaplah kita memiliki sebuah kontroler question yang akan mengembalikan semua list pertanyaan yang mana pada kontroler tersebut memiliki sebuah service question. Berikut adalah contoh classnya</p>
<pre><code class="lang-java"><span class="hljs-keyword">import</span> java.util.List;
<span class="hljs-keyword">import</span> org.springframework.beans.factory.annotation.Autowired;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.GetMapping;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RequestMapping;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RestController;

<span class="hljs-meta">@RestController</span>
<span class="hljs-meta">@RequestMapping(path = "/questions")</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">QuestionController</span> </span>{
  <span class="hljs-meta">@Autowired</span>
  QuestionService questionService;

  <span class="hljs-meta">@GetMapping(produces = "application/json")</span>
  <span class="hljs-function"><span class="hljs-keyword">public</span> List&lt;Question&gt; <span class="hljs-title">fecthAllQuestions</span><span class="hljs-params">()</span></span>{
    <span class="hljs-keyword">return</span> questionService.getAll();
  }

}
</code></pre>
<p>Bagaimana sebaiknya unit test yang tepat untuk class kontroler ini? Umunya untuk melakukan unit test untuk kontroler adalah dengan melakukan simulasi seolah-olah ada request dari Internet yang melakukan request get pada routing <code>/questions</code> yang kemudian aplikasi akan mengirim response list object question. Bearti apakah perlu untuk mengirim request dari brower?, tentu tidak, ide ini bisa disimpan ketika membutuhkan E2E test.</p>
<p>Pada Springboot terdapat class <code>MockMvc</code> dan <code>WebMvcTest</code> yang digunakan untuk menguji dan mensimulasikan perilaku aplikasi tanpa perlu mendeploy aplikasi ke server. dan memungkinkan untuk mengirim permintaan HTTP ke aplikasi tanpa browser dan memeriksa respons yang dihasilkan, serta melakukan verifikasi perilaku kontroler yang dibuat.</p>
<p>Mari kita mulai langkah demi langkah untuk membuat unit testnya, dimulai dari line kode dibawah sebagai dasar pembuatan unit testnya</p>
<pre><code class="lang-java"><span class="hljs-meta">@WebMvcTest(QuestionController.class)</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">QuestionControllerTest</span> </span>{

    <span class="hljs-meta">@Mock</span>
  QuestionService questionService;

  <span class="hljs-meta">@Autowired</span>
  <span class="hljs-keyword">private</span> MockMvc mockMvc;

  <span class="hljs-meta">@Test</span>
  <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">fetchShouldReturnAllQuestionsList</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception </span>{
    <span class="hljs-keyword">this</span>.mockMvc.perform(get(<span class="hljs-string">"/questions"</span>))
        .andExpect(status().isOk());
  }
}
</code></pre>
<p>Pada kode diatas <code>@WebMvcTest(QuestionController.class)</code> Anotasi ini digunakan untuk pengujian fungsionalitas pada kontroler tanpa harus menginisiai seluruh konteks aplikasi, sehingga pengujian menjadi lebih fokus dan cepat, simplenya class ini digunakan untuk menjalankan class controller kita, dan <code>mockMvc</code> anggaplah seperti service yang akan digunakan untuk mengirim request ke dummy controller yang sudah dibuat. Pada method test <code>fetchShouldReturnAllQuestionsList</code> kita membuat unit test <code>mockMvc</code> untuk mengirim request <code>GET</code> ke kontroller yang sudah dibuat sebelumnya yang mana berhapakan mendapatakn response OK (<code>HTTP status code 200</code>). Test diatas masih kurang komprehensif karena hanya melakukan checking pada http status code tapi tidak dengan response bodynya.</p>
<p>Pada case sperti ini akan lebih simple ketika kita memiliki sample response dalam file berupa <code>.json</code> dibanding membuat object pada unit testnya disamping lebih mudah, maintenancenya pun akan lebih simple ketika jumlah unit testnya terus bertambah. Buatlah sebuah file berupa <code>sampleQuestionResponse.json</code> pada <code>test/resources/fixtures/question/</code></p>
<pre><code class="lang-json">[
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-string">"65d8d8e73b429a2b1dacbb37"</span>,
    <span class="hljs-attr">"type"</span>: <span class="hljs-string">"SINGLE"</span>,
    <span class="hljs-attr">"question"</span>: <span class="hljs-string">"1 + 1 ="</span>,
    <span class="hljs-attr">"options"</span>: [
      {
        <span class="hljs-attr">"a"</span>: <span class="hljs-string">"0"</span>
      },
      {
        <span class="hljs-attr">"b"</span>: <span class="hljs-string">"1"</span>
      },
      {
        <span class="hljs-attr">"c"</span>: <span class="hljs-string">"2"</span>
      },
      {
        <span class="hljs-attr">"d"</span>: <span class="hljs-string">"3"</span>
      }
    ]
  }
]
</code></pre>
<p>kemudian unit test sebelumnya bisa diupdate degan menambahkan <code>resource</code> path dan <code>ObjectMapper</code> untuk melakukan mapping dari json menjadi object.</p>
<pre><code class="lang-java"><span class="hljs-meta">@WebMvcTest(QuestionController.class)</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">QuestionControllerTest</span> </span>{

  <span class="hljs-meta">@Autowired</span>
  MockMvc mockMvc;

  <span class="hljs-meta">@MockBean</span>
  <span class="hljs-keyword">private</span> QuestionService questionService;

  <span class="hljs-meta">@Value("classpath:fixtures/question/sampleQuestionResponse.json")</span>
  <span class="hljs-keyword">private</span> Resource responseSample;

  ObjectMapper mapper = <span class="hljs-keyword">new</span> ObjectMapper();

  <span class="hljs-meta">@Test</span>
  <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">fetchShouldReturnAllQuestionsList</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception </span>{
    mockMvc.perform(get(<span class="hljs-string">"/questions"</span>))
        .andExpect(status().isOk()).andExpect(content().json(<span class="hljs-string">"[]"</span>));
  }
}
</code></pre>
<p>untuk implementasinya diperlukan untuk melakukan load json fixture tersebut, dan menampungnya pada sebuah variable, tidak lupa untuk melakukan mock pada <code>questionService</code> dengan mengconvert json yang sudah dibaca menjadi object karena jika tidak di mock, secara default service tersebut akan akan megembalikan list kosong.</p>
<pre><code class="lang-java"><span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">fetchShouldReturnAllQuestionsList</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception </span>{
    String sampleResponse = responseSample.getContentAsString(StandardCharsets.UTF_8);
    TypeFactory typeFactory = mapper.getTypeFactory();
    List&lt;Question&gt; items = mapper.readValue(sampleResponse, typeFactory.constructCollectionType(List.class, Question.class));
    when(questionService.getAll()).thenReturn(items);

    mockMvc.perform(get(<span class="hljs-string">"/questions"</span>))
        .andExpect(status().isOk()).andExpect(content().json(sampleResponse));
 }
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Java - LocalDate dan LocalDateTime]]></title><description><![CDATA[Pada Java untuk membuat tipe data berformat tanggal dan waktu bisa menggunakan package java.time yang mana tipe data ini mulai diperkenalan pada java versi 8 yang mana untuk format tanggal dan waktu ini mengikuti standar sistem kalender ISO-8601. Umu...]]></description><link>https://rezapambudi.dev/java-localdate-dan-localdatetime</link><guid isPermaLink="true">https://rezapambudi.dev/java-localdate-dan-localdatetime</guid><category><![CDATA[Java]]></category><dc:creator><![CDATA[Reza Agung Pambudi]]></dc:creator><pubDate>Thu, 22 Feb 2024 17:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/oWUx0ON3EVc/upload/4192c01c988b0073eb169c1e0579b9b3.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Pada Java untuk membuat tipe data berformat tanggal dan waktu bisa menggunakan package <code>java.time</code> yang mana tipe data ini mulai diperkenalan pada java versi 8 yang mana untuk format tanggal dan waktu ini mengikuti standar sistem kalender <code>ISO-8601</code>. Umumnya pada pakcage <code>java.time</code> ada 2 jenis package yang sering digunakan yaitu</p>
<p><code>java.time.LocalDate</code> seperti namanya <code>LocalDate</code> hanya dapat digunakan untuk menyimpan data berupa tanggal (Tahun, bulan dan hari) (yyyy-mm-dd) dan harus diingat tipe data ini sifatnya immutable.</p>
<p>berikut adalah contoh penggunaannya.</p>
<pre><code class="lang-java"><span class="hljs-comment">// untuk membuat date format</span>
LocalDate date = LocalDate.of(<span class="hljs-number">2007</span>, <span class="hljs-number">12</span>, <span class="hljs-number">3</span>);
<span class="hljs-comment">// untuk menampikan tanggal sekarang</span>
LocalDate now = LocalDate.now();

<span class="hljs-comment">// membandingkan</span>
LocalDate date1 = LocalDate.of(<span class="hljs-number">2007</span>, <span class="hljs-number">12</span>, <span class="hljs-number">3</span>);
LocalDate date2 = LocalDate.of(<span class="hljs-number">2007</span>, <span class="hljs-number">12</span>, <span class="hljs-number">4</span>);

date1.isBefore(date2); <span class="hljs-comment">// =&gt; true</span>
date1.isAfter(date2); <span class="hljs-comment">// =&gt; false</span>

<span class="hljs-comment">//instance ini juga memiliki setter getternya sendiri</span>
now.getDayOfMoth(); <span class="hljs-comment">// =&gt; mengembalikan tanggal</span>
now.addDays(<span class="hljs-number">3</span>); <span class="hljs-comment">// =&gt; menambahkan hari ini + 3</span>
</code></pre>
<p><code>java.time.LocalDateTime</code> seperti namanya LocalDateTime ini seperti Tanggal yang membedakannya tipe data ini dapat menyimpan waktu dari jam, menit hingga detik.</p>
<p>Berikut adalah contoh penggunnaannya</p>
<pre><code class="lang-java"><span class="hljs-comment">// membuat instance</span>
LocalDateTime dateTime = LocalDateTime.of(<span class="hljs-number">2024</span>, <span class="hljs-number">1</span>, <span class="hljs-number">14</span>, <span class="hljs-number">20</span>, <span class="hljs-number">10</span>, <span class="hljs-number">01</span>);
<span class="hljs-comment">// untuk menampikan tanggal dan waktu sekarang</span>
LocalDateTime now = LocalDateTime.now();
<span class="hljs-comment">// konversi LocalDate ke LocalDateTime</span>
LocalDate datenow = LocalDate.of(<span class="hljs-number">2024</span>, <span class="hljs-number">01</span>, <span class="hljs-number">14</span>);
LocalDateTime dateTimeToday = datenow.at(<span class="hljs-number">10</span>, <span class="hljs-number">10</span>, <span class="hljs-number">10</span>);
dateTimeToday.toString() <span class="hljs-comment">// =&gt; 2024-01-14T10:10:10</span>
</code></pre>
<h3 id="heading-melakukan-formatting-pada-kedua-instance"><strong>Melakukan formatting pada kedua instance</strong></h3>
<p>Untuk format date time juga dapat dilakukan pembuatan instance dari dan ke string.</p>
<pre><code class="lang-java">LocalDateTime datetime = LocalDateTime.of(<span class="hljs-number">2024</span>, <span class="hljs-number">01</span>, <span class="hljs-number">14</span>, <span class="hljs-number">10</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
LocalDateTime parsed = LocalDateTime.parse(<span class="hljs-string">"2024-01-14T10:00:00"</span>);

dateTime.equal(parsed); <span class="hljs-comment">// =&gt; true</span>

<span class="hljs-comment">// dibuat dengan formatter</span>
DateTimeFormatter parser = DateTimeFormatter.ofPattern(<span class="hljs-string">"dd/MM/yyyy"</span>);
LocalDate date = LocalDate.parse(<span class="hljs-string">"14-01-2024"</span>, parser);

DateFormatter printer = DateTimeFormatter.ofPattern(<span class="hljs-string">"MMMM d, yyyy"</span>);
printer.format(date); <span class="hljs-comment">// =&gt; "January 14, 2024</span>

<span class="hljs-comment">//bisa juga dilakukan degan custom</span>
LocalDate now = LocalDate.now();
DateFormatter printer = DateTimeFormatter.ofPattern(<span class="hljs-string">"'sekarang adalah hari 'EEEE d, yyyy"</span>);

printer.format(now);
</code></pre>
]]></content:encoded></item></channel></rss>