Showing posts with label Database. Show all posts
Showing posts with label Database. Show all posts

Sunday, July 1, 2018

Multiple Database Configuration Spring Boot


Salam,

Untuk kesempatan kali ini saya akan sharing bagaimana cara menggunakan lebih dari satu database di spring boot.
oke langsung saja klo begitu .

Pertama kita bisa create new project spring boot di https://start.spring.io/




Selanjutnya konfigurasi database, kita akan menggunakan 2 database sebagai contoh.
Disini saya menggunakan 2 database mysql di file application.properties dan hibernate.properties

  • application.properties
spring.user.datasource.url=jdbc:mysql://localhost:3306/user
spring.user.datasource.username=root
spring.user.datasource.password=root
spring.user.datasource.driver-class-name=com.mysql.jdbc.Driver

spring.booking.datasource.url=jdbc:mysql://localhost:3306/booking
spring.booking.datasource.username=root
spring.booking.datasource.password=root
spring.booking.datasource.driver-class-name=com.mysql.jdbc.Driver

  • hibernate.properties

hibernate.show_sql=true
hibernate.format_sql=true
Kemudian kita buat model class untuk masing - masing database
  • Tabel user_details
package com.devglan.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "user_details")
public class UserDetails {

 @Id
 @Column
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 private Long id;

 @Column(name = "first_name")
 private String firstName;
 
 @Column(name = "last_name")
 private String lastName;
 
 @Column
 private String email;
 @Column
 private String password;

 public Long getId() {
  return id;
 }

 public void setId(Long id) {
  this.id = id;
 }

 public String getFirstName() {
  return firstName;
 }

 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }

 public String getLastName() {
  return lastName;
 }

 public void setLastName(String lastName) {
  this.lastName = lastName;
 }

 public String getEmail() {
  return email;
 }

 public void setEmail(String email) {
  this.email = email;
 }

 public String getPassword() {
  return password;
 }
 public void setPassword(String password) {
  this.password = password;
 }
 
}
  • Tabel Booking
package com.devglan.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "booking")
public class Booking {
 
 @Id
 @Column
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 private Long id;
 
 private Long createdBy;
 private String pickupAddress;
 private String dropAddress;
 private String bookingAmount;
 
 public Long getId() {
  return id;
 }
 public void setId(Long id) {
  this.id = id;
 }
 public Long getCreatedBy() {
  return createdBy;
 }
 public void setCreatedBy(Long createdBy) {
  this.createdBy = createdBy;
 }
 public String getPickupAddress() {
  return pickupAddress;
 }
 public void setPickupAddress(String pickupAddress) {
  this.pickupAddress = pickupAddress;
 }
 public String getDropAddress() {
  return dropAddress;
 }
 public void setDropAddress(String dropAddress) {
  this.dropAddress = dropAddress;
 }
 public String getBookingAmount() {
  return bookingAmount;
 }
 public void setBookingAmount(String bookingAmount) {
  this.bookingAmount = bookingAmount;
 }

}
Lalu kita buat konfigurasi class untuk masing - masing tabel dari 2 database yang berbeda
  • Database User
package com.devglan.config;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.devglan.model.UserDetails;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
  entityManagerFactoryRef = "userEntityManager", 
  transactionManagerRef = "userTransactionManager", 
  basePackages = "com.devglan.user.dao"
)
public class UserDBConfig {

 @Bean
 @ConfigurationProperties(prefix = "spring.user.datasource")
 public DataSource postgresqlDataSource() {
  return DataSourceBuilder
     .create()
     .build();
 }

 @Bean(name = "userEntityManager")
 public LocalContainerEntityManagerFactoryBean postgresqlEntityManagerFactory(EntityManagerFactoryBuilder builder) {
  return builder
     .dataSource(postgresqlDataSource())
     .properties(hibernateProperties())
     .packages(UserDetails.class)
     .persistenceUnit("userPU")
     .build();
 }

 @Bean(name = "userTransactionManager")
 public PlatformTransactionManager postgresqlTransactionManager(@Qualifier("userEntityManager") EntityManagerFactory entityManagerFactory) {
  return new JpaTransactionManager(entityManagerFactory);
 }

 private Map<String, Object> hibernateProperties() {

  Resource resource = new ClassPathResource("hibernate.properties");
  
  try {
   Properties properties = PropertiesLoaderUtils.loadProperties(resource);
   
   return properties.entrySet().stream()
           .collect(Collectors.toMap(
              e -> e.getKey().toString(),
              e -> e.getValue())
             );
  } catch (IOException e) {
   return new HashMap<String, Object>();
  }
 }
}
  • Database Booking
package com.devglan.config;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.devglan.model.Booking;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
  entityManagerFactoryRef = "bookingEntityManager", 
  transactionManagerRef = "bookingTransactionManager", 
  basePackages = "com.devglan.booking.dao"
)
public class BookingDBConfig {

 @Primary
 @Bean
 @ConfigurationProperties(prefix = "spring.booking.datasource")
 public DataSource mysqlDataSource() {
  return DataSourceBuilder
     .create()
     .build();
 }

 @Primary
 @Bean(name = "bookingEntityManager")
 public LocalContainerEntityManagerFactoryBean mysqlEntityManagerFactory(EntityManagerFactoryBuilder builder) {
  return builder
     .dataSource(mysqlDataSource())
     .properties(hibernateProperties())
     .packages(Booking.class)
     .persistenceUnit("bookingPU")
     .build();
 }

 @Primary
 @Bean(name = "bookingTransactionManager")
 public PlatformTransactionManager mysqlTransactionManager(@Qualifier("bookingEntityManager") EntityManagerFactory entityManagerFactory) {
  return new JpaTransactionManager(entityManagerFactory);
 }

 private Map<String, Object> hibernateProperties() {

  Resource resource = new ClassPathResource("hibernate.properties");
  
  try {
   Properties properties = PropertiesLoaderUtils.loadProperties(resource);
   
   return properties.entrySet().stream()
           .collect(Collectors.toMap(
              e -> e.getKey().toString(),
              e -> e.getValue())
             );
  } catch (IOException e) {
   return new HashMap<String, Object>();
  }
 }
}
Kemudian kita buat file dao dari masing- masing model
  • Dao Tabel UserDetails
package com.devglan.user.dao;

import org.springframework.data.repository.CrudRepository;

import com.devglan.model.UserDetails;

public interface UserDao extends CrudRepository<UserDetails, Long> {
 
 UserDetails findByEmail(String email);
 
}
  • Dao Tabel Booking
package com.devglan.booking.dao;

import java.util.List;

import org.springframework.data.repository.CrudRepository;

import com.devglan.model.Booking;

public interface BookingDao extends CrudRepository<Booking, Long> {
 
 List<Booking> findByCreatedBy(Long userId);

}
Selanjutnya kita buat class service
package com.devglan.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.devglan.booking.dao.BookingDao;
import com.devglan.model.Booking;
import com.devglan.model.UserDetails;
import com.devglan.service.BookingService;
import com.devglan.user.dao.UserDao;

@Service
public class BookingServiceImpl implements BookingService {
 
 @Autowired
 private UserDao userDao;
 
 @Autowired
 private BookingDao bookingDao;

 public List<Booking> findUserBookings(String emailId) {
  UserDetails userdetails = userDao.findByEmail(emailId);
  List<Booking> bookings = bookingDao.findByCreatedBy(userdetails.getId());
  return bookings;
 }
}
package com.devglan.service;

import java.util.List;

import com.devglan.model.Booking;

public interface BookingService {

 List<Booking> findUserBookings(String emailId);
}
Lalu kita buat controller
package com.devglan.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.devglan.model.Booking;
import com.devglan.service.BookingService;

@Controller
@RequestMapping("/booking")
public class BookingController {
 
 @Autowired
 private BookingService bookingService;

 @RequestMapping(value = "/{email:.+}", method = RequestMethod.GET)
 public ResponseEntity<List<Booking>> findUserBookings(@PathVariable(name = "email", value = "email") String email) {

  List<Booking> bookings = bookingService.findUserBookings(email);
  return new ResponseEntity<List<Booking>>(bookings, HttpStatus.OK);
 }

}
Kemudian kita coba jalankan aplikasi yang telah kita buat dan coba kita test di url  -localhost:8080/booking/abc@test.com dan lihat hasilnya

Referensi
- https://www.devglan.com/spring-boot/spring-boot-multiple-database-configuration

Saturday, April 14, 2018

Guide Line Database

Pada blog kali ini, bakal sharing session soal hal fundamental, yaitu database. Dari pengalaman, urusan database kebanyakan akan dihandle oleh back end, dari membuat tabel, membuat data initial, manipulasi data, dll. Padahal persoalan database kadang butuh handling sendiri, back end dan database duo yang saling melengkapi. Sendok dan garpu, piring dan gelas, back end dan database

Kebanyakan kita memberikan semua proses logika di bagian back end, sedangkan database hanya sebagai tempat penyimpanan. Untuk proses yang sederhana mungkin tidak akan jadi masalah, namun untuk yang sangat kompleks back end memiliki keterbatasan dalam waktu pemrosesan. Dari sisi lain, database juga memiliki keterbatasan lain, yaitu tidak dapat dilakukan unit testing untuk SQL dan maintain antar environment yang cukup merepotkan

Jadi kapan proses logika dibebankan di database ?
Berikut sedikit kasus nya

Report
Report di sini adalah kesimpulan dari sekumpulan data yang membentuk suatu pola. Karena biasanya membutuhkan banyak relasi tabel, report lebih baik dimaintain di database dalam bentuk view, atau jika perlu lebih cepat lagi juga dapat membuat function yang memberikan return bentuk tabel walaupun membuat function akan lebih sulit dibuat

Sinkronisasi Data
Dalam beberapa kasus, data yang kita butuhkan berasal dari sistem yang sudah ada, kalau kasus seperti ini ada beberapa pilihan sesuai kebutuhan
Apakah data ini masih di database yang sama ? Jika tidak, maka harus dihandle oleh back end
Apakah data ini sering dipakai ? Jika tidak, maka gunakan view saja. Jika sering, buat tabel bayangan nya dengan kolom seminimal mungkin dan buat prosedur sinkronisasi nya

Manipulasi Data Masif
Manipulasi data adalah perubahan data seperti insert, update, delete. Untuk jumlah data di bawah 20 bisa mengandalkan back end. Untuk jumlah data di atas 50 gunakan procedure atau function dari database. Perbedaan waktu antara proses back end dan proses database bisa berbeda ratusan kali lipat

Data Kompleks atau Loading Lama
Ada kalanya data yang ditampilkan berhubungan dengan banyak tabel, dan membuat kode di back end menjadi rumit dan panjang, yang tentunya juga membuat loading data menjadi lama. Jika proses return berjalan lebih dari 15 detik, maka itu pertanda back end tidak bisa menghandle proses ini. Dibutuhkan proses di database seperti pada report, yaitu dengan view, atau jika perlu lebih cepat dengan function. Penambahan index juga akan sangat membantu.

SQL bagi sebagian orang memang cukup sulit, dan jarang didalami. Butuh data yang banyak sekaligus variatif dan juga kasus-kasus baru untuk membuat query rumit sesuai proses bisnis. Jika ditemukan, jadikan lah sebagai kesempatan belajar. Tanya lah kepada yang berpengalaman jika mengalami kesulitan


Sunday, September 17, 2017

Room Persistence Library

Room adalah bagian dari Android Architecture Components yang diluncurkan oleh Google pada Google I/O Mei 2017. Room dapat membantu kita untuk menangani data persistence pada aplikasi android. Room adalah object mapping library, lebih spesifiknya adalah object relational mapping.

Room memberikan abstraction layer untuk SQLite database. Dengan Room, kita bisa membuat database dengan kode yang sedikit dan sederhana. Dan yang paling keren dari room adalah, room mengerti SQL. Jadi kita sudah memahami SQL, tidak akan sulit untuk menggunakan Room. Untuk membuat relational database dengan Room, kita menggunakan Dao, Entity, dan kelas database Room.

Why Room? 


  • Karena Room menggunakan SQL syntax untuk operasi pada database. Semua orang yang familiar dengan SQL query akan mudah menggunakan Room.
  • Berbeda dengan Realm database yang menggunakan method untuk melakukan query sederhana. Jika kita ingin menggunakan Realm kita harus mempelajari method yang disediakan oleh Realm yang tidak sederhana.
  • Karena SQL syntax yang kita tulis divalidasi saat compile time. Jadi jika kita salah menuliskan SQL, hal ini akan cepat diketahui dan dapat lebih cepat diperbaiki.



Example

Sebelum menggunakan Room, kita harus mengimport library room dengan menggunakan Gradle:

Setelah itu, membuat object entity. object entity adalah object yang menggambarkan table(entity) pada database.

 Lalu membuat object Dao untuk menuliskan operasi yang dapat dilakukan pada database


Setelah itu membuat object Database untuk menjahit Dao dengan object Entity.


Lalu menginisialisasikan Room Database.


Room database siap untuk digunakan.



Reference

Saturday, June 17, 2017

Realm - An Alternative to SQLite

Introduction:
Realm Mobile Database adalah sebuah open source mobile database yang dibuat sebagai alternatif SQLite dan Core Data. Realm lebih cepat dari SQLite dan ORM library lainnya. Realm lebih mudah digunakan, mempunyai API yang simple. Realm juga support untuk beberapa bahasa yaitu Java, Obj-C, Swift, React Native (JavaScript), dan Xamarin.

Keuntungan:
  • Fast!
  • Cross-Platform
  • Easy to use
  • Reduce dev time
  • Good documentation
Berikut adalah grafik perbandingan kecepatan dengan SQLite

 

Pada tulisan ini, saya hanya menunjukkan cara menggunakan Realm dalam Android saja.

Getting Started:
Pertama, pada project build.gradle dalam Android Studio tambahkan library berikut
  Screen Shot 2017-06-15 at 23.17.05.png
Selanjutnya, pada app build.gradle dalam Android Studio tambahkan plugin berikut
Screen Shot 2017-06-15 at 23.17.14.png 
Kemudian buat sebuah class dengan meng-extend aplication untuk membuat konfigurasi Realm yang nantinya ditambahkan kedalam file manifest project. Berikut contoh kofigurasi Realm
Screen Shot 2017-06-15 at 23.32.52.png
  
Selanjutnya konfigurasi tersebut bisa digunakan dimana saja. 
Screen Shot 2017-06-16 at 00.01.52.png 
 
SQLite vs Realm:
Misalnya kita ingin membuat sebuat tabel contact yang berisi kolom id, nama, dan nomor telepon. Dengan SQLite kita bisa membuat tabel seperti ini
 
Screen Shot 2017-06-15 at 14.43.22.png
Bayangkan jika kolom ada lebih dari 10 dst, pasti akan repot sekali ketik satu-satu. Dengan Realm kita tidak perlu repot-repot mengetik seperti SQLite. Kita hanya perlu membuat model class dengan meng-extend RealmObject dan generate getter-setter. Kekurangannya primary key ID pada Realm tidak tersedia auto-increment, sehingga kita harus membuat custom auto-increment atau menggunakan UUID pada java. Contoh model class Realm seperti ini

Screen Shot 2017-06-15 at 14.33.11.png

Selanjutnya untuk contoh method retrieve dalam SQLite seperti berikut
 
Screen Shot 2017-06-15 at 19.53.46.png

SQLite menggunakan cursor untuk mendapatkan hasil query, selanjutnya dikonversi kembali kedalam object java. Hal ini cukup merepotkan, ketika tabel mempunyai kolom yang banyak dan juga kita harus teliti dengan tipe variabel yang akan dikonversikan. Dengan Realm kita hanya perlu beberapa kode baris saja, contohnya seperti 
 
Screen Shot 2017-06-15 at 19.55.32.png
Untuk method add, update, delete tidak jauh berbeda baik SQLite dan Realm.
 
More Features:
Fitur-fitur lain dalam Realm, sebagai berikut
 
1. Query Options
- Conditions:
between(), greaterThan(), lessThan(),
greaterThanOrEqualTo() & lessThanOrEqualTo() 
equalTo() & notEqualTo() 
contains(), beginsWith() & endsWith() 
isNull() & isNotNull() 
beginsWith() & endsWith() & contains()
 
- Conditions Grouping: 
beginGroup() & endGroup() & not() & or()
 
- Sorting 
RealmResults<User> result = realm.where(User.class).findAll(); 
result.sort("age", RealmResults.SORT_ORDER_DESCENDING); 
 
- Querying 
findAll() & findFirst() & findAllSorted() 
 
2. Annotations 
@Required: Memberlakukan pemeriksaan untuk tidak membolehkan nilai null.
@Ignore: Kolom tidak harus disimpan.
@Index: Menambahkan index pencarian pada kolom.
@PrimaryKey: Identifikasi unik pada tiap database.
 
3. And Many More Features . . . .
Masih banyak lagi fitur-fitur yang disediakan oleh Realm, seperti enkripsi, schema, migrations, asynchronous transactions, dynamic realm, UI control,  browser, dll. 
 
Lebih lengkapnya silahkan buka docs realm :))

Terima kasih :D

Referensi: