Database
 sql >> база данни >  >> RDS >> Database

Работа с JDBC и Spring

В реален сценарий на приложение, огромно количество обработка се извършва на задния сървър, където данните действително се обработват и се съхраняват в хранилище. Освен много отличителни характеристики на Spring, като DI (Injection на зависимост), Aspects и POJO-ориентирана разработка, Spring има отлична поддръжка за обработка на данни. Има различни начини за писане на добри приложения за бази данни. Все още днес голям брой приложения са написани въз основа на възможността за достъп до данни JDBC. Тази статия се занимава конкретно с JDBC във връзка със Spring, неговата поддръжка и плюсове и минуси с подходящи примери и фрагменти от код.

Общ преглед на JDBC

Едно от най-големите предимства на все още използването на JDBC в света на ORM е, че не изисква овладяване на езика за заявки на друга рамка, освен работата с данни на много по-ниско ниво. Тя позволява на програмиста да се възползва от собствените функции на базата данни. Има и своите недостатъци. За съжаление, недостатъците често са толкова видими, че не се нуждаят от споменаване. Например един от тях е типичен код . Терминът типичен код по същество означава писане на един и същ код отново и отново, без да се включва каквато и да е стойност в кода. Това обикновено може да се види, когато запитваме данни от база данни; например в следния код просто извличаме Потребител запис от базата данни.

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;
}

Забележете, че всеки път, когато трябва да взаимодействаме с базата данни, трябва да създадем три обекта – връзка (Връзка ), изявление (PreparedStatement ) и набор от резултати (ResultSet ). Всичко това също трябва да бъде затворено в наложеното опитайте...улов блок. Дори затварянето на връзката също трябва да бъде затворено в try...catch . Това е нелепо, защото действително необходимият код за функцията е много по-малък. Кодът просто е запълнен с ненужен, но задължителен код и трябва да се повтаря навсякъде, където взаимодействаме с базата данни. Една умна схема за кодиране може да намали тази бъркотия, но е невъзможно да се изкорени проблемът с стандартния JDBC код. Това е не само проблемът с JDBC, но и с JMS, JNDI и REST.

Пролетно решение

Рамката на Spring предостави решение на тази каша и предостави средство за премахване на стандартния код чрез използване на шаблонни класове. Тези класове капсулират стандартния код, като по този начин облекчават програмиста. Това означава, че стандартният код все още е там, само програмистът, който използва един от шаблонните класове, се освобождава от проблемите да го напише. JdbcTemplate предоставен от Spring е централният клас на основния пакет JDBC.

Той опростява използването на JDBC и помага да се избегнат често срещани грешки. Той изпълнява основния JDBC работен поток, оставяйки кода на приложението да предоставя SQL и извлича резултати. Този клас изпълнява SQL заявки или актуализации, инициира итерация над ResultSets и улавя JDBC изключения и ги превежда в общата, по-информативна йерархия на изключенията, дефинирана в org.springframework.dao пакет.

Трябва да внедрим само интерфейсите за обратно извикване и да им дадем ясен, дефиниран договор. Например PreparedStatementCreator интерфейсът за обратно извикване се използва за създаване на подготвен оператор. ResultsetExtractor интерфейсът действа като ResultSet .

Следователно предходният кодов фрагмент може да бъде пренаписан с JdbcTemplate както следва:

@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 е интерфейс, който обикновено се използва от JdbcTemplate за картографиране на един ред на база от редове на ResultSet . RowMapper обектите са без състояние и следователно могат да се използват повторно. Те са идеални за прилагане на всяка логика за преобразуване на редове. Обърнете внимание, че в предишния код не обработвахме изключенията изрично, както направихме в кода, който не използва JdbcTemplate . Реализацията на RowMapper изпълнява действителната реализация на съпоставяне на всеки ред с обекта на резултата, без програмистът да се тревожи за обработката на изключения. Той ще бъде извикан и обработен чрез извикване на JdbcTemplate .

Изключенията

Изключенията, предоставени от JDBC, често са твърде внушителни, отколкото е необходимо, с малка стойност. Йерархията на изключенията за достъп до данни на Spring е по-рационализирана и разумна в това отношение. Това означава, че има последователен набор от класове изключения в своя арсенал за разлика от JDBC с един размер, подходящ за всички изключения, наречен SQLException за всички проблеми, свързани с достъпа до данни. Изключенията за достъп до данни на Spring се коренят с DataAccessException клас. Следователно можем да имаме както изборът между проверено и непроверено изключение, вкоренен в рамката. Това звучи по-практично, защото наистина няма решения за много от проблемите, възникнали по време на достъп до данни по време на изпълнение, и е безсмислено да ги хванем, когато не можем да се справим със ситуацията с подходяща алтернатива.

Начинът на Spring за опростяване на достъпа до данни

Това, което Spring всъщност прави, е, че разграничава фиксираната и променливата част от механизма за достъп до данни в два набора от класове, наречени шаблонни класове икласове за обратно извикване , съответно. Фиксираната част на кода представлява незначителната част от достъпа до данни, а променливата част е този метод за достъп до данни, който варира в зависимост от променящото се изискване.

Накратко,шаблонните класове дръжка:

  • Контрол на транзакциите
  • Управление на ресурсите
  • Обработка на изключения

И,класовете за обратно извикване дръжка:

  • Създаване на израз на заявка
  • Обвързване на параметри
  • Маршалиране на набор от резултати

Можем да изберем един измежду много класове шаблони, според избора на използваната постоянна технология. Например, за JDBC можем да изберем JdbcTemplate , или за ORM можем да изберем JpaTemplate , Hibernate Template , и така нататък.

Сега, докато се свързваме с базата данни, имаме три опции за конфигуриране на източника на данни, като например:

  • Дефинирано от JDBC драйвер
  • Потърсен от JNDI
  • Извлечено от пула за връзки

Готово за производство приложение обикновено използва пул за връзки или JNDI. Източниците на данни, дефинирани от драйвера на JDBC, са най-простите, въпреки че се използва предимно за тестови цели. Spring предлага три класа в пакета org.springframework.jdbc.datasource от тази категория; те са:

  • DriverManagerDataSource: Опростена реализация на стандартния JDBC DataSource интерфейс, конфигуриране на обикновения стар JDBC DriverManager чрез свойства на bean и връщане на нова Връзка от всяка заявка.
  • SingleConnectionDataSource: Връща същата връзка при всяка заявка. Този тип връзка е предназначен основно за тестване.
  • SimpleDriverDataSource: Същото като DriverManagerDataSource освен че има специални проблеми със зареждането на класове като OSGi; този клас работи директно с JDBC драйвер.

Конфигурирането на тези източници на данни е подобно. Можем да ги конфигурираме в bean клас или чрез 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"/>

JDBC шаблонни класове

Spring предлага няколко шаблонни класа за опростяване на достъпа до данни с JDBC:

  • JdbcTemplate: Това е основният клас на основния JDBC пакет org.springframework.jdbc.core който осигурява най-простия достъп до базата данни чрез индексирани заявки.
  • NamedParameterJdbcTemplate: Този шаблонен клас също така предоставя основен набор от JDBC операции, при които стойностите са обвързани с наименувани параметри, а не с традиционните „?“ заместители в SQL заявките.

JDBC класове за обратно извикване

Ключовите функционални интерфейси за обратно извикване на JDBC, дефинирани в org.springframework.jdbc.core са:

  • CallableStatementCallback: Работи на JDBC CallableStatement. Това обратно извикване се използва вътрешно от JdbcTemplate и позволява изпълнение на един CallableStatement като например единични или множество SQL извиквания за изпълнение с различни параметри.
  • PreparedStatementCallback: Работи на JDBC PreparedStatement. Това обратно извикване се използва вътрешно от JdbcTemplate и позволява изпълнение на повече от една операция върху един PreparedStatement като единично или множество SQL извикване executeUpdate с различни параметри.
  • StatementCallback: Работи с изявление JDBC . Това обратно извикване също се използва вътрешно от JdbcTemplate за изпълнение на повече от една операция върху един Изявление като единични или множество SQL повиквания executeUpdate.

Прост JDBC пример за пружинно зареждане

Нека опитаме прост пример за зареждане на Spring. Проектът за зареждане на Spring автоматично се справя с много от сложността на конфигурацията, при която разработчикът е освободен от всички проблеми, след като във файла на Maven pom.xml е включена правилна зависимост . За да запазим дължината на статията кратка, няма да включваме обяснения на кода. Моля, използвайте препратките, дадени в края на статията за по-подробно описание.

За да работите върху следния пример, създайте база данни и таблица в MySQl, както следва:

Влезте в базата данни на MySQL и създайте база данни и таблица със следната команда:

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)
);

Започнете като проект Spring starter от Spring Tool Suite (STS) със зависимостта JDBC и MySQL. Конфигурационният файл на Maven, pom.xml , на проекта е както следва:

<?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>

Клас на модела: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 + "]";
   }

}

Интерфейс на обекта за достъп до данни: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();
}

Клас за внедряване на обект за достъп до данни: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;
   }

}

Клас на Spring Boot Loader: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);
      }

   }

}

Application.properties

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

Стартирайте приложението

За да стартирате приложението, щракнете с десния бутон върху проекта в Project Explorer панел и изберете Изпълни като -> Приложение Spring Boot . Това е всичко.

Заключение

Имаме три опции за работа с програмиране на релационни бази данни с Spring:

  • Старомодният JDBC с Spring. Това означава използване на рамката на Spring за всички практически цели в програмата, с изключение на поддръжката на данни на Spring.
  • Използване на JDBC шаблонни класове. Spring предлага JDBC абстрактни класове за запитване на релационни бази данни; те са много по-прости от работата с родния JDBC код.
  • Spring също има отлична поддръжка за рамката ORM (Обектно релационно картографиране) и може да се интегрира добре с забележителна реализация на API на JPA (Java Persistent Annotation), като например Hibernate. Той също така има своя собствена помощ на Spring Data JPA, която може автоматично да генерира внедряване на хранилището в движение по време на изпълнение.

Ако някой избере JDBC по някаква причина, по-добре е да използва поддръжка на Spring шаблон, като JdbcTemplate различни от използването на ORM.

Препратки

  • Стени, Crag. Пролет в действие 4 , Manning Publications
  • Документация за API на Spring 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. NULL сложности – част 2

  2. Преименуване на Pluggable база данни

  3. Класификация на данните в IRI Workbench

  4. DSN файлове и IRI софтуер

  5. Как да поръчам по две колони в SQL?