Skip to content

5. Spring Data'ya Giriş JPA

Bu bölümde, aşağıdaki mimariyi inceleyeceğiz:

SGBD'in [DAO] katmanı ile JDBC sürücüsü arasına bir [JPA] katmanı (Java Persistence API) yerleştiriyoruz. Artık JPA katmanı, SGBD'e yönelik SQL komutlarını gönderiyor. [DAO] katmanı artık SQL komutlarını işlemiyor; yalnızca, kullanılan veritabanındaki çeşitli tabloların görüntüleri olan JPA varlıkları adı verilen nesneleri işliyor. Bu varlıkların alanları, Java açıklamaları sayesinde tablo sütunlarıyla benzersiz bir şekilde ilişkilendirilmiştir. Bu sayede JPA katmanı, [DAO] katmanında JPA varlıklar üzerinde gerçekleştirilen işlemleri SQL'e çevirebilir.

Spring Data, verilerin ister SGBDR ilişkisel veritabanında, ister NOSQL veritabanında, ister başka tür depolama alanlarında bulunmasına bakılmaksızın, verilere erişimle ilgilenen bir Spring dalıdır. Burada yalnızca SGBDR'leri ve bunlara JPA aracılığıyla erişimi ele alacağız. Daha sonra, aslında [Spring Data JPA]'i kastetmek için [Spring JPA] yazabileceğiz. Yukarıdaki mimaride, [Spring Data] katmanı, [DAO] katmanına JPA varlıklarını yönetme kolaylıkları sağlar.

JPA aslında bir spesifikasyondur. Bu spesifikasyonun üç farklı uygulamasını test edeceğiz:

5.1. Exemple-01

Spring web sitesinde, Spring'e başlamak için birçok öğretici bulunmaktadır ([http://spring.io/guides]). Spring Data'yı tanıtmak için bunlardan birini kullanacağız. Bunun için Spring Tool Suite'i kullanıyoruz (STS).

  • [1]'te, [spring.io/guides]'teki öğreticilerden birini içe aktarıyoruz;
  • [2]'te, Spring Data ile bir veritabanına nasıl erişileceğini gösteren [Accessing Data Jpa] öğreticisini seçiyoruz;
  • [3]'e dönüştürdükten sonra, Maven ile yapılandırılmış bir proje seçiyoruz;
  • [4]'te, öğretici iki şekilde sunulabilir: öğreticiyi takip ederek doldurulacak boş bir sürüm olan [initial] veya öğreticinin son hali olan [complete]. Biz ikincisini seçiyoruz;
  • [5]'te, öğreticiyi bir tarayıcıda görüntülemeyi seçebiliriz;
  • [6]'te ise son halini görebiliriz.

5.1.1. Projenin Maven yapılandırması

Projenin Maven bağımlılıkları [pom.xml] dosyasında yapılandırılmıştır:


    <groupId>org.springframework</groupId>
    <artifactId>gs-accessing-data-jpa</artifactId>
    <version>0.1.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.3.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>
    </dependencies>

    <properties>
        <!-- her şey için UTF-8'i kullan -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <start-class>hello.Application</start-class>
</properties>
  • 5-9. satırlar: bir üst Maven projesini tanımlar. Projenin bağımlılıklarının büyük kısmını bu proje tanımlar. Bu bağımlılıklar yeterli olabilir; bu durumda başka bağımlılık eklenmez, ya da yeterli olmayabilir; bu durumda eksik bağımlılıklar eklenir;
  • 12-15. satırlar: [spring-boot-starter-data-jpa]'e bir bağımlılık tanımlar. Bu artefakt, Spring Data sınıflarını içerir;
  • satır 16-19: bellek içi veritabanları oluşturmaya ve yönetmeye olanak tanıyan SGBD ve H2'e bir bağımlılık tanımlar.

Bu bağımlılıkların getirdiği sınıflara bir göz atalım:

Bunların sayısı oldukça fazla:

  • bazıları Spring ekosistemine aittir (spring ile başlayanlar);
  • diğerleri ise Hibernate ekosistemine aittir (hibernate, jboss); burada JPA uygulamasını kullanıyoruz;
  • bazıları test kütüphaneleridir (junit, hamcrest);
  • bazıları ise günlük kütüphaneleridir (log4j, logback, slf4j);

Hepsini saklayacağız. Üretim ortamındaki bir uygulama için ise sadece gerekli olanları saklamak gerekir.

[pom.xml] dosyasının 26. satırında şu satır yer almaktadır:


<start-class>hello.Application</start-class>

Bu satır, aşağıdaki satırlarla bağlantılıdır:


<build>
        <plugins>
            <plugin> 
                <artifactId>maven-compiler-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

6-9. satırlarda, [spring-boot-maven-plugin] eklentisi uygulamanın çalıştırılabilir jar dosyasını oluşturmaya olanak tanır. [pom.xml] dosyasının 26. satırı ise bu jar dosyasının çalıştırılabilir sınıfını belirtir.

5.1.2. [JPA] katmanı

Veritabanına erişim, [JPA] katmanı ve Java Persistence API aracılığıyla gerçekleştirilir:

  

Uygulama basittir ve [Customer] istemcilerini yönetir. [Customer] sınıfı, [JPA] katmanının bir parçasıdır ve şu şekildedir:


package hello;

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

@Entity
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    private String firstName;
    private String lastName;

    protected Customer() {
    }

    public Customer(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    @Override
    public String toString() {
        return String.format("Customer[id=%d, firstName='%s', lastName='%s']", id, firstName, lastName);
    }

}

Bir müşterinin kimliği [id], adı [firstName] ve soyadı [lastName]'tir. Her [Customer] örneği, veritabanındaki bir tablonun bir satırını temsil eder.

  • 8. satır: JPA anotasyonu, [Customer] örneklerinin kalıcılığının (Oluşturma, Okuma, Güncelleme, Silme) JPA uygulaması tarafından yönetileceğini belirtir. Maven bağımlılıklarına göre, JPA / Hibernate uygulamasının kullanıldığı görülmektedir;
  • 11-12. satırlar: JPA anotasyonları, [id] alanını [Customer] tablosunun birincil anahtarıyla ilişkilendirir. 12. satır, JPA uygulamasının, kullanılan SGBD'e özgü birincil anahtar oluşturma yöntemini (burada H2) kullanacağını belirtir;

JPA için başka bir açıklama bulunmamaktadır. Bu durumda varsayılan değerler kullanılacaktır:

  • [Customer] tablosu, sınıfın adını taşıyacaktır, yani [Customer];
  • bu tablonun sütunları, sınıfın alanlarının adlarını taşıyacaktır: [id, firstName, lastName]; tablonun sütun adlarında büyük/küçük harf ayrımı yapılmadığını unutmayın;

Kullanılan JPA uygulamasının hiçbir aşamada adı geçmediğine dikkat edilmelidir.

5.1.3. [Spring Data] katmanı

[CustomerRepository] sınıfı, [Customer] tablosuna erişim katmanını uygular. Kod şöyledir:

  

package hello;

import java.util.List;

import org.springframework.data.repository.CrudRepository;

public interface CustomerRepository extends CrudRepository<Customer, Long> {

    List<Customer> findByLastName(String lastName);
}

Dolayısıyla bu bir sınıf değil, bir arayüzdür (7. satır). Spring Data'ya ait [CrudRepository] arayüzünü genişletir (5. satır). Bu arayüz iki türle tanımlanmıştır: ilki, yönetilen öğelerin türü; burada [Customer] türüdür; ikincisi ise yönetilen öğelerin birincil anahtarının türü; burada [Long] türüdür. [CrudRepository] arayüzü şöyledir:


package org.springframework.data.repository;

import java.io.Serializable;

@NoRepositoryBean
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {

    <S extends T> S save(S entity);

    <S extends T> Iterable<S> save(Iterable<S> entities);

    T findOne(ID id);

    boolean exists(ID id);

    Iterable<T> findAll();

    Iterable<T> findAll(Iterable<ID> ids);

    long count();

    void delete(ID id);

    void delete(T entity);

    void delete(Iterable<? extends T> entities);

    void deleteAll();
}

Bu arayüz, JPA T türü üzerinde gerçekleştirilebilecek CRUD işlemlerini (Oluştur – Oku – Güncelle – Sil) tanımlar:

  • 8. satır: save yöntemi, bir T varlığının veritabanında kalıcı hale getirilmesini sağlar. Bu yöntem, varlığı SGBD tarafından verilen birincil anahtarla kalıcı hale getirir. Ayrıca, id birincil anahtarıyla tanımlanan bir T varlığının güncellenmesini de sağlar. Hangi işlemin gerçekleştirileceği, id birincil anahtarının değerine göre belirlenir: bu değer null ise kalıcılaştırma işlemi gerçekleştirilir, aksi takdirde güncelleme işlemi gerçekleştirilir;
  • 10. satır: Aynı işlem, ancak bu kez bir varlık listesi için;
  • 12. satır: findOne yöntemi, birincil anahtarı id ile tanımlanan bir T varlığını bulmaya olanak tanır;
  • 22. satır: delete yöntemi, birincil anahtarı id ile tanımlanan bir T varlığını silmeye yarar;
  • satır 24-28: [delete] yönteminin varyantları;
  • satır 16: [findAll] yöntemi, kalıcı T varlıklarının tümünü bulmaya yarar;
  • satır 18: aynısı, ancak tanımlayıcı listesi verilen varlıklarla sınırlıdır;

[CustomerRepository] arayüzüne geri dönelim:


package hello;

import java.util.List;

import org.springframework.data.repository.CrudRepository;

public interface CustomerRepository extends CrudRepository<Customer, Long> {

    List<Customer> findByLastName(String lastName);
}
  • 9. satır, [Customer]'i [lastName] adıyla bulmaya olanak tanır;

[DAO] katmanı ile ilgili bilgiler bu kadar. Önceki arayüzün bir uygulama sınıfı yoktur. Bu sınıf, yürütme sırasında [Spring Data] tarafından oluşturulur. [CrudRepository] arayüzünün yöntemleri otomatik olarak uygulanır. [CustomerRepository] arayüzüne eklenen yöntemler için ise durum değişebilir. [Customer] tanımına geri dönelim:


    private long id;
    private String firstName;
private String lastName;
  1. satırdaki yöntem, [Customer]'in [lastName] alanına (3. satır) referans verdiği için [Spring Data] tarafından otomatik olarak uygulanır. Spring Data, uygulanacak arayüzde bir [findBySomething] yöntemi ile karşılaştığında, bunu aşağıdaki JPQL (Java Persistence Query Language) sorgusu ile uygular:
select t from T t where t.something=:value

Bu nedenle, T türünün [something] adlı bir alanı olması gerekir. Böylece

List<Customer> findByLastName(String lastName);

yöntemi, aşağıdakine benzer bir kodla uygulanacaktır:

return [em].createQuery("select c from Customer c where c.lastName=:value").setParameter("value",lastName).getResultList()

burada [em], JPA kalıcılık bağlamını ifade eder. Bu, ancak [Customer] sınıfında [lastName] adlı bir alan varsa mümkündür; ki bu durum söz konusudur.

Sonuç olarak, basit durumlarda Spring Data, [DAO] katmanını basit bir arayüzle uygulamamıza olanak tanır.

5.1.4. [console] katmanı

  

[Application] sınıfı şu şekildedir:


package hello;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application implements CommandLineRunner {

    @Autowired
    CustomerRepository repository;
    
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }

    @Override
    public void run(String... strings) throws Exception {
        // birkaç müşteriyi kaydet
        repository.save(new Customer("Jack", "Bauer"));
        repository.save(new Customer("Chloe", "O'Brian"));
        repository.save(new Customer("Kim", "Bauer"));
        repository.save(new Customer("David", "Palmer"));
        repository.save(new Customer("Michelle", "Dessler"));

        // tüm müşterileri getir
        System.out.println("Customers found with findAll():");
        System.out.println("-------------------------------");
        for (Customer customer : repository.findAll()) {
            System.out.println(customer);
        }
        System.out.println();

        // ID ile tek bir müşteriyi getir
        Customer customer = repository.findOne(1L);
        System.out.println("Customer found with findOne(1L):");
        System.out.println("--------------------------------");
        System.out.println(customer);
        System.out.println();

        // müşterileri soyadına göre getir
        System.out.println("Customer found with findByLastName('Bauer'):");
        System.out.println("--------------------------------------------");
        for (Customer bauer : repository.findByLastName("Bauer")) {
            System.out.println(bauer);
        }
    }

}
  • 9. satır: Sınıf, [CommandLineRunner] arayüzünü uygular; bu arayüz, [Spring Boot] (4. satır) arayüzüdür. Bu arayüzün tek bir yöntemi vardır, o da 19. satırdaki yöntemdir;
  • 8. satır: @SpringBootApplication, birkaç [Spring Boot] anotasyonunu bir araya getiren bir anotasyondur:
    • @Configuration: sınıfın bir yapılandırma sınıfı olduğunu belirtir;
    • @EnableAutoConfiguration: [Spring Boot]’ten, çeşitli özelliklere, özellikle de projenin Classpath içeriğine bağlı olarak belirli sayıda bean’i kendisi oluşturmasını ister. Hibernate kütüphaneleri Classpath'te bulunduğu için, [entityManagerFactory] bean'i Hibernate ile uygulanacaktır. SGBD ve H2 kütüphaneleri Classpath'te bulunduğundan, [dataSource] bean'i H2 ile uygulanacaktır. [dataSource] bean'inde kullanıcı adı ve şifre de tanımlanmalıdır. Burada Spring Boot, H2'in varsayılan yöneticisini kullanacaktır; bu, şifresizdir. [spring-tx] kütüphanesi Classpath'te bulunduğu için Spring'in işlem yöneticisi kullanılacaktır;
    • @EnableWebMvc: Classpath'te [spring-mvc] kütüphanesi varsa. Bu durumda, web uygulaması için otomatik bir yapılandırma yapılır;
    • @ComponentScan: Spring'e diğer bean'leri, yapılandırmaları ve hizmetleri nerede arayacağını bildirir. Burada bunlar varsayılan olarak etiketli sınıfın bulunduğu pakette, yani [hello] paketinde aranır. Böylece [Customer] ve [CustomerRepository] sınıfları bulunacaktır. İlki [@Entity] anotasyonuna sahip olduğu için Hibernate tarafından yönetilecek bir varlık olarak kataloglanacaktır. İkincisi ise [CrudRepository] arayüzünü genişlettiği için bir Spring bean'i olarak kaydedilecektir;
  • 11-12. satırlar: [CustomerRepository] bean'i ana sınıfın koduna enjekte edilir;
  • 15. satır: Spring Boot projesindeki [SpringApplication] sınıfının [run] statik yöntemi yürütülür. Bu yöntemin parametresi, [Configuration] veya [EnableAutoConfiguration] anotasyonuna sahip sınıftır. Daha önce açıklanan tüm süreç bu aşamada gerçekleşir. Sonuç, bir Spring uygulama bağlamıdır, yani Spring tarafından yönetilen bir dizi bean;
  • 19-48. satırlar: Aşağıdaki işlemler, yalnızca [CustomerRepository] arayüzünü uygulayan bean'in yöntemlerini kullanır;

Konsol çıktıları şu şekildedir:

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.2.2.RELEASE)

2015-03-10 15:35:43.661  INFO 5784 --- [           main] hello.Application                        : Starting Application on Gportpers3 with PID 5784 (started by ST in C:\Users\Serge Tahé\Documents\workspace-sts-3.6.3.RELEASE\gs-accessing-data-jpa-complete)
2015-03-10 15:35:43.708  INFO 5784 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5d11346a: startup date [Tue Mar 10 15:35:43 CET 2015]; root of context hierarchy
2015-03-10 15:35:45.230  INFO 5784 --- [           main] j.LocalContainerEntityManagerFactoryBean : Building JPA container EntityManagerFactory for persistence unit 'default'
2015-03-10 15:35:45.254  INFO 5784 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [
    name: default
    ...]
2015-03-10 15:35:45.331  INFO 5784 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate Core {4.3.8.Final}
2015-03-10 15:35:45.332  INFO 5784 --- [           main] org.hibernate.cfg.Environment            : HHH000206: hibernate.properties not found
2015-03-10 15:35:45.334  INFO 5784 --- [           main] org.hibernate.cfg.Environment            : HHH000021: Bytecode provider name : javassist
2015-03-10 15:35:45.651  INFO 5784 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {4.0.5.Final}
2015-03-10 15:35:45.754  INFO 5784 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
2015-03-10 15:35:45.877  INFO 5784 --- [           main] o.h.h.i.ast.ASTQueryTranslatorFactory    : HHH000397: Using ASTQueryTranslatorFactory
2015-03-10 15:35:46.154  INFO 5784 --- [           main] org.hibernate.tool.hbm2ddl.SchemaExport  : HHH000227: Running hbm2ddl schema export
2015-03-10 15:35:46.169  INFO 5784 --- [           main] org.hibernate.tool.hbm2ddl.SchemaExport  : HHH000230: Schema export complete
2015-03-10 15:35:46.779  INFO 5784 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
Customers found with findAll():
-------------------------------
Customer[id=1, firstName='Jack', lastName='Bauer']
Customer[id=2, firstName='Chloe', lastName='O'Brian']
Customer[id=3, firstName='Kim', lastName='Bauer']
Customer[id=4, firstName='David', lastName='Palmer']
Customer[id=5, firstName='Michelle', lastName='Dessler']

Customer found with findOne(1L):
--------------------------------
Customer[id=1, firstName='Jack', lastName='Bauer']

Customer found with findByLastName('Bauer'):
--------------------------------------------
Customer[id=1, firstName='Jack', lastName='Bauer']
Customer[id=3, firstName='Kim', lastName='Bauer']
2015-03-10 15:35:47.040  INFO 5784 --- [           main] hello.Application                        : Started Application in 3.623 seconds (JVM running for 4.324)
2015-03-10 15:35:47.042  INFO 5784 --- [       Thread-1] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@5d11346a: startup date [Tue Mar 10 15:35:43 CET 2015]; root of context hierarchy
2015-03-10 15:35:47.044  INFO 5784 --- [       Thread-1] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown
2015-03-10 15:35:47.046  INFO 5784 --- [       Thread-1] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2015-03-10 15:35:47.047  INFO 5784 --- [       Thread-1] org.hibernate.tool.hbm2ddl.SchemaExport  : HHH000227: Running hbm2ddl schema export
2015-03-10 15:35:47.051  INFO 5784 --- [       Thread-1] org.hibernate.tool.hbm2ddl.SchemaExport  : HHH000230: Schema export complete
  • 1-8. satırlar: Spring Boot projesinin logosu;
  • 9. satır: [hello.Application] sınıfı çalıştırılır;
  • 10. satır: [AnnotationConfigApplicationContext], Spring'in [ApplicationContext] arayüzünü uygulayan bir sınıftır. Bu, bir bean konteyneridir;
  • satır 11: [entityManagerFactory] bean'i, bir Spring sınıfı olan [LocalContainerEntityManagerFactory] sınıfı ile uygulanır. Bu sınıf, [JPA] katmanını yönetir;
  • 12. satır: [Hibernate] ortaya çıkıyor. Seçilen, JPA adlı bu uygulamadır;
  • 19. satır: SGBD ile birlikte kullanılacak Hibernate lehçesi, SQL varyantıdır. Burada [H2Dialect] lehçesi, Hibernate'in SGBD ve H2 ile çalışacağını gösterir;
  • 21-22. satırlar: veritabanı oluşturulur. [CUSTOMER] tablosu oluşturulur. Bu, Hibernate'in JPA tanımlarından tabloları oluşturacak şekilde yapılandırıldığı anlamına gelir; burada [Customer] sınıfının JPA tanımı kullanılır;
  • 26-30. satırlar: arayüzün [findAll] yönteminin sonucu;
  • 34. satır: arayüzün [findOne] yönteminin sonucu;
  • 38-39. satırlar: [findByLastName] yönteminin sonuçları;
  • 41. satır ve sonrası: Spring bağlamının kapatılmasına ait günlükler.

5.1.5. Spring Data projesinin manuel yapılandırması

Önceki projeyi [gs-accessing-data-jpa-02] projesinde kopyalıyoruz:

  

Bu yeni projede, Spring Boot tarafından yapılan otomatik yapılandırmaya güvenmeyeceğiz. Yapılandırmayı manuel olarak gerçekleştireceğiz. Varsayılan yapılandırmalar bize uymuyorsa bu yararlı olabilir.

Öncelikle, [pom.xml] dosyasında gerekli bağımlılıkları açıkça belirteceğiz:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.springframework</groupId>
    <artifactId>gs-accessing-data-jpa-02</artifactId>
    <version>0.1.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.3.RELEASE</version>
    </parent>

    <dependencies>
        <!-- Spring Data -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
        </dependency>
        <!-- Hibernate -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
        </dependency>
        <!-- H2 Veritabanı -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>
        <!-- Tomcat JDBC -->
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-jdbc</artifactId>
        </dependency>
    </dependencies>

    <properties>
        <!-- her şey için UTF-8 kullanın -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-releases</id>
            <name>Spring Releases</name>
            <url>https://repo.spring.io/libs-release</url>
        </repository>
        <repository>
            <id>org.jboss.repository.releases</id>
            <name>JBoss Maven Release Repository</name>
            <url>https://repository.jboss.org/nexus/content/repositories/releases</url>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>spring-releases</id>
            <name>Spring Releases</name>
            <url>https://repo.spring.io/libs-release</url>
        </pluginRepository>
    </pluginRepositories>

</project>
  • 10-14. satırlar: Tanımladığı kütüphaneleri kullanacağımız üst Maven projesi;
  • 18-21. satırlar: Veritabanına erişmek için kullanılan Spring Data;
  • 23-26. satırlar: JPA spesifikasyonunun Hibernate uygulaması;
  • 28-31. satırlar: SGBD ve H2;
  • satır 33-36: Veritabanları genellikle, bağlantının tekrar tekrar açılıp kapatılmasını önleyen açık bağlantı havuzlarıyla kullanılır. Burada kullanılan uygulama [tomcat-jdbc]'tir;

Yeni projede, [Customer] varlığı ve [CustomerRepository] arayüzü değişmez. [Application] sınıfını değiştireceğiz; bu sınıf iki sınıfa bölünecek:

  • [Config], yapılandırma sınıfı olacak;
  • [Main], bu da çalıştırılabilir sınıf olacaktır;
  

Çalıştırılabilir sınıf [Application] artık şu şekildedir:


package console;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import repositories.CustomerRepository;
import config.AppConfig;
import entities.Customer;

public class Application {
    public static void main(String[] args) {
        // Spring bağlamı oluşturma
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        CustomerRepository repository = context.getBean(CustomerRepository.class);

        // birkaç müşteriyi kaydetme
        repository.save(new Customer("Jack", "Bauer"));
        repository.save(new Customer("Chloe", "O'Brian"));
        repository.save(new Customer("Kim", "Bauer"));
        repository.save(new Customer("David", "Palmer"));
        repository.save(new Customer("Michelle", "Dessler"));

        ...

        // bağlamın kapatılması
        context.close();
    }

}
  • 9. satır: [Application] sınıfında artık yapılandırma açıklamaları bulunmamaktadır;
  • 3-7. satırlar: [Spring Boot] paketlerinin artık içe aktarılmadığına dikkat edin;
  • 12. satır: Spring bean'leri oluşturulur. Bu şekilde oluşturulan bean'lerin referansını içeren Spring bağlamı elde edilir;
  • 13. satır: [CustomerRepository] türündeki bean'e bir referans talep edilir;

Projeyi yapılandıran [Config] sınıfı aşağıdaki gibidir:


package config;

import javax.persistence.EntityManagerFactory;

import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@EnableTransactionManagement
@EnableJpaRepositories(basePackages = { "repositories" })
@Configuration
// @ComponentScan(basePackages={"package1","package2"})
public class AppConfig {

    // veritabanı H2
    @Bean
    public DataSource dataSource() {
        // veri kaynağı TomcatJdbc
        DataSource dataSource = new DataSource();
        // erişim yapılandırması JDBC
        dataSource.setDriverClassName("org.h2.Driver");
        dataSource.setUrl("jdbc:h2:./demo");
        dataSource.setUsername("sa");
        dataSource.setPassword("");
        // başlangıçta açık bir bağlantı
        dataSource.setInitialSize(1);
        // sonuç
        return dataSource;
    }

    // sağlayıcı JPA
    @Bean
    public JpaVendorAdapter jpaVendorAdapter() {
        HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
        hibernateJpaVendorAdapter.setShowSql(false);
        hibernateJpaVendorAdapter.setGenerateDdl(true);
        hibernateJpaVendorAdapter.setDatabase(Database.H2);
        return hibernateJpaVendorAdapter;
    }

    // EntityManagerFactory
    @Bean
    public EntityManagerFactory entityManagerFactory(JpaVendorAdapter jpaVendorAdapter, DataSource dataSource) {
        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(jpaVendorAdapter);
        factory.setPackagesToScan("entities");
        factory.setDataSource(dataSource);
        factory.afterPropertiesSet();
        return factory.getObject();
    }

    // İşlem yöneticisi
    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager txManager = new JpaTransactionManager();
        txManager.setEntityManagerFactory(entityManagerFactory);
        return txManager;
    }

}
  • 17. satır: [@EnableTransactionManagement] anotasyonu, [@Transactional] anotasyonlarının yorumlanması gerektiğini belirtir. [CrudRepository] arayüzlerinin yöntemleri bu anotasyonlara sahiptir. Bu yöntemler bir işlem içinde yürütülür;
  • 18. satır: [@EnableJpaRepositories] anotasyonu, Spring Data [CrudRepository] arayüzlerinin bulunduğu klasörleri belirtmeye yarar. Bu arayüzler Spring bileşenlerine dönüşecek ve Spring bağlamında kullanılabilir hale gelecektir;
  • 19. satır: [@Configuration] anotasyonu, [Config] sınıfını bir Spring yapılandırma sınıfı haline getirir;
  • 20. satır: [@ComponentScan] anotasyonu, Spring bileşenlerinin aranacağı klasörlerin listelenmesini sağlar. Spring bileşenleri, @Service, @Component, @Controller gibi Spring anotasyonlarıyla etiketlenmiş sınıflardır. Burada, [AppConfig] sınıfında tanımlananlar dışında başka bileşen bulunmadığından, anotasyon yorum satırına alınmıştır;
  • 24-37. satırlar: Veri kaynağını, yani H2 veritabanını tanımlar. Bu yöntemle oluşturulan nesneyi Spring tarafından yönetilen bir bileşen haline getiren, 25. satırdaki @Bean anotasyonudur. Yöntemin adı burada herhangi bir şey olabilir. Ancak, 51. satırdaki EntityManagerFactory yoksa ve otomatik yapılandırma ile tanımlanmışsa, bu yöntemin adı [dataSource] olmalıdır;
  • 30. satır: Veritabanının adı [demo] olacak ve proje klasöründe oluşturulacaktır;
  • satır 40-47: Kullanılan JPA uygulamasını tanımlar; burada bir Hibernate uygulamasıdır. Yöntemin adı burada herhangi bir şey olabilir;
  • 43. satır: SQL için günlük kaydı yok;
  • satır 44: veritabanı mevcut değilse oluşturulacaktır;
  • satır 50-58: JPA'in kalıcılığını yönetecek olan EntityManagerFactory'i tanımlar. Yöntemin adı mutlaka [entityManagerFactory] olmalıdır;
  • satır 51: Yöntem, daha önce tanımlanan iki bean'in türünde iki parametre alır. Bunlar daha sonra oluşturulur ve Spring tarafından yöntemin parametreleri olarak enjekte edilir;
  • satır 53: Kullanılacak JPA uygulaması belirlenir;
  • satır 54: JPA varlıklarının bulunacağı klasörleri belirler;
  • satır 55: yönetilecek veri kaynağını belirler;
  • satır 61-66: işlem yöneticisi. Yöntemin adı mutlaka [transactionManager] olmalıdır. Bu yöntem, satır 51-58'deki bean'i parametre olarak alır;
  • 64. satır: işlem yöneticisi, EntityManagerFactory ile ilişkilendirilir;

Yukarıdaki yöntemler herhangi bir sırayla tanımlanabilir.

Projenin çalıştırılması aynı sonuçları verir. Proje klasöründe, H2 adlı yeni bir veritabanı dosyası belirir:

  

5.1.6. Çalıştırılabilir arşiv oluşturma

Projenin çalıştırılabilir arşivini oluşturmak için şu adımları uygulayabilirsiniz:

  • [1]'te: bir yürütme yapılandırması oluşturulur;
  • [2]'te: [Java Application] türünde
  • [3]: çalıştırılacak projeyi belirtir (Browse düğmesini kullanın);
  • [4]'te: yürütülecek sınıfı belirtir;
  • [5]: çalıştırma yapılandırmasının adı – herhangi bir isim olabilir;
  • [6]'te: proje dışa aktarılır;
  • [7]'te: JAR yürütülebilir bir arşiv biçiminde;
  • [8]: oluşturulacak yürütülebilir dosyanın yolunu ve adını belirtir;
  • [9]'te: [5]'te oluşturulan çalıştırma yapılandırmasının adı;
10
  
  • [10]'te, oluşturulan arşiv;

Bu işlem tamamlandıktan sonra, yürütülebilir arşivin bulunduğu klasörde bir konsol açılır:

.....\dist>dir
12/06/2014  09:11        15 104 869 gs-accessing-data-jpa-02.jar

Arşiv şu şekilde çalıştırılır:


.....\dist>java -jar gs-accessing-data-jpa-02.jar

Konsolda elde edilen sonuçlar şunlardır:

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder daha fazla ayrıntı için.
mars 10, 2015 5:27:20 PM org.hibernate.jpa.internal.util.LogHelper logPersistenceUnitInformation
INFO: HHH000204: Processing PersistenceUnitInfo [
    name: default
    ...]
mars 10, 2015 5:27:20 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.3.8.Final}
mars 10, 2015 5:27:20 PM org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
mars 10, 2015 5:27:20 PM org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
mars 10, 2015 5:27:22 PM org.hibernate.annotations.common.reflection.java.JavaReflectionManager <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.5.Final}
mars 10, 2015 5:27:22 PM org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
mars 10, 2015 5:27:22 PM org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory
mars 10, 2015 5:27:22 PM org.hibernate.tool.hbm2ddl.SchemaUpdate execute
INFO: HHH000228: Running hbm2ddl schema update
mars 10, 2015 5:27:22 PM org.hibernate.tool.hbm2ddl.SchemaUpdate execute
INFO: HHH000102: Fetching database metadata
mars 10, 2015 5:27:22 PM org.hibernate.tool.hbm2ddl.SchemaUpdate execute
INFO: HHH000396: Updating schema
mars 10, 2015 5:27:22 PM org.hibernate.tool.hbm2ddl.DatabaseMetadata getTableMetadata
INFO: HHH000262: Table not found: Customer
mars 10, 2015 5:27:22 PM org.hibernate.tool.hbm2ddl.DatabaseMetadata getTableMetadata
INFO: HHH000262: Table not found: Customer
mars 10, 2015 5:27:22 PM org.hibernate.tool.hbm2ddl.DatabaseMetadata getTableMetadata
INFO: HHH000262: Table not found: Customer
mars 10, 2015 5:27:22 PM org.hibernate.tool.hbm2ddl.SchemaUpdate execute
INFO: HHH000232: Schema update complete
Customers found with findAll():
-------------------------------
Customer[id=1, firstName='Jack', lastName='Bauer']
Customer[id=2, firstName='Chloe', lastName='O'Brian']
Customer[id=3, firstName='Kim', lastName='Bauer']
Customer[id=4, firstName='David', lastName='Palmer']
Customer[id=5, firstName='Michelle', lastName='Dessler']

Customer found with findOne(1L):
--------------------------------
Customer[id=1, firstName='Jack', lastName='Bauer']

Customer found with findByLastName('Bauer'):
--------------------------------------------
Customer[id=1, firstName='Jack', lastName='Bauer']
Customer[id=3, firstName='Kim', lastName='Bauer']

5.1.7. Bir proje oluşturma [Spring Data]

Bir Spring Data proje şablonu oluşturmak için aşağıdaki adımları uygulayabilirsiniz:

  • [1]'te yeni bir proje oluşturulur;
  • [2] adımında: [Spring Starter Project] türünde;
  • oluşturulan proje bir Maven projesi olacaktır. [3]'te, proje grubunun adını belirtin;
  • [4]'te: projenin derlenmesi sırasında oluşturulacak artefaktın (burada bir jar dosyası) adını belirtin;
  • [5]'te: projenin Eclipse adı – herhangi bir ad olabilir ([4] ile aynı olması gerekmez);
  • [7]'te: [JPA] katmanına sahip ve SGBD ile MySQL'i içeren bir proje oluşturulacağı belirtilir. Böyle bir proje için gerekli bağımlılıklar daha sonra [pom.xml] dosyasına eklenecektir;
  • [8]'te proje klasörünün adını girin;
  • [9] dosyasına, sihirbazı tamamlayın;
  • [10]: oluşturulan proje;

[pom.xml] dosyası, JPA projesine gerekli bağımlılıkları ekler:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>istia.st.springdata</groupId>
    <artifactId>intro-spring-data-01</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>intro-spring-data-01</name>
    <description>démo spring data avec table de produits</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.3.RELEASE</version>
        <relativePath/> <!-- depo içinden üst öğeyi ara -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <start-class>demo.IntroSpringData01Application</start-class>
        <java.version>1.7</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </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>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
  • 14-19. satırlar: üst Maven projesi;
  • 28-31. satırlar: JPA için gerekli bağımlılık – [Spring Data]'i içerecektir;
  • satır 32-36: MySQL'in JDBC sürücüsüne olan bağımlılığı;
  • satır 37-41: Spring ile entegre JUnit testleri için gerekli bağımlılıklar;

Çalıştırılabilir sınıf [Application] hiçbir işlem yapmaz ancak önceden yapılandırılmıştır:


package demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class IntroSpringData01Application {

    public static void main(String[] args) {
        SpringApplication.run(IntroSpringData01Application.class, args);
    }
}
  • [@SpringBootApplication] anotasyonu, sınıfı projenin otomatik yapılandırma sınıfı haline getirir;

[ApplicationTests] test sınıfı hiçbir işlem yapmaz, ancak önceden yapılandırılmıştır:


package demo;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = IntroSpringData01Application.class)
public class IntroSpringData01ApplicationTests {

    @Test
    public void contextLoads() {
    }

}
  • 9. satır: [@SpringApplicationConfiguration] anotasyonu, [IntroSpringData01Application] yapılandırma dosyasının kullanılmasını sağlar. Böylece test sınıfı, bu dosyada tanımlanan tüm bean'lerden yararlanacaktır;
  • 8. satır: [@RunWith] anotasyonu, Spring’in JUnit ile entegrasyonunu sağlar: sınıf, bir JUnit testi olarak çalıştırılabilecektir. [@RunWith], JUnit (4. satır) anotasyonudur; [SpringJUnit4ClassRunner] sınıfı ise bir Spring sınıfıdır (6. satır);

Artık JPA adlı bir uygulama iskeletine sahip olduğumuza göre, bunu tamamlayabiliriz.