Database
 sql >> Baza danych >  >> RDS >> Database

Współpraca z JDBC i Spring

W rzeczywistym scenariuszu aplikacji, ogromna ilość przetwarzania jest wykonywana na serwerze zaplecza, gdzie dane są faktycznie przetwarzane i utrwalane w repozytorium. Oprócz wielu ważnych funkcji Spring, takich jak DI (Dependency Injection), Aspects i programowanie zorientowane na POJO, Spring ma doskonałe wsparcie dla obsługi danych. Istnieją różne sposoby pisania dobrych aplikacji bazodanowych. Jeszcze dzisiaj wiele aplikacji jest pisanych w oparciu o możliwości dostępu do danych JDBC. Ten artykuł dotyczy w szczególności JDBC w związku ze Springiem, jego obsługą oraz zaletami i wadami z odpowiednimi przykładami i fragmentami kodu.

Omówienie JDBC

Jedną z największych zalet korzystania z JDBC w świecie ORM jest to, że nie wymaga opanowania języka zapytań innego frameworka, poza pracą z danymi na znacznie niższym poziomie. Umożliwia programiście skorzystanie z własności bazy danych. Ma też swoje wady. Niestety wady są często tak widoczne, że nie trzeba o nich wspominać. Na przykład jednym z nich jest kod wzorcowy . Termin kod wzorcowy zasadniczo oznacza pisanie tego samego kodu w kółko bez włączania jakiejkolwiek wartości do kodu. Zwykle można to zaobserwować, gdy wysyłamy zapytanie do bazy danych; na przykład w poniższym kodzie po prostu pobieramy użytkownika rekord z bazy danych.

public User getUserById(long id) {
   User user = null;
   Connection con = null;
   PreparedStatement pstmt = null;
   ResultSet rs = null;
   try {
      con = dataSource.getConnection();
      pstmt = con.prepareStatement("select * from "
         + "user_table where userid=?");
      pstmt.setInt(1, id);
      rs.pstmt.executeQuery();
      if (rs.next()) {
         user = new User();
         user.setId(rs.getInt("userid"));
         user.setFullName(rs.getString("fullname"));
         user.setUserType(rs.getString("usertype"));
         user.setPassword(rs.getString("password"));
      }
   } catch (SQLException ex1) {}
   finally {
      try {
         if (rs != null)
         rs.close();
         if (pstmt != null)
            rs.close();
         if (con != null)
            rs.close();
      } catch (SQLException ex2) {}
   }
   return user;
}

Zauważ, że za każdym razem, gdy musimy wejść w interakcję z bazą danych, musimy utworzyć trzy obiekty — połączenie (Połączenie ), oświadczenie (PreparedStatement ) i zestaw wyników (Zestaw wyników ). Wszystko to również musi być zawarte w narzuconym spróbuj…łap blok. Nawet zamknięcie połączenia również musi być ujęte w try…catch . Jest to niedorzeczne, ponieważ rzeczywisty wymagany kod funkcji jest znacznie mniejszy. Kod jest po prostu nadęty niepotrzebnym, ale obowiązkowym kodem i musi być powtarzany wszędzie tam, gdzie wchodzimy w interakcję z bazą danych. Sprytny schemat kodowania może zredukować ten bałagan, ale nie da się wyeliminować problemu z szablonowym kodem JDBC. To nie tylko problem z JDBC, ale także z JMS, JNDI i REST.

Rozwiązanie Springa

Framework Spring zapewnił rozwiązanie tego bałaganu i zapewnił sposób na wyeliminowanie standardowego kodu za pomocą klas szablonów. Klasy te hermetyzują kod wzorcowy, odciążając w ten sposób programistę. Oznacza to, że kod wzorcowy nadal istnieje, jedynie programista korzystający z jednej z klas szablonów jest zwolniony z trudu jej napisania. Szablon Jdbc dostarczana przez Spring jest centralną klasą podstawowego pakietu JDBC.

Upraszcza korzystanie z JDBC i pomaga uniknąć typowych błędów. Wykonuje podstawowy przepływ pracy JDBC, pozostawiając kod aplikacji dostarczający SQL i wyodrębniający wyniki. Ta klasa wykonuje zapytania lub aktualizacje SQL, inicjując iterację zestawów wyników i przechwytując wyjątki JDBC i tłumacząc je na ogólną, bardziej informacyjną hierarchię wyjątków zdefiniowaną w org.springframework.dao pakiet.

Musimy zaimplementować tylko interfejsy oddzwaniania i dać im jasną, określoną umowę. Na przykład PreparedStatementCreator interfejs wywołania zwrotnego służy do tworzenia przygotowanej instrukcji. Ekstraktor zestawu wyników interfejs działa jak zestaw wyników .

Dlatego powyższy fragment kodu może zostać przepisany za pomocą JdbcTemplate w następujący sposób:

@Autowired
private JdbcTemplate jdbcTemplate;
public User getUserById(long id) {
   return jdbcTemplate.queryForObject(
      "select * from user_table where userid=?",
      new UserRowMapper(),id);
   }
class UserRowMapper implements RowMapper<User>{
   @Override
   public User mapRow(ResultSet rs, int runNumber)
         throws SQLException {
      User user=new User();
      user.setId(rs.getInt("userid"));
      user.setFullName(rs.getString("fullname"));
      user.setUserType(rs.getString("usertype"));
      user.setPassword(rs.getString("password"));
      return user;
   }
}

RowMapper jest interfejsem zwykle używanym przez JdbcTemplate mapować jeden wiersz na bazę wierszy ResultSet . RowMapper obiekty są bezstanowe i dlatego nadają się do wielokrotnego użytku. Są idealne do implementacji dowolnej logiki mapowania wierszy. Zauważ, że w poprzednim kodzie nie obsługiwaliśmy wyjątków w sposób jawny, tak jak zrobiliśmy to w kodzie, który nie używa JdbcTemplate . Implementacja RowMapper wykonuje rzeczywistą implementację mapowania każdego wiersza do obiektu wynikowego bez konieczności martwienia się programisty o obsługę wyjątków. Zostanie wywołany i obsłużony przez wywołanie JdbcTemplate .

Wyjątki

Wyjątki zapewniane przez JDBC są często zbyt imponujące, niż to konieczne, i mają niewielką wartość. Hierarchia wyjątków dostępu do danych w Springu jest pod tym względem bardziej uproszczona i rozsądna. Oznacza to, że ma spójny zestaw klas wyjątków w swoim arsenale, w przeciwieństwie do jednego rozmiaru dopasowanego do wszystkich wyjątków JDBC o nazwie SQLException dla wszystkich problemów związanych z dostępem do danych. Wyjątki dostępu do danych Springa są zakorzenione w DataAccessException klasa. W związku z tym możemy mieć zakorzeniony we frameworku wybór wyjątku sprawdzonego i niesprawdzonego. Brzmi to bardziej praktycznie, ponieważ tak naprawdę nie ma rozwiązań wielu problemów, które wystąpiły podczas dostępu do danych w czasie wykonywania i nie ma sensu, gdy wyłapujemy je, gdy nie możemy rozwiązać sytuacji za pomocą odpowiedniej alternatywy.

Sposób Springa na uproszczenie dostępu do danych

To, co właściwie robi Spring, to rozróżnienie stałej i zmiennej części mechanizmu dostępu do danych na dwa zestawy klas zwane klasami szablonów i zajęcia zwrotne , odpowiednio. Stała część kodu reprezentuje pobieżną część dostępu do danych, a zmienna część to metoda dostępu do danych, która zmienia się w zależności od zmieniających się wymagań.

Krótko mówiąc, klasy szablonów uchwyt:

  • Kontrola transakcji
  • Zarządzanie zasobami
  • Obsługa wyjątków

Oraz zajęcia zwrotne uchwyt:

  • Tworzenie instrukcji zapytania
  • Powiązanie parametrów
  • Porządkowanie zestawu wyników

Możemy wybrać jedną z wielu klas szablonów, zgodnie z wyborem zastosowanej technologii trwałej. Na przykład dla JDBC możemy wybrać JdbcTemplate , lub dla ORM możemy wybrać JpaTemplate , Szablon Hibernate i tak dalej.

Teraz podczas łączenia się z bazą danych mamy trzy opcje konfiguracji źródła danych, takie jak:

  • Zdefiniowane przez sterownik JDBC
  • Wyszukane przez JNDI
  • Pobrano z puli połączeń

Aplikacja gotowa do produkcji zazwyczaj korzysta z puli połączeń lub JNDI. Źródła danych zdefiniowane przez sterownik JDBC są zdecydowanie najprostsze, chociaż są używane głównie do celów testowych. Spring oferuje trzy klasy w pakiecie org.springframework.jdbc.datasource tej kategorii; są to:

  • Źródło danych sterownika: Prosta implementacja standardowego JDBC DataSource interfejs, konfiguracja zwykłego starego JDBC DriverManager poprzez właściwości fasoli i zwrócenie nowego połączenia z każdego żądania.
  • SingleConnectionDataSource: Zwraca to samo połączenie przy każdym żądaniu. Ten typ połączenia jest przeznaczony głównie do testowania.
  • SimpleDriverDataSource: To samo co DriverManagerDataSource z wyjątkiem tego, że ma problemy z ładowaniem klas specjalnych, takich jak OSGi; ta klasa działa bezpośrednio ze sterownikiem JDBC.

Konfiguracja tych źródeł danych jest podobna. Możemy je skonfigurować w klasie fasoli lub przez XML.

// Configuring MySQL data source
@Bean
public DataSource dataSource() {
   DriverManagerDataSource ds=new DriverManagerDataSource();
   ds.setDriverClassName("com.mysql.jdbc.Driver");
   ds.setUrl("jdbc:mysql://localhost:3306/testdb");
   ds.setUsername("root");
   ds.setPassword("secret");
   return ds;
}

<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p_driverClassName="com.mysql.jdbc.Driver"
p_url="jdbc:mysql://localhost:3306/testdb"
p_username="root"
p_password="secret"/>

Klasy szablonów JDBC

Spring oferuje kilka klas szablonów, aby uprościć dostęp do danych za pomocą JDBC:

  • Szablon Jdbc: To jest podstawowa klasa podstawowego pakietu JDBC org.springframework.jdbc.core który zapewnia najprostszy dostęp do bazy danych poprzez indeksowane zapytania.
  • NamedParameterJdbcTemplate: Ta klasa szablonu zapewnia również podstawowy zestaw operacji JDBC, w których wartości są powiązane z nazwanymi parametrami, a nie tradycyjnymi symbolami zastępczymi „?” w zapytaniach SQL.

Klasy wywołań zwrotnych JDBC

Kluczowe funkcjonalne interfejsy wywołania zwrotnego JDBC zdefiniowane w org.springframework.jdbc.core są:

  • CallableStatementCallback: Działa na JDBC CallableStatement. To wywołanie zwrotne jest używane wewnętrznie przez JdbcTemplate i umożliwia wykonanie na jednym CallableStatement takie jak pojedyncze lub wiele wywołań SQL z różnymi parametrami.
  • PreparedStatementCallback<: Działa na JDBC PreparedStatement. To wywołanie zwrotne jest wewnętrznie używane przez JdbcTemplate i umożliwia wykonanie więcej niż jednej operacji na jednym PreparedStatement takie jak pojedyncze lub wielokrotne wywołanie instrukcji SQL executeUpdate z różnymi parametrami.
  • StatementCallback: Działa na JDBC Oświadczenie . To wywołanie zwrotne jest również używane wewnętrznie przez JdbcTemplate wykonać więcej niż jedną operację na jednym Oświadczeniu takie jak jedno lub wiele wywołań instrukcji SQL executeUpdate.

Prosty przykład JDBC Spring Boot

Wypróbujmy prosty przykład Spring Boot. Projekt rozruchowy Spring automatycznie obsługuje wiele złożoności konfiguracji, w której programista jest zwolniony z wszelkich problemów, gdy poprawna zależność zostanie dołączona do pliku Mavena pom.xml . Aby artykuł był krótki, nie będziemy zamieszczać wyjaśnień dotyczących kodu. Proszę skorzystać z odnośników podanych na końcu artykułu, aby uzyskać bardziej szczegółowy opis.

Aby pracować na poniższym przykładzie, utwórz bazę danych i tabelę w MySQL w następujący sposób:

Zaloguj się do bazy danych MySQL i utwórz bazę danych oraz tabelę za pomocą następującego polecenia:

CREATE DATABASE testdb;
USE testdb;
CREATE TABLE candidate(
   id INT UNSIGNED NOT NULL AUTO_INCREMENT,
   fullname VARCHAR(100) NOT NULL,
   email VARCHAR(100) NOT NULL,
   phone VARCHAR(10) NOT NULL,
   PRIMARY KEY(id)
);

Zacznij jako projekt startowy Spring z pakietu Spring Tool Suite (STS) z zależnością JDBC i MySQL. Plik konfiguracyjny Mavena, pom.xml , projektu wygląda następująco:

<?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>org.mano.springbootjdbc.demo</groupId>
   <artifactId>spring-boot-jdbc-demo</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <packaging>jar</packaging>

   <name>spring-boot-jdbc-demo</name>
   <description>Demo project for Spring Boot
      jdbc</description>

   <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>1.5.10.RELEASE</version>
      <relativePath/> <!-- Look up parent from repository -->
   </parent>

   <properties>
     <project.build.sourceEncoding>UTF-8
         </project.build.sourceEncoding>
     <project.reporting.outputEncoding>UTF-8
         </project.reporting.outputEncoding>
      <java.version>1.8</java.version>
   </properties>

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

Klasa modelu:Candidate.java

package org.mano.springbootjdbc.demo.model;

public class Candidate {
   private int id;
   private String fullname;
   private String email;
   private String phone;

   public Candidate() {
      super();
   }

   public Candidate(int id, String fullname,
         String email, String phone) {
      super();
      setId(id);
      setFullname(fullname);
      setEmail(email);
      setPhone(phone);
   }

   public int getId() {
      return id;
   }

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

   public String getFullname() {
      return fullname;
   }

   public void setFullname(String fullname) {
      this.fullname = fullname;
   }

   public String getEmail() {
      return email;
   }

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

   public String getPhone() {
      return phone;
   }

   public void setPhone(String phone) {
      this.phone = phone;
   }

   @Override
   public String toString() {
      return "Candidate [id=" + id + ", fullname=" + fullname
         + ", email=" + email + ", phone=" + phone + "]";
   }

}

Interfejs obiektu dostępu do danych:CandidateDao.java

package  org.mano.springbootjdbc.demo.dao;

import java.util.List;

import org.mano.springbootjdbc.demo.model.Candidate;

public interface CandidateDao {
   public void addCandidate(Candidate candidate);

   public void modifyCandidate(Candidate candidate,
      int candidateId);

   public void deleteCandidate(int candidateId);

   public Candidate find(int candidateId);

   public List<Candidate> findAll();
}

Klasa implementacji obiektu dostępu do danych:CandidateDaoImpl.java

package org.mano.springbootjdbc.demo.dao;

import java.util.ArrayList;
import java.util.List;

import org.mano.springbootjdbc.demo.model.Candidate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
@Qualifier("candidateDao")
public class CandidateDaoImpl implements CandidateDao {

   @Autowired
   JdbcTemplate jdbcTemplate;

   @Override
   public void addCandidate(Candidate candidate) {
      jdbcTemplate.update("insert into candidate
            (id,fullname,email,phone) "
            + "values (?,?,?,?)", candidate.getId(),
         candidate.getFullname(), candidate.getEmail(),
         candidate.getPhone());
      System.out.println(candidate+" is added successfully!");

   }

   @Override
   public void modifyCandidate(Candidate candidate, int
         candidateId) {
      jdbcTemplate.update("update candidate fullname=?,
         email=?,phone=? "
         + "where id=? values (?,?,?,?)",candidate.getFullname(),
      candidate.getEmail(), candidateId);
      System.out.println("Candidate with id="+candidateId+
         " modified successfully!");

   }

   @Override
   public void deleteCandidate(int candidateId) {
      jdbcTemplate.update("delete from candidate where id=?",
         candidateId);
      System.out.println("Candidate with id="+candidateId+
         " deleted successfully!");

   }

   @Override
   public Candidate find(int candidateId) {
      Candidate c = null;
      c = (Candidate) jdbcTemplate.queryForObject("select *
         from candidate "
         + "where id=?", new Object[] { candidateId },
      new BeanPropertyRowMapper<Candidate>(Candidate.
         class));
      return c;
   }

   @Override
   public List<Candidate> findAll() {
      List<Candidate> candidates = new ArrayList<>();
      candidates = jdbcTemplate.query("select * from candidate",
      new BeanPropertyRowMapper<Candidate>
         (Candidate.class));
      return candidates;
   }

}

Klasa modułu ładującego Spring Boot:SpringBootJdbcDemoApplication.java

package org.mano.springbootjdbc.demo;

import java.util.List;

import org.mano.springbootjdbc.demo.dao.CandidateDao;
import org.mano.springbootjdbc.demo.model.Candidate;
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 SpringBootJdbcDemoApplication implements
      CommandLineRunner {
   @Autowired
   private CandidateDao cdao;

   public static void main(String[] args) {
     SpringApplication.run(SpringBootJdbcDemoApplication.
         class, args);
   }

   @Override
   public void run(String... arg0) throws Exception {
      Candidate c1 = new Candidate(1, "Sachin Tendulkar",
         "[email protected]", "1234567890");
      Candidate c2 = new Candidate(2, "Amit Saha",
         "[email protected]", "9632587410");
      Candidate c3 = new Candidate(3, "Sandip Paul",
         "[email protected]", "8527419630");
      Candidate c4 = new Candidate(4, "Rajib Kakkar",
         "[email protected]", "9876543210");
      Candidate c5 = new Candidate(5, "Rini Simon",
         "[email protected]", "8624793150");
      cdao.addCandidate(c1);
      cdao.addCandidate(c2);
      cdao.addCandidate(c3);
      cdao.addCandidate(c4);
      cdao.addCandidate(c5);

      List<Candidate> candidates = cdao.findAll();
      for (Candidate candidate : candidates) {
         System.out.println(candidate);
      }
      cdao.deleteCandidate(3);

      candidates = cdao.findAll();
      for (Candidate cc : candidates) {
         System.out.println(cc);
      }

   }

}

Właściwości.aplikacji

spring.driverClassName=com.mysql.jdbc.Driver
spring.url=jdbc:mysql://localhost:3306/testdb
spring.username=root
spring.password=secret

Uruchom aplikację

Aby uruchomić aplikację, kliknij prawym przyciskiem myszy projekt w Eksploratorze projektów okienko i wybierz Uruchom jako -> Aplikacja Spring Boot . To wszystko.

Wniosek

Mamy trzy opcje pracy z programowaniem relacyjnych baz danych w Spring:

  • Staromodny JDBC ze Springiem. Oznacza to używanie frameworka Spring do wszystkich praktycznych celów w programie, z wyjątkiem obsługi danych Springa.
  • Korzystanie z klas szablonów JDBC. Spring oferuje klasy abstrakcji JDBC do tworzenia zapytań do relacyjnych baz danych; są one znacznie prostsze niż praca z natywnym kodem JDBC.
  • Spring ma również doskonałe wsparcie dla frameworka ORM (Object Relational Mapping) i może dobrze integrować się z wybitną implementacją API JPA (Java Persistent Annotation), taką jak Hibernate. Posiada również własną pomoc Spring Data JPA, która może automatycznie generować implementację repozytorium w locie w czasie wykonywania.

Jeśli ktoś z jakiegoś powodu zdecyduje się na JDBC, lepiej użyć wsparcia szablonów Spring, takich jak JdbcTemplate inne niż przy użyciu ORM.

Referencje

  • Mury, Urwisko. Wiosna w działaniu 4 , Manning Publikacje
  • Dokumentacja API wiosna 5

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Co to jest NoSQL i jak jest wykorzystywany?

  2. Brakujące indeksy w MS SQL lub błyskawiczna optymalizacja

  3. Różnica między sprzężeniem wewnętrznym a sprzężeniem zewnętrznym w SQL

  4. Podstawy programowania równoległego z platformą rozwidlenia/połączenia w Javie

  5. Pandy:Jak czytać i pisać pliki