Memulai Java Enterprise Dengan Spring Boot
Memulai Java Enterprise Dengan Spring Boot
Sebelum ada Spring, sebenarnya ada tools untuk pemrograman enterprise yaitu Java Enterprise atau
JEE pada tahun 2000. Namun, ketika membuat aplikasi menggunakan J2EE sangatlah tidak mudah dan
sangat rumit. J2EE sangat tightly coopling sehingga tidak memungkinkan untuk melakukan unit testing
dengan koneksi eksternal misalnya koneksi ke database. Bahkan, untuk menguji fitur sederhana
sekalipun harus menggunakan / mengerahkan seluruh aplikasi dalam sebuah container. Karena hal
seperti itu lah J2EE mulai banyak ditinggalkan oleh programmer Java khususnya programmer Java di
era tahun 2000 an.
Dalam buku ini akan dibahas mengenai, pengenalan Java Enterprise menggunakan Spring Boot dari
fundamental sampai tingkat lanjut. Topik-topik yang akan dibahas dalam buku ini adalah :
Semoga ebook ini sedikitnya dapat membantu teman-teman dalam mempelajari Spring Boot dasar.
Teten Nugraha
Daftar Isi
Kata Pengantar........................................................................................................................................ 2
Daftar Isi .................................................................................................................................................. 3
Konsep Object Oriented Programming ................................................................................................... 6
Tools yang digunakan.............................................................................................................................. 6
1. JDK (Java Development Kit) dan setting JAVA_HOME ................................................................ 6
2. Maven 3 .................................................................................................................................... 10
3. MySQL Database ....................................................................................................................... 12
4. IntelliJ IDEA Community IDE ..................................................................................................... 12
5. Spring Tool Suite ....................................................................................................................... 13
6. Postman REST Api Client ........................................................................................................... 13
7. DBeaver ..................................................................................................................................... 14
Konsep Dependency Injection ............................................................................................................... 15
Pengenalan Spring Framework ............................................................................................................. 17
Java Enterprise Edition...................................................................................................................... 17
Spring Framework ............................................................................................................................. 17
Spring Modules ................................................................................................................................. 18
Spring Projects .................................................................................................................................. 20
Pengenalan Spring Boot ........................................................................................................................ 22
Java Based Configurations ................................................................................................................ 22
XML Configuration VS Java Configuration ........................................................................................ 22
Membuat Projek di Spring Boot............................................................................................................ 24
Spring Data JPA ..................................................................................................................................... 26
Pengenalan Spring Data JPA ............................................................................................................. 26
Membuat Projek Spring Data JPA ..................................................................................................... 26
Membuat Entity Class ....................................................................................................................... 28
Membuat JPA Properties dan Hikari Connection Pool ..................................................................... 29
Eksekusi Query Method .................................................................................................................... 31
Eksekusi Native Query....................................................................................................................... 35
One to Many Relationship ................................................................................................................ 37
Many to Many Relationship .............................................................................................................. 43
Spring Web + Thymeleaf ....................................................................................................................... 48
Membuat RESTful WebService ............................................................................................................. 53
Membuat Product Model ................................................................................................................. 54
Database dan JPA Properties ............................................................................................................ 56
Membuat Product Repository .......................................................................................................... 57
Membuat Product Service ................................................................................................................ 57
Membuat Product Controller ........................................................................................................... 59
Spring Security ...................................................................................................................................... 66
Basic Authentication ......................................................................................................................... 67
Unit test dengan JUnit dan Mockito ..................................................................................................... 71
Tambahkan Plugin Unit Test , Commons dan Mapper ..................................................................... 71
Membuat Data Fake Generator ........................................................................................................ 74
Unit Testing Service Layer ................................................................................................................. 75
Testing findAll ............................................................................................................................... 76
Testing getProductById ................................................................................................................. 77
Testing getProductByIdWithNullDataFromDB .............................................................................. 78
Testing saveOrUpdateProduct ...................................................................................................... 79
Testing deleteProduct ................................................................................................................... 80
Unit Testing Controller Layer ............................................................................................................ 83
Testing testSaveOrUpdateProduct ............................................................................................... 84
Testing testGetAllProducts ........................................................................................................... 85
Testing getOneProduct ................................................................................................................. 85
Testing testDeleteProduct ............................................................................................................ 86
Generate Code Coverage dengan Jacoco dan Sonarqube .................................................................... 90
Apa itu Codecoverage ? .................................................................................................................... 90
Jacoco and SonarQube...................................................................................................................... 90
Setting WebService integrasi dengan Jacoco dan Sonarqube .......................................................... 91
Start Local Server Sonarqube ........................................................................................................ 91
Tambahkan Jacoco Plugin pada file Pom.xml ............................................................................... 93
Integrasi Jacoco dan Sonarqube ................................................................................................... 97
Spring AOP (Abstract Oriented Programming) ................................................................................... 101
Pengertian AOP ............................................................................................................................... 101
Membuat LoggingAspect ................................................................................................................ 101
Deploy Webservice menggunakan Docker dan Docker-compose ...................................................... 105
Mengunduh Docker ........................................................................................................................ 105
Menyiapkan Image untuk Deploy Aplikasi...................................................................................... 107
Proses Deploy Aplikasi .................................................................................................................... 108
Merubah Profile Aplikasi ............................................................................................................. 108
Membuat Docker Network ......................................................................................................... 109
Membuat File .sh untuk Container DependOn ........................................................................... 110
Membuat Dockerfile ................................................................................................................... 111
Membuat Docker-compose ........................................................................................................ 115
Test Dengan Postman Client ....................................................................................................... 117
Tentang Penulis ................................................................................................................................... 120
Daftar Pustaka..................................................................................................................................... 121
Konsep Object Oriented Programming
Berikut adalah beberapa konsep OOP (Object Oriented Programming) yang dimiliki Java :
- Class merupakan sebuah kerangka/model (blueprint) atau bentuk awal (prototype) yang
berfungsi untuk menaruh dan mendeskripsikan perilaku (behavior) dsari sebuah objek nantinya.
Penamaan nama class deprogram harus sama dengan struktur file extension .java.
- Object merupakan sebuah representasi dari instance dari Class. Object adalah sebuah inti dan
wujud real dari sebuah Class. Object didefinisikan sebagai state dan behavior dari Class.
- Attribute merupakan sebuah unsur data yang ada di class, atribut biasanya terdiri dari sebuah
data, variable, property dan field. Atribut bisa juga disebut state dari object tersebut. Missal
Manusia mempunyai atribut nama, tinggi, berat dan Jenis Kelamin
- Method merupakan perilaku dari sebuah class, misalkan untuk class Manusia mempunyai
method: Berjalan, Berbicara, Tidur.
- Encapsulation adalah suatu memkanisme membungkus suatu data (variable) agar tidak dapat
diakses oleh class lain, dengan menggunakan modifier private atau protected. Didalam konsep
ini beberapa variable akan disembunyikan oleh class lain dan hanya bisa diakses di main Class
dengan menggunakan method yang mempunyai modifier public.
- Inheritance (Pewarisan) adalah suatu proses dimana, suatu class yang bsia disebut super class
dapat mewarisi sifat atau ciri-ciri seperti atribut dan method ke dalam class turunannya yaitu sub
class. Super class akan mewarisi nilai dan atribut atau behavior dari class turunannya .
- Polymorphisme adalah suatu kemampuan yang dimiliki oleh sebuah method dengan nama
yang sama tapi dengan perilak yang berbeda-beda.
Pada situs tersebut kalian bisa memilih versi 8, 11 atau 13 untuk JDK yang akan
digunakan. Tapi dalam buku ini menggunakan Microsoft Windows 10 64 bit dengan
versi JDK 11.
- Install OpenJDK
Karena AdoptOpenJDK berbasis GUI, maka user dapat dengan mudah melakukan
proses instalasi nya :
a. Masuk ke direktori tempat mengunduh JDK kemudian double klik
b. Baca dan setujui prasayat
c. Dalam Custom Setup, kalian bisa memilih tempat dimana jdk akan diinstall. Tapi
secara default, system akan menyimpan pada direktori c:\Program
Files\AdoptOpenJDK\<package>
d. Klik Install dan Tunggu sampai instalasi beres
e. Masukan Path JAVA_HOME pada local computer anda sesuai dengan direktori
tempat instalasi OpenJDK
java -version
Maven adalah sebuah build tools yang berdasarkan pada POM (Project Object Model) digunakan untuk
mem-build, deploy dan dokumentasi semua project yang berbasiskan Java. Dengan Maven, banyak
memudahkan pekerjaan programmer Java.
a. Mengunduh Maven
Untuk mengunduh maven, kalian bisa langsung mengunduh nya di situs resmi maven yaitu
https://maven.apache.org/ . Disitus tersebut terdapat banyak versi Maven yang bisa
digunakan. Namun penulis menggunakan versi Maven 3.6.2
Maven tersedia dalam paket yang sudah dikompresi (.tar.gz / zip). Silahkan unduh
menggunakan versi .zip nya
Kemudian unzip paket maven yang sudah diunduh ke dalam sebuah folder.
Masuk ke Environtment Variables (Seperti yang dilakukan pada saat setting PATH Java Home).
Pada bagian System Variables buat sebuah variable baru dengan nama MAVEN_HOME dengan
nilai yaitu direktori tempat maven disimpan difolder diatas.
Kemudian klik Ok, dan masukan MAVEN_HOME yang sudah dibuat ke dalam Path system variables
sama seperti pada saat melakukan instalasi JAVA_HOME. Jika selesai maka kita akan test apakah
maven nya sudah berjalan di service dengan mengetikan sintak berikut pada command prompt.
mvn -v
3. MySQL Database
Pada buku ini kita menggunakan MySQL Database untuk penyimpanan datanya. MySQL adalah engine
database yang bersifat RDBMS (Relational Database Management System) yang biasanya digunakan
oleh banyak programmer baik untuk development atau bahkan ada yang sudah dipakai di level
production.
MySQL AB membuat MySQL tersedia gratis untuk komunitas dibawah lisensi GNU General Public
License, tetapi mereka juga membuat versi yang premium atau komersial untuk kasus-kasus
penggunanya tidak cocok dengan GPL.
Untuk IDE (Integrated Development Environtment), bisa menggunakan IntelliJ IDEA. Menurut penulis,
IDE ini merupakan salah satu IDE yang paling baik untuk membuat program khususnya berbasis Java.
Karena sangat cepat, ringan, memiliki tools-tools yang sangat digunakan oleh programmer Java tetapi
menggunakan memory yang sangat banyak. Terdapat dua versi, yaitu versi Commercial dan versi
Community. Kalian bisa langsung mengunduh disitus resmi nya
https://www.jetbrains.com/idea/download/ .
5. Spring Tool Suite
Jika kalian terbiasa menggunakan IDE Eclipse dalam membuat program Java. Saya sarankan untuk
menggunakan IDE ini. STS atau kepanjangan dari Spring Tool Suite adalah sebuah Eclipse IDE yang di
modifikasi atau di desain khusus untuk framework Spring.
Untuk lisensi, IDE ini adalah Opensource jadi kita tidak perlu khawatir mengenai lisensi dan bisa dipakai
sampai kapanpun tanpa ada batasan waktu. Untuk mengunduhna dapat mengunjungi laman resmi
nya di https://spring.io/tools .
Postman adalah sebuah perangkat lunak atau tools yang digunakan untuk melakukan uji coba RestAPI
dari aplikasi yang sedang kita buat. Karena di bab selanjutnya postman akan digunakan ketika
membangun webservice berbasis Rest API.
Postman dapat kalian unduh dalam laman resmi nya disini https://www.postman.com/postman.
7. DBeaver
DBeaver adalah salah satu SQL Client yang bisa digunakan untuk manajemen database dari aplikasi
yang sedang kita bangun. Mendukung multiplatform database yaitu MySQL, Postgresql, Oracle,
SQLServer, DB2, SyBase, MS Access, Teradata, Firebird, Apache Hive dan lain-lain.
Software ini bersifat open source dan kalian bisa mengunduh langsung dilaman resmi nya yaitu
https://dbeaver.io/download/.
Konsep Dependency Injection
Jika kita berbicara mengenai Spring Framework maka tidak akan lepas dengan apa yang Namanya
Dependency Injection atau yang disebut juga Inversion of Control. Dijelaskan bahwa Dependency
Injection itu adalah :
IoC is also know as dependency injection (DI). It is a process whereby object define their
dependencies, that is, the other objects they work with, only through constructor arguments,
arguments to a factory method, or properties that are set on the object instance after it is
contructed or returned from a factory method. The container then injects those dependencies when
it creates the bean. This is process is fundamentally the inverse, hence the name Inverstion of
Control (IOC), of the bean itself controlling the instantiation or location of its dependencies by using
direct contructor of classes, or a mechanism such as the Service Locator pattern.
Dalam setiap aplikasi java pasti berisi sekumpulan class-class Java, atau yang disebut juga dengan
bean. Dari sinilah muncul istilah Java Bean. Idealnya sebuah bean itu bersifat mandiri dan tidak terikat
pada bean yang lain (depend) atau yang disebut dengan loose coupling sehingga pada Spring sangatlah
menekankan akan hal itu dan kebalikan dari loose coupling itu adalah tight coupling dimana suatu
kondisi bean yang mempunyai ketergantuangan kuat pada bean yang lain.
class Trip {
Car c = new Car();
void startTrip() {
c.go();
}
}
class Car {
void go () {
// logic …
}
}
Jika kita lihat pada bean atau class diatas class Trip memiliki ketergantungan kepada class Car dengan
adanya deklarasi Car c = new Car() . Hal ini lah yang ingin dihindari oleh Spring. Sehingga jika kita ingin
mengubah kode diatas menjadi loose coupling maka kita edit menjadi sebagai berikut .
1. Buatlah sebuah interface karena interface sangat lah mendukung penggunaan sifat loose
coupling karena hanya berisi defisni dan tidak ada bisnis logic.
interface Vehicle () {
void go ();
}
@Override
pulic void go() {
System.out.println(“Go with Bike”);
}
}
3. Edit class Trip yang memiliki sifat tight coupling dengan mendeklarasikan interface Vechile
dan bukan dari class implementasinya. Dan buatlah setter untuk menginisiasi interface
tersebut.
class Trip {
Vehicle vehicle;
void startTrip() {
v.go();
}
Pada method setVehicle diatas itu kita menempatkan atau menginject class Car atau Bike nya
sehingga program diatas sudah bisa dikatakan sebagai potongan program yang loose coupling.
Pengenalan Spring Framework
Sebelum ada Spring, sebenarnya ada tools untuk pemrograman enterprise yaitu Java Enterprise atau
JEE pada tahun 2000. Namun, ketika membuat aplikasi menggunakan J2EE sangatlah tidak mudah dan
sangat rumit. J2EE sangat tightly coopling sehingga tidak memungkinkan untuk melakukan unit testing
dengan koneksi eksternal misalnya koneksi ke database. Bahkan, untuk menguji fitur sederhana
sekalipun harus menggunakan / mengerahkan seluruh aplikasi dalam sebuah container. Karena hal
seperti itu lah J2EE mulai banyak ditinggalkan oleh programmer Java khususnya programmer Java di
era tahun 2000 an.
Spring Framework
a. Pengembangan Cepat
Spring Framework menawarkan kecepatan dalam membangun aplikasi menggunakan
Java, karena sudah built-in dengan tomcat, sehingga pemrogram bisa langsung mendebug
pada laptop nya tanpa perlu deploy terlebih dahulu ke server.
Spring Modules
Spring mempunyai banyak fitur, semua nya itu di bungkus dalam 20 module yang digroup kan dalam
diagram dibawah ini.
- Core Container
Dalam layer Core Container ini terbagi dua, yaitu :
a. Core and Beans
Sesuai dengan namanya, ini adalah inti dari Spring Framework sendiri. Modul ini menyediakan
IoC atau DI / Dependency Injection dan menyediakan implementasi factory pattern yang canggih
menggunakan BeanFactory.
b. Context
Berisi dukungan untuk beberapa fitur Java EE seperti EJB (Enterprise Java Beans), JMX (Java
Management Extension) dan dukungan layanan remote.
1. JDBC
Atau yang disebut dengan Java Database Connectivity adalah sebuah API (Application
Programming Interface) yang memungkinkan program java dapat berkomunikasi dengan
Relasional Database.
2. ORM
Object Relational Mapping adalah sebuah teknik pemrograman yang memetakan sebuah object
dengan database sehingga dapat berkomunikasi menggunakan paradigm Object Oriented
Programming.
Banyak sekali produk-produk ORM, namun yang sering banyak dipakai sekarang yaitu JPA,
Hibernate dan MyBatis. Dari setiap produk tersebut mempunyai keunggulan masing-masing
tergantung dengan kebutuhan proses bisnis yang akan dibuat nantinya.
3. OXM
Menyediakan layer abstraksi untuk menggunakan sebuah nilai dari implementasi Object/XML
seperti JAXB, Castor, XMLBeans, JiBX, XStream.
4. JMS
Menyediakan layanan untuk mendukung pengiriman pesan (messaging) menggunakan teknologi
JMS (Java Messaging Service). Dengan menggunakan JMS seorang programmer dapat melakukan
komunikasi pesan dari MQSeries IBM, SonicMQ dan beberapa produk messaging lainnya. Selain
itu, JMS mendukung pesan yang berisi objek Java dan pesan yang berisi halaman Extensible
Markup Language (XML).
5. Transaction
Menyediakan abstraksi yang konsisten untuk manajemen transaksi tidak hanya untuk class
yang mengimplementasi special interface tapi untuk semua POJOs (plain old Java Object).
- Web
Dalam layer web ini terdiri dari tiga bagian yang terpisah yaitu, Spring Web, Web Servlet dan Web
Portlet modul.
Spring Web, modul ini menyediakan keperluan dan fungsionalitas dasar dari web seperti upload
data modul ini juga berelasi dengan dukungan layanan jarak jauh Spring (Spring remoting support).
Web Servlet, jika kalian sudah menggunakan pola MVC (Model View Controller) dalam sebuah
framework maka hal ini juga di dukung oleh Web Servlet. Web Servlet mendukung pola MVC sehingga
menyediakan sebuah pemisah yang sangat bersih antara kode untuk Tampilan (VIew), logic dari
aplikasi (Controller) dan layer yang berhubungan dengan database (Model).
- AOP
Adalah singkatan dari Aspect Oriented Programming. AOP lebih menekankah pada cross cutting
concern yaitu seperti fitur authentication, logging, security dan lain-lain. Untuk AOP akan kita bahas
lebih lanjut pada bab tersendiri.
- Test
Modul ini mendukung bahwa semua Spring Component bisa menggunakan JUnit atau TestNG.
Spring Projects
Disamping Spring Modules, Spring juga mengembangkan beberapa projek yang dapat
membantu programmer dalam membuat aplikasi enterprise . Salah satu projek nya yaitu
Spring Boot yang akan kita gunakan dalam buku ini .
- Spring Cloud
Spring Cloud adalah sebuah framework aplikasi cloud yang sangat handal. Framework ini
menyediakan apa saja yang dibutuhkan untuk membuat aplikasi dalam lingkungan
terdistribusi (distributed environtment).
Untuk buku versi ke-2 mengenai Microservices, akan banyak menggunakan fitur Spring
Cloud ini.
- Spring Data
Misi dari Spring Data adalah menyediakan model pemrograman yang berbasis Spring yang
konsisten tapi tetap mempertahankan sifat-sifat khusus dari manipulasi data.
Memberikan kemudahan dalam mengatur data yang bersifat relational dan non-
relational. Projek Spring Data ini dikembangkan dengan bekerja sama dengan beberapa
perusahaan dan para pengembang.
- Spring Integration
Turunan dari model pemrograman Spring atau yang lebih dikenal dengan Enterprise
Integration Pattern. Misi projek ini adalah menyediakan model yang sederhana untuk
mengintegrasikan solusi enterprise sambil mempertahankan separation of concern untuk
mengahasilkan kode yang dapat dipertahankan dan dapat diuji.
- Spring Batch
Batch Processing adalah sebuah proses yang melibatkan banyak jobs tanpa interaksi
dengan manusia. Sebuah batch dapat menangani data dalam jumlah besar dan running
dalam waktu yang lama. Spring Batch membantu untuk menangani batch processing
tersebut.
- Spring Security
Sesuai dengan nama nya, projek ini digunakan biasanya untuk menangani Authentication
(siapa yang akan masuk ke sistem) dan Authorization (menu /fitur apa saja yang bisa
diakses oleh seseorang) .
- Spring AMQP
Menyediakan core concept untuk development berbasis AMQP Messaging. Projek ini
terbagi menjadi dua abstraksi yaitu spring-amqp sebagai dasar abstraksi dan spring-rabbit
sebagai implementasi.
- Spring Mobile
Framework yang memberikan solusi untuk mendeteksi device yang melakukan request ke
server.
- Spring Webservice
Adalah salah satu produk dari Spring Community yang focus untuk membuat layanan web
berbasis document-driven.
- Spring LDAP
Projek Spring yang memberikan kemudahan dalam management LDAP (Lighweight
Directory Access Protocol) yang salah satu nya terkait dengan Active Directory.
- Spring Shell
Projek Spring yang memberikan kemudahan programmer dalam mengembangkan aplikasi
berbasis Spring dengan command line yang dapat berkomunikasi langsung dengan REST
API atau bagaimana berinteraksi dengan konten file local.
- Spring Flo
Spring Flo adalah sebuah library yang ditulis dalam bahasa Javascript yang meng-embedd
HTML5 Visual Builder untuk pipeline dan graph yang sederhana.
- Spring Kafka
Jika anda pernah menggunakan product messaging maka tidak asing lagi dengan kafka.
Dengna projek ini spring memberikan komudahan dalam berkomunikasi dengan kafka.
Lebih tepat nya menyediakan semacam template sebagai high level abstraction untuk
mengirim pesan.
Semua project diatas adalah main project dari Spring Framework tapi dalam buku ini, kita tidak
akan membahas semua. Kita focus hanya membahas Spring Framework, Spring Boot, Spring
Data JPA, Spring Web dan Spring Security. Untuk lebih detail nya kalian bisa mempelajarinya
langsung dalam situs ini https://spring.io/projects/.
Pengenalan Spring Boot
Sejak Spring Framework diperkenalkan untuk menjawab permasalahan-permasalahan umum dari Java
Enterprise Edition maka dengan itu perkembangan dan popularitas Spring Framework sangatlah
cepat. Banyak aplikasi yang dibangun menggunakan fondasi Spring khususnya di lingkungan
enterprise. Karena konfigurasi Spring banyak menggunakan XML maka hal itu agak menjadi sebuah
masalah karena pada jaman sekarang Spring harus dapat bersaing dengan framework-framework
yang mengedepankan kecepatan dalam proses development seperti Rails, Laravel atau Django. Untuk
menjawab tantangan itu maka di perkenalkan lah Spring Boot V1.0 pada tahun 2014.
- Sudah menyediakan fleksibilitas untuk mengatur Java Beans, Konfigurasi dan Database
Transaction
- Mendukung batch processing dan mendukung REST endpoint (untuk aplikasi berbasis
webservice API).
- Dalam Spring Boot, semua sudah di auto configuration sehingga tidak perlu melakukan
manual konfigurasi
- Sudah mendukung Annotation Based Konfigurasi menggunakan annotasi
@SpringBootApplication
- Karena sudah terintegrasi dengan Maven, maka dapat dengan mudah mengatur plugin /
dependency aplikasi.
- Embedded Servlet Container sudah tertanam.
Spring Boot banyak menggunakan Java Based Annotations untuk konfigurasi nya untuk menggantikan
pendahulunya yang masih menggunakan XML Based. Berikut annotasi-annotasi berbasis Java yang
sering digunakan dalam Spring Boot:
- @Autowired
- @Service
- @Component
- @Bean
- @Configuration
XML Config
<beans>
<bean id = "helloWorld" class = "id.belajar.HelloWorld" />
</beans>
Java Config
@Configuration
public class HelloWorldConfig {
@Bean
public HelloWorld helloWorld(){
return new HelloWorld();
}
}
Anda bisa bandingkan kode diatas, lebih nyaman menggunakan XML Config atau Java Config.
Membuat Projek di Spring Boot
Spring Initialzr adalah sebuah webtool yang disediakan oleh Spring untuk membantu programmer
dalam membuat projek berbasis spring secara cepat. Didalamnya, kalian juga bisa memilih plugin
apa saja yang akan digunakan nantinya. Bisa diakses langsung ke sini https://start.spring.io/ .
- Membuat projek langsung di dalam IDE nya yaitu di Spring tool Suite atau Intelljidea
- Atau menggunakan Spring Boot CLI
Project, pilih build tools mana yang akan digunakan antara Maven atau Gradle. Karena dalam buku ini
menggunakan maven sebagai build tools nya maka pilihlah Maven Project.
Language, pilih bahasa pemrograman apa yang akan digunakan pilihannya yaitu Java, Kotlin atau
Groovy.
Spring Boot, pilih versi Spring Boot sebagai base versi framework yang akan dibuatkan aplikasi
nantinya.
Project Metadata, simpan informasi projek khususnya tentang Group dan Artifact dari aplikasi.
Dependencies, kalian bisa memilih plugin / dependencies yang diperlukan. Misalnya Spring Web,
Spring Data JPA, Spring Security dan lain-lain.
Ekstrak projek tersebut ke dalam suatu folder kemudian buka IDE (IntelijIDEA atau Spring Tool Suite),
namun saya menggunakan Intelijidea pada buku ini. Biasanya maven akan terlebih dahulu mengunduh
dependency-dependency yang telah dipilih tadi jika di laptop belum terdapat dendency nya. jadi
pastikan laptop kalian sudah terhubung dengan jaringan internet.
Spring Data JPA
Sebelumnya kita bahas apa itu JPA, Hibernate karena kadang-kadang kita suka kebingungan mengenai
dua hal ini. Penulis khususnya, sebelum mengenal JPA, untuk membuat aplikasi yang bisa
berkomunikasi dengan database perlu banyak hal yang harus dilakukan diantaranya membuat koneksi
database, membuat try-catch manual untuk masing-masing method yang mengoperasian CRUD
(Create, Read, Update, Delete) kemudian membuat sintak-sintak SQL secara native dalam class secara
manual dengan menggunakan Statement atau PreparedStatement seakan harus membuat semua nya
itu dari nol.
JPA atau singkatan Java Persistence API adalah sebuah spesifikasi standar bagaimana Java yang
notabene nya adalah Object Oriented Programming dapat berkomunikasi dengan Database khususnya
RDBMS (Relational Database Management System) yang tidak ada kaitannya dengan OOP sehingga
menggunakan JPA kita tidak perlu lagi menyiapkan konfigurasi ke database dari awal seperti di atas
dan kita bisa focus hanya ke dalam class-class yang akan dipetakan ke dalam table-table yang ada di
database karena JPA sudah mengimplementasikan teknologi ORM (Obejct Relational Mapping).
Hibernate adalah sebuah framework salah satu implementasi atau produk dari JPA itu sendiri. Dibuat
oleh Gavin King pada tahun 2002 sebagai sebuah alternative penggunaan entity beans pada layer
persistence. Selain Hibernate ada banyak vendor yang telah mengimplementasikan JPA diantaranya :
- Toplink
- iBatis
- OpenJPA
- Spring Data JPA
- Dan lain-lain
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Dengan plugin ini, kita tidak perlu menambahkan lagi hibernate-core, spring-orm ke dalam projek
secara manual. Semua nya sudah di bundle dalam plugin ini.
pom.xml
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
id.belajar.springdatajpa.entity.Book
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private String writer;
@Column(nullable = false)
private String isbn;
public Book() {
}
@Entity : merepresentasikan bahwa class tersebut adalah sebuah entitas, yang nantinya akan ada
sebuah Tabel di database dengan nama Book
@Id : menunjukan bahwa variable itu adalah sebuah ID atau Primary Key dari suatu table. Penggunaan
@Id di Spring Data JPA sangat lah mandatory /wajib. Setiap class yang diberi annotasi @Entity harus
mempunyai @Id.
@Column(nullable = false) : Memberikan info bahwa variable tersebut yang akan menjadi kolom
di table Book, tidak boleh null atau not null.
src.main.resources.application.properties
#MySQL Connection
spring.datasource.url = jdbc:mysql://localhost:3306/spring-data-jpa
spring.datasource.username = root
spring.datasource.password = root
#HikariCP
spring.datasource.hikari.connection-timeout=20000
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.maximum-pool-size=12
spring.datasource.hikari.idle-timeout=300000
spring.datasource.hikari.max-lifetime=120000
spring.datasource.hikari.auto-commit=true
#JPA Properties
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto=update
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url : adalah alamat dari database MySQL yang sudah diinstall. Default nya
yaitu localhost:3306
Tidak disarankan untuk menggunakan username root dan password root jika aplikasi kalian sudah
release
Jalankan program ini jika tidak terdapat error, kita bisa lihat di DBeaver pada database spring-data-jpa
akan terdapat Book table.
Kita bisa lihat apa yang kita sudah deklarasikan di class Book maka akan di representasikan menjadi
sebuah table. Untuk kolom Id menjadi primary key dengan nilai auto_increment. Dan untuk kolom
isbn, title, writer mempunyai atribut Not Null.
id.belajar.springdatajpa.repository.BookRepository
import id.belajar.springdatajpa.entity.Book;
import org.springframework.data.jpa.repository.JpaRepository;
Dari kode diatas dapat kita lihat bahwa BookRepository adalah turunan JPARepository dimana
JPARepository menyediakan fungsi untuk create, retrieve, update, delete, findAll dan lain-lain sehingga
kita tidak perlu lagi untuk membuat fungsi-fungsi CRUD dari awal. Supaya lebih paham kita akan coba
menyimpan dua buah object Book ke dalam database.
id.belajar.springdatajpa.SpringDataJpaApplication
@SpringBootApplication
public class SpringDataJpaApplication implements CommandLineRunner {
@Autowired
private BookRepository bookRepository;
@Override
public void run(String... args) throws Exception {
bookRepository.save(book1);
bookRepository.save(book2);
}
}
Jika kita jalankan kode diatas, maka dalam console LOG aka nada keterangan mengenai dua object
Book yang sudah kita simpan ke database.
Kemudian kita cek lagi di database, terdapat dua nilai dari table Book.
Disamping method save kita akan coba untuk meretrieve data. Edit kembali file BookRepository dan
tambahkan query methodnya sebagai berikut.
id.belajar.springdatajpa.repository.BookRepository
List<Book> findAll();
Kita ubah kembali class SpringDataJpaApplication dan panggil method findAll diatas.
id.belajar.springdatajpa.SpringDataJpaApplication
@SpringBootApplication
public class SpringDataJpaApplication implements CommandLineRunner {
@Autowired
private BookRepository bookRepository;
@Override
public void run(String... args) throws Exception {
LOG.info("Books : "+books);
}
}
Kita jalankan kode nya, terlihat di consle log terdapat dua buah nilai dari hasil method findAll.
Disamping findAll, kita juga bisa membuat query misalnya untuk mendapatkan data book berdasarkan
penulis atau writer.
id.belajar.springdatajpa.repository.BookRepository
@Override
public void run(String... args) throws Exception {
LOG.info("Books : "+books);
Terakhir, misalkan kita ingin melakukan pencarian data Book berdasarkan nomor ISBN. Nomor ISBN
itu unik dan pasti akan ada satu data Book dengan satu nomor ISBN, oleh karenanya kita buat method
seperti ini.
id.belajar.springdatajpa.repository.BookRepository
id.belajar.springdatajpa.SpringDataJpaApplication
@Override
public void run(String... args) throws Exception {
LOG.info("Book : "+book);
List<Book> findAll();
id.belajar.springdatajpa.repository.BookRepository
@Query(
nativeQuery = true,
value = "select * from book"
)
List<Book> findAllQueryNative();
…
}
id.belajar.springdatajpa.SpringDataJpaApplication
@Override
public void run(String... args) throws Exception {
LOG.info("Book : "+books);
Jika kita running maka dalam log akan ada dua data book yang kita panggil.
nah sekarang bagaimana cara nya jika kita ingin melempar sebuah parameter, katakanlah kita ingin
mendapatkan semua data buku berdasarkan nama penulis ?.
id.belajar.springdatajpa.repository.BookRepository
@Query(
nativeQuery = true,
value = "select * from book where writer = ?1"
)
List<Book> findAllByWriterQueryNative(String writer);
…
}
id.belajar.springdatajpa.SpringDataJpaApplication
@Override
public void run(String... args) throws Exception {
LOG.info("Book : "+books);
id.belajar.springdatajpa.repository.BookRepository
package id.belajar.springdatajpa.repository;
import id.belajar.springdatajpa.entity.Book;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
List<Book> findAll();
@Query(
nativeQuery = true,
value = "select * from book"
)
List<Book> findAllQueryNative();
@Query(
nativeQuery = true,
value = "select * from book where writer = ?1"
)
List<Book> findAllByWriterQueryNative(String writer);
}
Buat Sebuah class BookCategory pada package entity. Class ini nantinya akan menjadi class parent
untuk class Book beserta class BookCategoryRepositorynya.
id.belajar.springdatajpa.entity.BookCategory
package id.belajar.springdatajpa.entity;
import javax.persistence.*;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Entity
public class BookCategory {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(
mappedBy = "bookCategory",
cascade = CascadeType.ALL
)
private List<Book> books;
public BookCategory() {
}
@Override
public String toString() {
return "BookCategory{" +
"id=" + id +
", name='" + name + '\'' +
", books=" + books +
'}';
}
}
Selanjutnya, edit class Book dan tambahkan objek variable BookCategory bookCategory.
….
@ManyToOne
@JoinColumn
private BookCategory bookCategory;
…..
id.belajar.springdatajpa.entity.Book
package id.belajar.springdatajpa.entity;
import javax.persistence.*;
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private String writer;
@Column(nullable = false)
private String isbn;
@ManyToOne
@JoinColumn
private BookCategory bookCategory;
public Book() {
}
@Override
public String toString() {
return "Book{" +
"id=" + id +
", title='" + title + '\'' +
", writer='" + writer + '\'' +
", isbn='" + isbn + '\'' +
'}';
}
}
id.belajar.springdatajpa.repository.BookCategoryRepository
package id.belajar.springdatajpa.repository;
import id.belajar.springdatajpa.entity.BookCategory;
import org.springframework.data.jpa.repository.JpaRepository;
Untuk melakukan pengetesan, lakukan pada class SpringDataJpaApplication dan lakukan injeksi
terhadap class BookCategoryRepository dan class BookRepository. Setelah itu kita masukan data
BookCategory beserta dengan Book nya namun sebelumnya hapus terlebih dahulu data Book yang
sudah ada didalam database.
@Autowired
private BookRepository bookRepository;
@Autowired
private BookCategoryRepository bookCategoryRepository;
@Override
public void run(String... args) throws Exception {
LOG.info("BookCategory : "+bookCategory);
Jika dijalankan,maka dalam LOG akan terdapat keterangan data dari BookCategory beserta Book nya.
Jika kita check dalam database, hasilnya akan sama dengan yang di LOG.
id.belajar.springdatajpa.SpringDataJpaApplication
@SpringBootApplication
public class SpringDataJpaApplication implements CommandLineRunner {
@Autowired
private BookRepository bookRepository;
@Autowired
private BookCategoryRepository bookCategoryRepository;
@Override
public void run(String... args) throws Exception {
LOG.info("BookCategory : "+bookCategory);
}
}
Many to Many Relationship
Setelah relasi one-to-many selanjutnya akan kita bahas mengenai relasi many-to-many dengan
menggunakan contoh kasus relasi antara entitas students dan courses. Dimana satu Students bisa
memiliki bayak Courses dan begitu juga satu Courses dapat memiliki banyak Students. Biasanya relasi
ini akan membuat sebuah table yang menghubungkan kedua entitas seperti pada diagram ERD
berikut.
Table Students_courses diatas adalah sebuah table yang mempunyai dua foreign key, student_id dan
course_id dimana student_id menghubungkan dengan table Student dan course_id menghubungkan
dengan table Course.
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToMany(
fetch = FetchType.LAZY,
cascade = CascadeType.PERSIST
)
@JoinTable(
name = "students_courses",
joinColumns = {
@JoinColumn(name = "student_id", referencedColumnName =
"id", nullable = false, updatable = false)},
inverseJoinColumns = {
@JoinColumn(name = "course_id", referencedColumnName = "id",
nullable = false, updatable = false)
}
)
private Set<Course> courses = new HashSet<>();
public Student() {
}
- FetchType.LAZY konfigurasi yang tidak secara otomatis memuat data yang berada
pada relasi nya sehingga untuk memuat harus menggunakan getter method.
- CascadeType.PERSIST artinya bahwa operasi save() dari Spring Data JPA cascade ke
entitas yang berelasi.
- @JoinTable adalah anotasi yang akan membuat sebuah table baru dengan nama
students_courses sesuai pada gambar ERD diatas. Menggunakan @JoinColumn untuk
menghubungkan antara foreign_key dengan table referensi nya.
id.belajar.springdatajpa.entity.Course
@Entity
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToMany(
mappedBy = "courses",
fetch = FetchType.LAZY
)
private Set<Student> students = new HashSet<>();
public Course() {
}
package id.belajar.springdatajpa.repository;
import id.belajar.springdatajpa.entity.Student;
import org.springframework.data.jpa.repository.JpaRepository;
id.belajar.springdatajpa.repository.CourseRepository
package id.belajar.springdatajpa.repository;
import id.belajar.springdatajpa.entity.Course;
import org.springframework.data.jpa.repository.JpaRepository;
id.belajar.springdatajpa.SpringDataJpaApplication
@SpringBootApplication
public class SpringDataJpaApplication implements CommandLineRunner {
@Autowired
private StudentRepository studentRepository;
@Autowired
private CourseRepository courseRepository;
@Override
public void run(String... args) throws Exception {
// create a student
Student student = new Student("Bagoes Okta", 15);
// save courses
courseRepository.saveAll(Arrays.asList(course1, course2, course3));
}
}
Jalankan programnya, lalu cek menggunakan DBeaver pada table students, courses, dan
students_courses.
Spring Web + Thymeleaf
Spring Web adalah salah satu projek yang membantu kita untuk membuat sebuah aplikasi web (yang
bisa berjalan menggunakan browser) dan Spring Web ini pasti menggunakan annotasi @Controller
pada level Controller nya. Sedangkan Thymeleaf adalah sebuah template engine khusus Java tempat
kita menuliskan file-file HTML nya sama seperti JSP tapi Thyemeleaf adalah template engine yang
paling baru didunia Java.
Dengan detail file pom.xml untuk projek ini seperti di bawah ini.
pom.xml
<properties>
<java.version>1.8</java.version>
<bootstrap.version>4.2.1</bootstrap.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>${bootstrap.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package id.belajar.springweb.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Arrays;
import java.util.List;
@Controller
public class WelcomeController {
// /hello?name=Jhon
@GetMapping("/hello")
public String mainWithParam(
@RequestParam(name = "name", required = false, defaultValue = "")
String name, Model model) {
model.addAttribute("message", name);
- @Controller adalah annotasi yang menandakan bahwa kita akan membuat sebuah
aplikasi Web.
- @GetMapping adalah akses web menggunakan method GET
- Model adalah class yang digunakan untuk mengirimkan data ke View dengan nama file
welcome.html
- @RequestParam adalah annotasi untuk membuat sebuah parameter dalam suatu URL.
Lalu setelahnya kita membuat sebuah file html dengan nama welcome.html
src.resources.templates.welcome.html
<!DOCTYPE HTML>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-
to-fit=no">
<link rel="stylesheet"
th:href="@{webjars/bootstrap/4.2.1/css/bootstrap.min.css}"/>
<link rel="stylesheet" th:href="@{/css/main.css}"/>
</head>
<body>
<div class="starter-template">
<h1>Spring Boot Web Thymeleaf Example</h1>
<h2>
<span th:text="'Hello, ' + ${message}"></span>
</h2>
</div>
<ol>
<li th:each="task : ${tasks}" th:text="${task}"></li>
</ol>
</main>
<!-- /.container -->
<script type="text/javascript"
th:src="@{webjars/bootstrap/4.2.1/js/bootstrap.min.js}"></script>
</body>
</html>
h1{
color:#0000FF;
}
h2{
color:#FF0000;
}
Aplikasi ini akan kita jalankan menggunakan port 8083 untuk itu tambahkan konfigurasinya pada file
application.properties.
server.port=8083
jalankan aplikasi, kemudian akses menggunakan webbrowser dengan port 8083.
Apabila kita mengakses URL localhost:8083/hello?name=Budi, maka output nya akan seperti ini.
Membuat RESTful WebService
Sebelum kita membuat sebuah webservice berbasis restful, alangkah baiknya kita mengenal terlebih
dahulu apa itu REST ? REST adalah singkatan dari Representational State Transfer adalah sebuah
arsitektur layanan komunikasi berbasis web atau menggunakan protocol HTTP. Rest pertama kali
diperkenalkan oleh Roy Fielding dalam disertasi dokternya pada tahun 2000. REST terkenal karena
fleksibilitasnya yang luar biasa sehingga pada jaman sekaran di era cloud banyak layanan-layanan yang
menggunakannya.
Langsung saja, akses web https://start.spring.io/ dan generate projek baru dengan spesifikasi sebagai
berikut :
Penjelasan :
Dalam projek ini kita menggunakan plugin Project Lombok, yaitu plugin yang membantu kita untuk
membuat class POJO menjadi lebih cepat sehingga penulisan default constructor, constructor
parameter, getter, setter, toString. Plugin ini akan digunakan di class-class model.
pom.xml
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package id.learn.webservicesrestful.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import javax.persistence.*;
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String nama;
@Column(nullable = false)
private Long hargaBeli;
@Column(nullable = false)
private Long hargaJual;
Terlihat bahwa dalam class entity Product kali ini kita mengunakan plugin Lombok.
Coba lihat attribute class pada IDE terlhat bahwa kita sudah berhasil menggenerate nya berdasarkan
annotasi diatas.
Database dan JPA Properties
Pastikan kita sudah membuat database untuk webservice restful ini, misalkan kita ambil nama
database nya yaitu spring-webservice .
src.main.resources.application.properties
#MySQL Connection
spring.datasource.url = jdbc:mysql://localhost:3306/spring-webservice
spring.datasource.username = root
spring.datasource.password = root
#JPA Properties
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto=update
spring.datasource.driverClassName=com.mysql.jdbc.Driver
#HikariCP
spring.datasource.hikari.connection-timeout=20000
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.maximum-pool-size=12
spring.datasource.hikari.idle-timeout=300000
spring.datasource.hikari.max-lifetime=120000
spring.datasource.hikari.auto-commit=true
server.port=8082
package id.learn.webservicesrestful.repository;
import id.learn.webservicesrestful.model.Product;
import org.springframework.data.jpa.repository.JpaRepository;
package id.learn.webservicesrestful.service;
import id.learn.webservicesrestful.model.Product;
import java.util.List;
List<Product> findAllProducts();
Dalam kode diatas kita bisa melihat bahwa kita mendefinisikan kontrak atau method :
package id.learn.webservicesrestful.service.impl;
import id.learn.webservicesrestful.exception.ProductNotFoundException;
import id.learn.webservicesrestful.model.Product;
import id.learn.webservicesrestful.repository.ProductRepository;
import id.learn.webservicesrestful.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
ProductRepository productRepository;
@Override
public List<Product> findAllProducts() {
return productRepository.findAll();
}
@Override
public Product findProductById(Long id) throws Exception {
Product product = productRepository.findById(id).orElse(new Product());
return product;
}
@Override
public Product saveorUpdateProduct(Product product) {
return productRepository.save(product);
}
@Override
public void deleteProduct(Long id) {
Product product = productRepository.findById(id).orElse(new Product());
productRepository.delete(product);
}
}
id.learn.webservicesrestful.controller.ProductController
package id.learn.webservicesrestful.controller;
import id.learn.webservicesrestful.model.Product;
import id.learn.webservicesrestful.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
@RestController
@RequestMapping("/api/products")
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping
public ResponseEntity<List<Product>> getAllProducts() {
return new ResponseEntity<>(productService.findAllProducts(),
HttpStatus.OK);
}
@PostMapping
public ResponseEntity<Product> saveProduct(@Valid @RequestBody Product
product) {
return new ResponseEntity<>(productService.saveorUpdateProduct(product),
HttpStatus.OK);
}
@GetMapping("/{id}")
public ResponseEntity<Product> getOneProduct(@PathVariable Long id) throws
Exception {
return new ResponseEntity<Product>(productService.findProductById(id),
HttpStatus.OK);
}
@DeleteMapping("/{id}")
public String deleteProduct(@PathVariable Long id) {
productService.deleteProduct(id);
return "delete sukses";
}
}
- @RestController adalah annotasi yang menyatakan class tersebut akan membuat sebuah
restful.
- @RequestMapping("/api/products") annotasi yang memberikan URL atau alamat dari
rest class ini yaitu "/api/products" sebagai base URL.
- @Autowired annotasi untuk menginject. Dalam hal ini kita akan menginject layer service
ProductService dimana pada service tersebut kita menyimpan logic dalam aplikasi ini. Jadi setiap
data yang diterima pada layer controller kemudian akan diteruskan pada layer service sehingga
controller tidak membuat logic.
Untuk penjelasan setiap method dalam class ProductController akan dijelaskan sambil langsung
mengetest menggunakan PostMan client yang sudah kalian download dan jangan lupa jalankan
program ini dengan running di port :8082 .
URL : localhost:8082/api/products
Method : POST
Body -> RAW -> JSON dengan data berikut yang biasanya dinamakan payload
{
"nama": "Mouse gaming",
"hargaBeli": 10000,
"hargaJual": 15000
}
- getAllProducts() adalah method yang digunakan untuk mengambil semua data yang ada
pada table Product. Menggunakan method annotasi @GETMapping.
URL : localhost:8082/api/products
Method : GET
URL : localhost:8082/api/products/2
Method : GET
URL : localhost:8082/api/products/2
Method : DELETE
Kemudian apabila di cek kembali menggunakan method getAllProducts() terlihat bahwa data
Mouse gaming sudah terhapus dari database.
Spring Security
Security adalah salah satu fitur yang sangat penting dalam membangun sebuah aplikasi. Tanpa adanya
fitur ini maka aplikasi yang dibuat menjadi tidak bagus. Biasanya fitur security dalam aplikasi
menggunakan mekanisme Authentication dan Authorization. Authentication adalah fitur security yang
menangani siapa user yang masuk ke dalam system. Sedangkan Authorization adalah setelah user
tersebut berhasil masuk ke dalam system, lalu user tersebut bisa mengakses kemana saja.
Dalam Spring Security fitur tersebut sudah dibungkus dalam satu library yaitu spring-boot-starter-
security. Dalam bab ini, masih menggunakan source code dala bab Membuat Webrestful. Edit file
pom.xml dan tambahkan library tersebut.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
pom.xml
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Dalam library spring-boot-starter-security, terdapat tiga komponen penting yang dijadikan dalam satu
bundle, yaitu :
Basic Authentication
Secara default ketika kita menambahkan plugin spring boot security, maka semua endpoint yang
telah dibuat bersifat authenticated atau user harus login terlebih dahulu. Bisa kita check, akses
endpoint /api/products dan hasil nya akan mengambalikan status 401 Unauthorized.
Ketika sebuah resource diamankan menggunakan basic authentication maka kita perlu mengirim
username dan password untuk melakukan request authentication. Tapi jika kita tidak mengirim, maka
secara default Spring Boot membuatkan password generator dengan menggunakan basic username
yaitu user . Untuk password generator sendiri, bisa dilihat pada log seperti berikut.
Lakukan pengetesan kembali menggunakan postman client, masih menggunakan endpoint yang sama
tapi dalam tab Authorization menggunakan tipe Basic Auth, masukan username dengan nilai user dan
password dengan nilai dari password generator yang ada di log.
Lalu muncul pertanyaan, bagaimana jika kita ingin mendefinisikan sendiri username dan password nya
sehingga tidak perlu menggunakan password generator. Untuk itu masukan konfigurasi berikut pada
file application.properties nya.
#Security Config
spring.security.user.name=user
spring.security.user.password=@123
application.properties
#MySQL Connection
spring.datasource.url = jdbc:mysql://localhost:3306/spring-webservice
spring.datasource.username = root
spring.datasource.password = root
#JPA Properties
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto=update
spring.datasource.driverClassName=com.mysql.jdbc.Driver
#HikariCP
spring.datasource.hikari.connection-timeout=20000
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.maximum-pool-size=12
spring.datasource.hikari.idle-timeout=300000
spring.datasource.hikari.max-lifetime=120000
spring.datasource.hikari.auto-commit=true
#Security Config
spring.security.user.name=user
spring.security.user.password=@123
server.port=8082
Jalankan kembali aplikasinya, dan lakukan pengetesan kembali menggunakan postman dan jangan
lupa gunakan username dan password yang sudah didefinisikan pada file application.properties.
Unit test dengan JUnit dan Mockito
Pada bab ini masih menggunakan source code restul API untuk mensimulasikan dalam penggunaan
unit testing. Namun sebelumnya kita bahas dulu apa itu unit testing ?
Unit Testing adalah salah satu level dari proses testing dalam software development. Unit testing
adalah pengujian dasar yang menguji setiap unit atau component baik itu dari segi functional atau
behavior.
Sebagai programmer, sudah menjadi suatu kewajiban selain bisa memembuat program, juga bisa
membuat unit testing dari program yang telah di tulis. Kenapa ? karena kalau tidak melakukan unit
testing kita tidak bisa meyakinkan orang lain bahwa kode yang sudah kita tulis itu sudah sesuai dengan
proses bisnis atau tidak dan bagaimana kualitas code yang ditulis. Namun kebanyakan programmer
khusus nya pemula, suka melewati pembelajaran unit testing ini padahal hal ini sangat lah penting
dalam software development. Di dalam bab ini akan dijelaskan dasar bagaimana membuat Unit Testing
menggunakan tools yang sering digunakan yaitu JUnit dan Mockito.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>1.3.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.3.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
c. Plugin Zalando
Yang terakhir adalah plugin zalando yaitu mapper yang berfungsi untuk mengubah file json
menjadi object. Plugin ini nantinya akan digunakan untuk membuat unit testing dalam layer
controller.
<dependency>
<groupId>org.zalando</groupId>
<artifactId>problem-spring-web-starter</artifactId>
<version>${problem-spring-web.version}</version>
<type>pom</type>
</dependency>
Untuk lengkapnya berikut isi dari file pom.xml yang telah kita modifikasi.
pom.xml
<properties>
<java.version>1.8</java.version>
<problem-spring-web.version>0.25.0</problem-spring-web.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package id.learn.webservicesrestful;
import id.learn.webservicesrestful.model.Product;
import org.apache.commons.lang3.RandomStringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class TestObjectFactory {
return product;
}
Buat sebuah class ProductServiceTest dengan base konfigurasi seperti dibawah ini.
id.learn.webserservicerestful.ProductServiceTest
@RunWith(SpringRunner.class)
public class ProductServiceTest{
@InjectMocks
private ProductService productService = new ProductServiceImpl();
@Mock
private ProductRepository productRepository;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(productService, "productRepository",
productRepository);
}
Testing findAll
Didalam class ProductServiceImpl terdapat method findAllProduct(),method ini akan kita coba
buatkan unit testing nya. Buatlah sebuah method dengan nama testFindAll() didalam class
ProductServiceTest.
id.learn.webserservicerestful.ProductServiceTest
…
@Test
public void testFindAll() {
final List<Product> datas = TestObjectFactory.createProductList(10);
Mockito.when(productRepository.findAll()).thenReturn(datas);
MatcherAssert.assertThat(actual.size(), Matchers.equalTo(datas.size()));
}
Testing getProductById
Didalam class ProductServiceImpl terdapat method findProductById(),method ini akan kita coba
buatkan unit testing nya. Buatlah sebuah method dengan nama testProductById() didalam class
ProductServiceTest.
id.learn.webserservicerestful.ProductServiceTest
@Test
public void testProductById() throws Exception {
final Long id = new Random().nextLong();
final Product product = TestObjectFactory.createProduct();
Mockito.when(productRepository.findById(id)).thenReturn(Optional.of(product));
MatcherAssert.assertThat(actual.getId(), Matchers.equalTo(product.getId()));
MatcherAssert.assertThat(actual.getNama(),
Matchers.equalTo(product.getNama()));
MatcherAssert.assertThat(actual.getHargaBeli(),
Matchers.equalTo(product.getHargaBeli()));
MatcherAssert.assertThat(actual.getHargaJual(),
Matchers.equalTo(product.getHargaJual()));
}
- final Long id karena kita akan mensimulasikan findProductById maka kita harus
menyiapkan variable id dengan tipe data Long dan inisialisasi dengan nilai Long secara
random / acak.
- Buat object Product dan inisialisasi dengan TestObjectFactory.
- Mockito.when(productRepository.findById(id)).thenReturn(Optional.
of(product)) eksekusi productRepository.findById() mock dan kembalikan
object product tapi menggunakan class Optional. Karena jika kita lihat dalam method
ProductService – findProductById itu melempar object Optional.
- final Product actual buat object actual seakan-akan kita mengakses method
productService.findProductById(id).
- Terakhir lakukan pencocokan data antara objek actual dan objek product.
- Jika berhasil maka hasilnya akan seperti ini
Testing getProductByIdWithNullDataFromDB
Sebenarnya test ini sama dengan method diatas yaitu mencari data single product berdasarkan ID
namun disimulasikan seakan-akan data dari ID yang dicari itu tidak ada di database.
id.learn.webserservicerestful.ProductServiceTest
…
@Test
public void testProductByIdWithNullDataFromDB() throws Exception {
final Long id = new Random().nextLong();
Mockito.when(productRepository.findById(id)).thenReturn(Optional.empty());
MatcherAssert.assertThat(actual, Matchers.nullValue());
}
Testing saveOrUpdateProduct
Didalam class ProductServiceImpl terdapat method saveOrUpdateProduct(),method ini akan kita
coba buatkan unit testing nya. Buatlah sebuah method dengan nama testSaveOrUpdateProduct()
didalam class ProductServiceTest.
id.learn.webserservicerestful.ProductServiceTest
@Test
public void testSaveOrUpdateProduct() {
final Product product = TestObjectFactory.createProduct();
Mockito.when(productRepository.save(product)).thenReturn(product);
MatcherAssert.assertThat(actual, Matchers.notNullValue());
}
@Test
public void testdeleteProduct() {
Mockito.when(productRepository.findById(id)).thenReturn(Optional.of(product));
Mockito.doNothing().when(productRepository).delete(product);
productService.deleteProduct(id);
Mockito.verify(productRepository, times(1)).delete(product);
package id.learn.webservicesrestful.service;
import id.learn.webservicesrestful.TestObjectFactory;
import id.learn.webservicesrestful.model.Product;
import id.learn.webservicesrestful.repository.ProductRepository;
import id.learn.webservicesrestful.service.impl.ProductServiceImpl;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.util.ReflectionTestUtils;
import java.util.List;
import java.util.Optional;
import java.util.Random;
@RunWith(SpringRunner.class)
public class ProductServiceTest{
@InjectMocks
private ProductService productService = new ProductServiceImpl();
@Mock
private ProductRepository productRepository;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(productService, "productRepository",
productRepository);
}
@Test
public void testFindAll() {
final List<Product> datas = TestObjectFactory.createProductList(10);
Mockito.when(productRepository.findAll()).thenReturn(datas);
MatcherAssert.assertThat(actual.size(), Matchers.equalTo(datas.size()));
}
@Test
public void testProductById() throws Exception {
final Long id = new Random().nextLong();
final Product product = TestObjectFactory.createProduct();
Mockito.when(productRepository.findById(id)).thenReturn(Optional.of(product));
MatcherAssert.assertThat(actual.getId(),
Matchers.equalTo(product.getId()));
MatcherAssert.assertThat(actual.getNama(),
Matchers.equalTo(product.getNama()));
MatcherAssert.assertThat(actual.getHargaBeli(),
Matchers.equalTo(product.getHargaBeli()));
MatcherAssert.assertThat(actual.getHargaJual(),
Matchers.equalTo(product.getHargaJual()));
}
@Test
public void testProductByIdWithNullDataFromDB() throws Exception {
final Long id = new Random().nextLong();
Mockito.when(productRepository.findById(id)).thenReturn(Optional.empty());
MatcherAssert.assertThat(actual, Matchers.nullValue());
}
@Test
public void testSaveOrUpdateProduct() {
final Product product = TestObjectFactory.createProduct();
Mockito.when(productRepository.save(product)).thenReturn(product);
MatcherAssert.assertThat(actual, Matchers.notNullValue());
}
@Test
public void testdeleteProduct() {
final Long id = new Random().nextLong();
Mockito.when(productRepository.findById(id)).thenReturn(Optional.of(product));
Mockito.doNothing().when(productRepository).delete(product);
productService.deleteProduct(id);
Mockito.verify(productRepository, times(1)).delete(product);
Dan jika kita eksekusi secara keseluruhan maka hasilnya akan seperti berikut.
@ExtendWith(SpringExtension.class)
@WebMvcTest(controllers = ProductController.class)
public class ProductControllerTest {
@MockBean
private ProductService productService;
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
}
- Karena kita akan mencoba menggunakan JUnit 5 maka tidak diperlukan lagi @RunWith
tapi diganti dengan @ExtendWith. SpringExtension adalah class yang disediakan Spring 5
untuk dapat berintegrasi dengan Spring TestContext Framework. @ExtendWith annotasi
menerima class yang mengimplementasi Extension interface.
- @WebMvcTest annotasi yang digunakan untuk menjalankan application context yang
berisi hanya beans untuk melakukan testing sebuah class controller / web. Dan disini kita
akan mengetest class ProductController.class
- Kita menggunakan @MockBean untuk membuat object palsu / mock dalam sebuah class
controller.
- Annotasi @WebMvcTestdigunakan untuk mengkonfigurasi secara otomatis object
mockMvc.
- objectMapper akan digunakan untuk mengubah class menjadi JSON.
Testing testSaveOrUpdateProduct
kita akan coba mensimulasikan method testSaveOrUpdateProduct() yang ada di class
ProductController. Untuk itu tambahkan method baru dalam file ProductControllerTest sebagai
berikut :
id.learn.webserservicerestful.controller.ProductControllerTest
@Test
public void testCreateOrUpdateNewProduct() throws Exception {
Product product = TestObjectFactory.createProduct();
Mockito.when(productService.saveorUpdateProduct(product)).thenReturn(product);
mockMvc.perform(post("/api/products")
.contentType("application/json")
.content(objectMapper.writeValueAsString(product)))
.andExpect(status().isOk());
}
@Test
public void testGetAllProducts() throws Exception {
final List<Product> datas = TestObjectFactory.createProductList(10);
Mockito.when(productService.findAllProducts()).thenReturn(datas);
this.mockMvc.perform(get("/api/products"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.size()", is(datas.size())));
}
- Buat object datas yang berisi collection dari class Product menggunakan factory sebanyak
10 item.
- Akses service layer productService.findAllProducts() dan simulasikan seakan-
akan mengembalikan object datas.
- Akses /api/products dan set hasil yang diharapkan yaitu status nya ok dan
mengembalikan data dan lakukan pencocokan bahwa yang diberikan oleh server itu sama
dengan jumlah data pada object datas.
Testing getOneProduct
Sesuai dengan namanya, kita akan mengetest URL /api/products/{id} dan mengembalikan satu
data atau object dari database. Edit kembali class ProductControllerTest dan buat method test
baru dengan nama testGetOneProduct().
id.learn.webserservicerestful.controller.ProductControllerTest
…
@Test
public void testGetOneProduct() throws Exception {
final Long id = new Random().nextLong();
final Product product = TestObjectFactory.createProduct();
Mockito.when(productService.findProductById(id)).thenReturn(product);
mockMvc.perform(get("/api/products/{id}", id)
.contentType("application/json"))
.andExpect(status().isOk());
}
…
- Karena kita akan mensimulasikan untuk mendapatkan object berdasarkan ID maka kita
buat object id dengan tipe data Long menggunakan class random generator.
- Buat object Product
- Akses url /api/products/{id} dan berikan id yang telah dibuat, atur bahwa
contentType nya adalah application/json. Terakhir kita mengharapkan bahwa hasilnya
mengembalikan status OK.
Testing testDeleteProduct
Terakhir, kita akan coba melakukan testing penghapus dan data dengan mengakses
/api/products/{id} menggunakan method DELETE.disamping itu, kita juga akan memerlukan id
yang akan dilemparkan ke server. Pada class ProductControllerTest buat sebuah method test
dengan nama testDeleteProduct.
id.learn.webserservicerestful.controller.ProductControllerTest
@Test
public void testDeleteProduct() throws Exception {
final Long id = new Random().nextLong();
Mockito.doNothing().when(productService).deleteProduct(id);
this.mockMvc.perform(delete("/api/products/{id}", id))
.andExpect(status().isOk());
}
…
- Karena kita akan mensimulasikan untuk mendapatkan object berdasarkan ID maka kita
buat object id dengan tipe data Long menggunakan class random generator.
- Akses service layer productService pada deleteProduct(id). Karena method
deleteProduct() tidak melemparkan apapun (void) maka gunakan Mockito.doNothing().
- Terakhir, akses url /api/products/{id}, sertakan id yang telah dibuat dan hasil yang
diharapkan server mengembalikan status OK.
Dan jika ditest secara keseluruhan dengan melakukan pengetesan lansung pada class
ProductControllerTest maka hasil yang kita harapkan adalah semuanya lolos test.
id.learn.webserservicerestful.controller.ProductControllerTest
package id.learn.webservicesrestful.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import id.learn.webservicesrestful.TestObjectFactory;
import id.learn.webservicesrestful.model.Product;
import id.learn.webservicesrestful.service.ProductService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import static
org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static
org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.hamcrest.CoreMatchers.is;
import java.util.List;
import java.util.Random;
@ExtendWith(SpringExtension.class)
@WebMvcTest(controllers = ProductController.class)
public class ProductControllerTest {
@MockBean
private ProductService productService;
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Test
public void testGetAllProducts() throws Exception {
final List<Product> datas = TestObjectFactory.createProductList(10);
Mockito.when(productService.findAllProducts()).thenReturn(datas);
this.mockMvc.perform(get("/api/products"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.size()", is(datas.size())));
}
@Test
public void testCreateOrUpdateNewProduct() throws Exception {
Product product = TestObjectFactory.createProduct();
Mockito.when(productService.saveorUpdateProduct(product)).thenReturn(product);
mockMvc.perform(post("/api/products")
.contentType("application/json")
.content(objectMapper.writeValueAsString(product)))
.andExpect(status().isOk());
}
@Test
public void testGetOneProduct() throws Exception {
final Long id = new Random().nextLong();
final Product product = TestObjectFactory.createProduct();
Mockito.when(productService.findProductById(id)).thenReturn(product);
mockMvc.perform(get("/api/products/{id}", id)
.contentType("application/json"))
.andExpect(status().isOk());
}
@Test
public void testDeleteProduct() throws Exception {
final Long id = new Random().nextLong();
Mockito.doNothing().when(productService).deleteProduct(id);
this.mockMvc.perform(delete("/api/products/{id}", id))
.andExpect(status().isOk());
}
}
Generate Code Coverage dengan Jacoco dan Sonarqube
Dengan coverage kita bisa mengetahui berapa persen kode yang telah di testing dan yang belum, dan
untuk mendapatkan hasil yang PASS atau berhasil minimal semua code yang tercover minimal 90%.
Sonarqube adalah sebuah tool yang dilakukan untuk menginspeksi kualitas dari code yang telah ditulis
untuk melihat bug, code smells, security vulnerabilities. Hasil report yang dihasilkan dari jacoco
kemudian akan dibaca oleh Sonarqube yang selanjutkan akan ditampilkan secara informative
menggunakan webbrowser.
Pada bab ini penulis menggunakan Sonarqube free edition sonarqube-7.9.1 Community Edition dan
kalian bisa unduh secara langsung pada situs nya https://www.sonarqube.org/downloads/ .
Setting WebService integrasi dengan Jacoco dan Sonarqube
Pada bab ini akan menggunakan kembali code yang telah dibuat unit testing pada bab sebelumnya
yang focus pada unit testing service layer dan controller layer.
Klik file StartSonar.bat dan akan muncul window berupa command line untuk melihat log dari server
sonarqube.
jvm 1 |
wrapper | Wrapper Process has not received any CPU time for 27 seconds. Extending timeouts.
pada potongan log diatas di baris terakhir, terlihat bahwa server sonarqube sudah berhasil running.
Setelah itu, masih dalam file pom.xml tambahkam plugin Jacoco didalam tag plugin.
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<configuration>
<skip>${maven.test.skip}</skip>
<destFile>${basedir}/target/coverage-reports/jacoco-unit.exec</destFile>
<dataFile>${basedir}/target/coverage-reports/jacoco-unit.exec</dataFile>
<output>file</output>
<append>true</append>
<excludes>
<exclude>*MethodAccess</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>jacoco-initialize</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<phase>test-compile</phase>
</execution>
<execution>
<id>jacoco-site</id>
<phase>verify</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<properties>
<java.version>1.8</java.version>
<problem-spring-web.version>0.25.0</problem-spring-web.version>
<jacoco.version>0.8.3</jacoco.version>
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
<sonar.jacoco.reportPath>${project.basedir}/../target/jacoco.exec</sonar.jacoco.
reportPath>
<sonar.language>java</sonar.language>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</project>
Konfigurasi diatas adalah konfigurasi yang dibutuhkan jacoco untuk menggenerate codecoverage.
Buka terminal atau command prompt dan build projeknya dengan sintak berikut dan tunggu sampai
proses build selesai.
mvn sonar:sonar
tunggu sampai selesai, sampai keluar link yang bisa kita gunakan untuk mengakses codecoverage
report.
Copy link tersebut kemudian paste pada browser.
Pada windows tersebut kita bisa melihat informasi dari code yang telah ditulis, mulai dari security,
maintainability dan coverage. Terlihat codecoverage bernilai 90.9% yang artinya bahwa kode kita
sudah bisa dikatakan berkualitas. Jika angka tersebut di klik maka akan masuk ke halaman detail nya.
Dan untuk melihat lebih detail lagi, bisa menekan folder-folder diatas misalkan melihat codecoverage
pada class ProductServiceImpl.
Spring AOP (Abstract Oriented Programming)
Pengertian AOP
AOP adalah singkatan dari Aspect Oriented Programming yaitu sebuah paradigm yang memungkinkan
kita untuk melakukan cross-cutting-concern. cross-cutting-concern adalah fungsi-fungsi umum yang
dibutuhkan oleh aplikasi tapi tidak ada hubungan dengan proess bisnis pada aplikasi tersebut. Adapun
contoh-contoh cross-cutting-concern diantaranya :
- Performance Monitoring
- Logging
- Transaction Management
- Caching
- Security
- Handling Error
- Dan Lain-lain.
Namun dalam bab ini kita akan menggunakan untuk membuat Logger pada layer Controller. Seperti
yang sudah kita tahu bahwa ketika kita membuat Logger secara Manual menggunakan
sangatlah merepotkan, setiap log harus di definisikan pada setiap method yang ingin diketahui dalam
Log. Dengan menggunakan AOP maka proses ini akan kita rubah menjadi otomatis, jadi kita tidak perlu
membuat Logger disetiap method nya. Namun sebelumnya tambahkan library AOP terlebih dahulu.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
#Logging Config
logging.level.org.springframework.web=INFO
logging.level.org.hibernate=ERROR
logging.level.id.learn.webservicesrestful=DEBUG
Membuat LoggingAspect
Class LoggingAspect nantinya akan kita gunakan untuk membuat Logger pada setiap class yang berada
dalam layer Controller. Terdapat beberapa istilah yang harus kita pahami dalam AOP, yaitu :
- Joinpoint adalah suatu titik pengeksekusikan pada aplikasi, misalnya pada saat
pemanggilan method, inisialisasi class, field assignment atau inisiasi object
- Advice adalah kode yang akan dieksekusi oleh Joinpoint. Ada beberapa macam Joinpoint
yaitu before advice dan after advice.
- Pointcut adalah kumpulan beberapa Joinpoint yang mendefinisikan kapan Advice akan
dijalankan.
- Aspect adalah kombinasi antara Advice dan Pointcut. Kombinasi ini yang nantinya akan
menghasilkan logic yang harus dieksekusi oleh aplikasi.
- Target adalah objek yang dimodifikasi oleh AOP.
id.learn.webserservicerestful.aspect.LoggingAspect
package id.learn.webservicesrestful.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Aspect
@Component
public class LoggingAspect {
@Pointcut("within(@org.springframework.stereotype.Repository *)" +
" || within(@org.springframework.stereotype.Service *)" +
" || within(@org.springframework.web.bind.annotation.RestController
*)")
public void springBeanPointcut() {
@Pointcut("within(id.learn.webservicesrestful.controller..*)")
public void applicationPackagePointcut() {
Lalu kita jalankan aplikasi dan lakukan hit menggunakan Postman Client.
Lalu kita cek pada Log aplikasi, jika seperti pada gambar di bawah ini,maka Logger menggunakan AOP
berhasil dilakukan.
2020-03-10 13:48:20.005 DEBUG 26380 --- [io-8082-exec-10] i.l.w.aspect.LoggingAspect :
Enter: id.learn.webservicesrestful.controller.ProductController.getAllProducts() with argument[s] = []
Docker adalah sebuah tools containe yang bersifat opensource yang ditujukan untuk para developer
atau sysadmin untuk mengemas, membangun dan menjalankan aplikasi dimanapun di dalam sebuah
container sesuai dengan konfigurasi yang diinginkan.
Dulu penulis sebelum mengenal docker, penulis selalu menggunakan Vagrant untuk development.
Jika teman-teman belum tahu, Vagrant adalah sebuah tool virtualisasi yang menggunakan Oracle
Virtual Box. Jadi menjalankan OS didalam OS dan sudah pasti sangat banyak memakan memory.
Beberapa perbedaan dapat dilihat pada table berikut .
Dari table diatas bisa kita lihat banyak sekali perbedaan antara Docker dan Vagrant. Adapun
perbedaan yang sangat mencolok adalah pada pemakaian resource atau memory. Docker cenderung
menggunakan memory lebih sedikit daripada Vagrant. Jika menggunakan Vagrant maka kita wajib
menginstall virtual machine seperti Oracle Virtualbox atau VMware, berbeda dengan docker yang
hanya menggunakan Linux container sehingga tidak perlu menggunakan virtual machine.
Mengunduh Docker
Docker tersedia dengan berbagai variant sesuai dengan Sistem Operasi seperti Windows 10, Mac OS
dan Linux. Pada bab ini penulis menggunakan versi Docker untuk windows 10, namun apabila teman-
teman menggunakan Sistem Operasi yang berbeda bisa akses langsung disitus mengenai cara
installasinya. Disana sudah terdapat cara-cara instalasi sesuai dengan Sistem Operasi.
- Versi untuk windows yaitu minimal harus menggunakan Windows 10 64Bit dengan versi
Pro, Enterprise atau Education
- Fitur Hyper-V dan Container Windows harus enable.
- Minimal RAM atau memory sebesar 4GB.
- Pada BIOS, Hardware Virtualization harus di enable.
- Tekan dua kali pada file Docker installer yang telah selesai diunduh.
- Ikuti instruksi sesuai dengan tampilan wizard seperti accept license, authorize the
installer dan lain-lain.
- Ketika windows popup, izinkan Docker untuk menggunakan password pada computer
Anda. Hal ini digunakan Docker untuk melakukan instalasi komponen networking,
membuat link ke desktop dan mengatur fitur Hyper-V VM.
- Setelah selesai klik Finish
Untuk memulai docker, klik icon docker pada Dekstop atau cari Docker pada hasi pencarian sepertin
ini.
docker version
Apabila response nya seperti dibawah ini maka Docker telah berhasil berjalan
Version: 19.03.5
Go version: go1.12.12
OS/Arch: windows/amd64
Experimental: false
Karena aplikasi yang akan dideploy berbasis Java, maka imags yang akan digunakan adalah :
- OpenJDK Versi 11
- MySQL 5.7.28
Untuk itu, lakukan proses unduh images menggunakan terminal atau command prompt pada local
computer menggunakan sintak berikut.
jika kedua images tersebut sudah berhasil diunduh, lakukan pengecekan dengan sintak
docker images
dan hasilnya docker images yang tadi diunduh telah berhasil terdaftar.
a. Pada directory src/resources buat satu buah profiles application.properties dengan nama
application-docker.properties
sehingga akan ada dua file profile yaitu default dan docker.
application.properties, digunakan untuk pointing ke koneksi database yang ada di local atau
untuk kebutuhan development
application-docker.properties, digunakan untuk pointing ke koneksi database yang ada
didocker container yang akan dibuat di tahap selanjutnya.
application-docker.properties
#Spring Profile
spring.profiles.include=docker
#MySQL Connection
spring.datasource.url=jdbc:mysql://dbcontainer:3306/webrestful
spring.datasource.username=sa
spring.datasource.password=webrestful
#HikariCP
spring.datasource.hikari.connection-timeout=20000
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.maximum-pool-size=12
spring.datasource.hikari.idle-timeout=300000
spring.datasource.hikari.max-lifetime=120000
spring.datasource.hikari.auto-commit=true
#JPA Properties
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto=update
spring.datasource.driverClassName=com.mysql.jdbc.Driver
server.port=8082
Terlihat diatas pada bagian url menggunakan container dbcontainer dan database
webrestful yang akan dibuat pada tahap berikutnya.
Untuk mengecek docker network yang telah dibuat, ketikan sintak berikut:
#!/usr/bin/env sh
ARGUMENT=$1
HOST="$(echo $ARGUMENT | cut -d ':' -f1)"
PORT="$(echo $ARGUMENT | cut -d ':' -f2)"
MAX_RETRY=90
count=0
while [ $count -lt $MAX_RETRY ]
do
count=$((count+1))
nc -z $HOST $PORT
result=$?
if [ $result -eq 0 ]; then
echo "Connection is available after $count second(s)."
exit 0
fi
echo "Retrying..."
sleep 1
done
>&2 echo "Timeout occurred after waiting $MAX_RETRY seconds for $HOST:$PORT."
exit -1
Membuat Dockerfile
Dockerfile adalah sebuah file yang akan dibaca oleh Docker sebagai rujukan untuk melakukan proses
deploy ke dalam bentuk docker images. Untuk mempermudah, edit nama aplikasi nya menjadi
webrestful dengan menambahkan konfigurasi dalam file pom.xml pada bagian tag plugin.
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<finalName>webrestful</finalName>
</configuration>
</plugin>
<properties>
<java.version>1.8</java.version>
<problem-spring-web.version>0.25.0</problem-spring-web.version>
<jacoco.version>0.8.3</jacoco.version>
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
<sonar.jacoco.reportPath>${project.basedir}/../target/jacoco.exec</sonar.jacoco.
reportPath>
<sonar.language>java</sonar.language>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<finalName>webrestful</finalName>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<configuration>
<skip>${maven.test.skip}</skip>
<destFile>${basedir}/target/coverage-reports/jacoco-
unit.exec</destFile>
<dataFile>${basedir}/target/coverage-reports/jacoco-
unit.exec</dataFile>
<output>file</output>
<append>true</append>
<excludes>
<exclude>*MethodAccess</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>jacoco-initialize</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<phase>test-compile</phase>
</execution>
<execution>
<id>jacoco-site</id>
<phase>verify</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Selanjutnya, build project nya dengan sintak mvn clean install dan tunggu sampai proses buildnya
selesai .
File webrestful.jar selanjutnya akan dideploy ke Docker menggunakan Dockerfile. Untuk itu, buat
sebuah file dengan nama Dockerfile dalam directory root aplikasi webrestful.
Dockerfile
FROM openjdk:11
ADD target/webrestful.jar webrestful.jar
ADD wait-for.sh wait-for.sh
EXPOSE 8082
ENTRYPOINT ["java", "-Dspring.profiles.active=docker","-jar", "webrestful.jar"]
- FROM images yang akan digunakan oleh aplikasi yang akan kita deploy
- ADD proses menambahkan file .jar local ke docker
- EXPOSE aplkasi yang dideploy akan listen dalam port 8082
- ENTRYPOINT adalah sintak untuk menjalankan aplikasi yang sudah dalam bentuk images.
Terlihat bahwa untuk koneksi internet kita menggunakan properties aplikasi docker .
Pada terminal atau command prompt yang sama, ketikan sintak berikut untuk proses deploy image
dengan nama webrestful dan tunggu sampai proses deploy berhasil.
docker build -t webrestful .
---> 243e95d792e3
---> 9edbc46f55cc
---> 3f0cb49ed345
---> e5c90c6c51ad
---> f92513300b1e
Kemudian cek apakah proses deploy sudah berhasil atau belum dengan docker images -a
Membuat Docker-compose
Pada saat menjalankan mysql container, kita menjalankan secara manual, bayangkan jika ada 10
images yang akan di mounting ke container, berarti akan ada 10 tahapan manual yang harus kita
lakukan, untuk mengotomasi proses mounting ini ada satu fitur yang dinamakan docker-compose.
Dengan docker-compose, berapapun jumlah images yang akan dimounting, cukup dengan satu kali
sintak yaitu.
docker-compose up
buat sebuah file dengan nama docker-compose.yml pada directory root aplikasi webrestful.
Docker-compose.yml
version: '3.3'
services:
webrestful:
container_name: webrestful
build: .
image: webrestful
ports:
- "8087:8082"
command: ["-c", "wait-for.sh dbcontainer:3306 && java -jar -
Dspring.profiles.active=docker webrestful.jar"]
networks:
- mysql-webrestful
depends_on:
- dbcontainer
dbcontainer:
container_name: dbwebrestful
image: mysql:5.7.28
networks:
- mysql-webrestful
volumes:
- db-data-mysql:/data/mysql
environment:
MYSQL_ROOT_PASSWORD: 'webrestful'
MYSQL_DATABASE: 'webrestful'
MYSQL_USER: 'sa'
MYSQL_PASSWORD: 'webrestful'
MYSQL_ROOT_HOST: "%"
ports:
- "3306:3306"
# Volumes
volumes:
db-data-mysql:
# Networks
networks:
mysql-webrestful:
apabila dicek dengan docker ps –a terlihat bahwa ada dua container yang berhasil di mounting.
- Web : https://medium.com/backend-habit
- Linkedin : https://www.linkedin.com/in/teten-nugraha/
- Github: https://github.com/teten777
- Email : [email protected]
- Telegram : @TetenNugraha
** silahkan jika ada yang kurang mengerti dari penjelasan diatas atau jika ada yang ingin meminta
source, dapat langsung menghubungi penulis
Daftar Pustaka
Karanam, Ranga Rao. 2018. Spring: Microservices with Spring Boot. Packt Publishing
https://www.javaguides.net
https://mkyong.com/
https://www.baeldung.com/
https://medium.com/backend-habit