Kategoriler
Spring Boot

Spring Boot Veritabanı İşlemleri

6. Bölüm: Spring Web ve Spring Data ile RESTful Webservisi üzerinden, Veritabanı – CRUD (Ekleme, Okuma, Güncelleme, Silme) işlemleri yapacağız.

Katmanların Oluşturulması

Spring Boot uygulamasının katmanlarını Spring Boot Mimarisi bölümünde incelemiştik. Bu katmanlar genel olarak klasik bir uygulama için de, mikroservis için de aynıdır. Mikroservis için eklememiz gereken bazı bileşenler ve yapmamız gereken bazı konfigürasyonlar var tabii ki.

Fırsat bulabilirsem yaptığımız bu örneğe bir kaç örnek daha ekleyerek, mikroservis olarak nasıl kullanırızı da yazmaya çalışırım. Ama biraz ipucu ile -eureka, zull, doker, openshift gibi konuları inceleyerek- bir başlangıç yapmayı deneyebilirsiniz.

Projemizi oluştururken eklediğimiz;

Spring Data ile Domain(Entity/Model) ve Repository(DAO) katmanlarını, Spring Web ile de Service ve Controller katmanlarını oluşturup, uygulamamızı tamamlayacağız.

1.Domain Katmanı:

Domain katmanında POJO sınıflarımız vardır. Bu sınıfları Spring Data(JPA/Hibernate) kullanarak, ORM yöntemi ile veritabanı tablolarına dönüştürürüz. Bu anotasyonlar sadece Springe özel değildir. JPA/Hibernate kullanılan bütün Java projeleri için geçerlidir.‌

Bilmeyenler için kullanacağımız temel anotasyonların en önemlilerini kısaca özetleyeceğim:‌

@Entity: Sınıfı, veritabanı tablosuna eşitler.‌

@Table(name = “tablo_adı”): Tablonun ismi sınıftan farklı olacaksa kullanılır. Kullanılmazsa zaten tablo sınıfın adını alır.

@Id: Her tablonun olmazsa olmazı olan ID’sinin annotasyonudur.‌

@NotNull: Boş olmaması gereken alanlar için kullanılır.‌

@Column(name = “title”, length = 300): Kolon adı ve uzunluğu vermek için kullanılır.‌

@GeneratedValue(strategy = GenerationType.AUTO/IDENTITY/ SEQUENCE): Oracle, PostgreSQL’de, ID’yi otomaik artırma SEQUENCE yardımı ile yapıyor. MS SQL Server ve MySQL ise IDENTITY yardımı ile… Bir de durumu veritabanının kontrolüne bırakan AUTO vardır, h2 gibi veritabanlarında kullanılır.‌

JPA Anotasyonları
/*
 * AUTO, IDENTITY, SEQUENCE ÖRNEKLERİ
 */
// AUTO - H2 vb.
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "example_id", length = 20)
private Long exampleId;
// AUTO 
@Id
  @GeneratedValue(strategy = GenerationType.IDENTİTY)
@Column(name = "example_id", length = 20)
private Long exampleId;
// SEQUENCE - Oracle, PostgresSQL
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_EXAMPLE_ID")
@SequenceGenerator(name = "SEQ_EXAMPLE_ID", sequenceName = "SEQ_EXAMPLE_ID", allocationSize = 1, initialValue = 1000)
/* name: Uygulama içinde Sequence’in adını tanımlamaya yarar.
 * sequenceName: verilen string’i veritabanı katmanında sequence adı olarak tanıtır.
 * initialValue: id'in kaçtan başlayacağını belirtir.
 * allocationSize: Cache yapısı içinde, max. kaç id tutlacağını belirtmeye yarar.
 */
@Column(name = "example_id", length = 20)
private Long exampleId;

NOT

@SQLDelete: Silme (delete) işlemi esnasında bir sql işlemi yapılacaksa, buna yardımcı olur. Bu annotasyon tıpkı Entity annotasyonu gibi sınıfın üzerine yazılır. Biz kullanmadık ama işinize yarar diye paylaştım. Aynı şeyi; insert ve update içinde kullanabilirsiniz. @SQLInsert, @SQLUpdate @SQLDeleteAll gibi annotastonlar da mevcuttur.

Örneğin; aşağıdaki örnekte silme esnasında veriyi silmez, silindi olarak günceller.

SQLDelete(sql="UPDATE post SET status = 'deleted' WHERE id = ?")

Post adında bir Entity sınıfı oluşturacağız. Bu sınıf, içindeki üç alan (id, title, content) olan post adında bir tablo oluşturacak. Eskiden Entity sınıflarımız, serializable arayüzüne implemente edilerek serileştirilirdi. Ama Spring Data bünyesindeki repository katmanında kullandığımız arayüz bu işlemi otomatik olarak yapmaktadır.

package com.vedatyildirim.basiccrudexample.domain;
import lombok.*;
import javax.persistence.*;

@Entity
@Table(name="post")
@Data
public class Post {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id", unique = true, nullable = false)
    private Long Id;

    @Column(name = "title", length = 200)
    private String title;

    @Column(name = "content", length = 500)
    private String content;
}

2. Repository Katmanı:

Bu katman, klasik Java uygulamalarındaki DAO katmanıdır. Sağladığı altyapı (CrudRepository, PagingAndSortingRepository ,JpaRepository) ile Entity sınıfından aldığı veriler üzerinde CRUD işlemleri gibi şeyleri yapmamızı sağlayan arayüzler sunar. Bu arayüzlerler, serileştirme (Serializable) işlemi için kullanılan arayüze de bağlıdırlar. Detayları linktedir.

package com.vedatyildirim.basiccrudexample.repository;

import com.vedatyildirim.basiccrudexample.domain.Post;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface PostRepository extends CrudRepository<Post, Long> {

}

3. DTO Katmanı:

DTO katmanını, ModelMapper kullanarak veya klasik yöntemlerle oluşturabiliriz. Entity katmanına, Service ve Controller katmanlarının doğrudan erişmesini engellemek için kullanılır. Yanı sıra parola vs. gibi özel alanlara erişimi de kısıtlar ve Entityleri daha güvenli hale getirir. Bizim örneğimizde olduğu gibi her zaman gerekli değildir ve kullanmak gerekmez.

4. Servis Katmanı:

Servis katmanı, repository katmanı üzerinden giderek domain katmanından alınan verileri controllera aktarır. Aynı zamanda uygulamada yapılması gereken mantıksal işlemler (iş/business) bu katmanda yapılır.

package com.vedatyildirim.basiccrudexample.service;

import com.vedatyildirim.basiccrudexample.domain.Post;
import com.vedatyildirim.basiccrudexample.repository.PostRepository;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.Optional;

@Service
@Transactional
@AllArgsConstructor
public class PostService {

    @Autowired
    private PostRepository postRepository;

    public Iterable<Post> findAll() {
        return postRepository.findAll();
    }

    public Optional<Post> findById(Long id) {
        return postRepository.findById(id);
    }

    public Post save(Post post) {
        return postRepository.save(post);
    }

    public void deleteById(Long id) {
        postRepository.deleteById(id);
    }
    
}

5. Controller Katmanı:

Controller katmanı, uygulamanın client/tarayıcı ile konuşan ara birimidir. HTTP üzerinden gelen istekleri, RESTful yöntemleriyle yorumlayarak; GET, POST, PUT, DELETE gibi istekleri alır ve CRUD işlemleri gibi şeyleri yapmamızı sağlar.

package com.vedatyildirim.basiccrudexample.controller;

import com.vedatyildirim.basiccrudexample.domain.Post;
import com.vedatyildirim.basiccrudexample.service.PostService;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.lang.Long;
import java.util.Optional;

@RequiredArgsConstructor
@RequestMapping("/api")
@RestController
public class PostController {

    @Autowired
    private PostService postService;

    private static final Logger log = LoggerFactory.getLogger(PostController.class);

    @GetMapping("/")
    public ResponseEntity<Iterable<Post>> getAll() {
        return ResponseEntity.ok(postService.findAll());
    }

    @GetMapping("/{id}")
    public ResponseEntity<Post> findById(@PathVariable Long id) {
        Optional<Post> post = postService.findById(id);
        if (!post.isPresent()) {
            log.error("Id " + id + " is not existed");
            ResponseEntity.badRequest().build();
        }
        return ResponseEntity.ok(post.get());
    }


    @PostMapping("/")
    public ResponseEntity create(@RequestBody Post post) {
        return ResponseEntity.ok(postService.save(post));
    }

    @PutMapping("/{id}")
    public ResponseEntity<Post> update(@PathVariable Long id, @Valid @RequestBody Post post) {
        if (!postService.findById(id).isPresent()) {
            log.error("Id " + id + " is not existed");
            ResponseEntity.badRequest().build();
        }
        return ResponseEntity.ok(postService.save(post));
    }

    @DeleteMapping("/{id}")
    public ResponseEntity delete(@PathVariable Long id) {
        if (!postService.findById(id).isPresent()) {
            log.error("Id " + id + " is not existed");
            ResponseEntity.badRequest().build();
        }
        postService.deleteById(id);
        return ResponseEntity.ok().build();
    }
}

Şimdi, IDE’mizin run tuşuna basarak uygulamamızı çalıştırırız. Tarayıcıdan localhost:9090 adresine gittiğimizde yayında olduğunu görürüz.

Bir Cevap Yazın