sql >> Database teknologi >  >> RDS >> Database

Arbejder med JDBC og Spring

I et applikationsscenarie i den virkelige verden udføres en enorm mængde behandling på backend-serveren, hvor dataene faktisk behandles og forbliver i et lager. Bortset fra mange fremtrædende træk ved Spring, såsom DI (Dependency Injection), Aspects og POJO-orienteret udvikling, har Spring fremragende support til datahåndtering. Der er forskellige måder at skrive gode databaseapplikationer på. Stadig i dag er et stort antal applikationer skrevet baseret på JDBC-dataadgangskapacitet. Denne artikel omhandler specifikt JDBC i forbindelse med Spring, dets support og fordele og ulemper med passende eksempler og kodestykker.

JDBC-oversigt

En af de største fordele ved stadig at bruge JDBC i ORM-verdenen er, at det ikke kræver at beherske et andet frameworks forespørgselssprog udover at arbejde med data på et meget lavere niveau. Det gør det muligt for en programmør at drage fordel af databasens proprietære funktioner. Det har også sine ulemper. Desværre er ulemperne ofte så synlige, at de ikke behøver en omtale. For eksempel er en af ​​dem boilerplate code . Udtrykket boilerplate code betyder grundlæggende at skrive den samme kode igen og igen uden at inkorporere nogen værdi i koden. Dette kan typisk ses, når vi forespørger data fra en database; for eksempel, i den følgende kode henter vi blot en Bruger optage fra databasen.

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

Bemærk, at hver gang vi skal interagere med databasen, skal vi oprette tre objekter - en forbindelse (Forbindelse ), erklæring (PreparedStatement ), og resultatsæt (Resultatsæt ). Alle disse skal også være indeholdt i den pålagte forsøg...fangst blok. Selv lukningen af ​​forbindelsen skal også være indeholdt i try…catch . Dette er latterligt, fordi den faktisk nødvendige kode til funktionen er meget mindre. Koden er simpelthen pustet op med unødvendig, men obligatorisk kode og skal gentages overalt, hvor vi interagerer med databasen. Et smart kodningsskema kan reducere dette rod, men alligevel er det umuligt at udrydde problemet med JDBC-kode. Dette er ikke kun problemet med JDBC, men også JMS, JNDI og REST.

Forårets løsning

Spring-rammen gav en løsning på dette rod og gav et middel til at eliminere boilerplate-kode ved at bruge skabelonklasser. Disse klasser indkapsler boilerplate-koden og aflaster dermed programmøren. Det betyder, at boilerplate-koden stadig er der, kun programmøren, der bruger en af ​​skabelonklasserne, er fritaget for besværet med at skrive det. JdbcTemplate leveret af Spring er den centrale klasse i JDBC-kernepakken.

Det forenkler brugen af ​​JDBC og hjælper med at undgå almindelige fejl. Det udfører kerne JDBC-workflow og efterlader applikationskode til at levere SQL og udtrække resultater. Denne klasse udfører SQL-forespørgsler eller opdateringer, initierer iteration over ResultSets og fanger JDBC-undtagelser og oversætter dem til det generiske, mere informative undtagelseshierarki defineret i org.springframework.dao pakke.

Vi skal kun implementere call-back-grænseflader og give dem en klar, defineret kontrakt. For eksempel PreparedStatementCreator tilbagekaldsgrænseflade bruges til at oprette en forberedt erklæring. ResultsetExtractor grænsefladen fungerer som et Resultatsæt .

Derfor kan det foregående kodestykke omskrives med JdbcTemplate som følger:

@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 er en grænseflade, der typisk bruges af JdbcTemplate at kortlægge en række pr. basis af rækker i Resultatsættet . RowMapper genstande er statsløse og kan derfor genbruges. De er perfekte til at implementere enhver rækkekortlægningslogik. Bemærk, at vi i den forrige kode ikke håndterede undtagelser eksplicit, som vi har gjort i koden, der ikke bruger JdbcTemplate . RowMapper-implementeringen udfører den faktiske implementering af at kortlægge hver række til resultatobjektet uden at programmøren behøver at bekymre sig om håndtering af undtagelser. Det vil blive kaldt og håndteret ved at kalde JdbcTemplate .

Undtagelserne

Undtagelserne fra JDBC er ofte for imponerende end nødvendigt, med ringe værdi. Springs undtagelseshierarki for dataadgang er mere strømlinet og rimeligt i denne henseende. Dette betyder, at den har et konsistent sæt af undtagelsesklasser i sit arsenal i modsætning til JDBC's one size fit to all undtagelse kaldet SQLException for alle problemer i forbindelse med dataadgang. Springs undtagelser for dataadgang er forankret med DataAccessException klasse. Derfor kan vi både have valget mellem markeret vs. ukontrolleret undtagelse indgroet i rammen. Dette lyder mere praktisk, fordi der virkelig ikke er nogen løsninger på mange af de problemer, der opstod under runtime-dataadgang, og det er meningsløst, at vi fanger dem, når vi ikke kan løse situationen med et passende alternativ.

Forårets måde at forenkle dataadgangen

Hvad Spring faktisk gør, er, at den adskiller den faste og variable del af dataadgangsmekanismen i to sæt klasser kaldet skabelonklasser og tilbagekaldskurser , henholdsvis. Den faste del af koden repræsenterer den overflødige del af dataadgang, og den variable del er den dataadgangsmetode, der varierer i henhold til det skiftende krav.

Kort sagt, skabelonklasserne håndtag:

  • Transaktionskontrol
  • Ressourcestyring
  • Undtagelseshåndtering

Og tilbagekaldskurserne håndtag:

  • Opretter forespørgselserklæring
  • Parameterbinding
  • Resultatsæt rangering

Vi kan vælge en blandt mange skabelonklasser, alt efter valget af den anvendte vedvarende teknologi. For eksempel kan vi for JDBC vælge JdbcTemplate , eller for ORM kan vi vælge JpaTemplate , HibernateTemplate , og så videre.

Nu, mens vi opretter forbindelse til databasen, har vi tre muligheder for at konfigurere datakilden, såsom:

  • Defineret af JDBC-driveren
  • Søgt op af JNDI
  • Hentet fra forbindelsespuljen

En produktionsklar applikation bruger typisk en forbindelsespulje eller JNDI. De JDBC-driverdefinerede datakilder er langt de enkleste, selvom de mest bruges til testformål. Spring tilbyder tre klasser i pakken org.springframework.jdbc.datasource af denne kategori; de er:

  • DriverManagerDataSource: Simpel implementering af standarden JDBC DataSource interface, konfiguration af den almindelige gamle JDBC DriverManager via bønneegenskaber og returnere en ny forbindelse fra hver anmodning.
  • SingleConnectionDataSource: Returnerer den samme forbindelse ved hver anmodning. Denne type forbindelse er primært beregnet til test.
  • SimpleDriverDataSource: Samme som DriverManagerDataSource bortset fra, at den har særlige klassebelastningsproblemer såsom OSGi; denne klasse fungerer direkte med JDBC Driver.

Konfiguration af disse datakilder er ens. Vi kan konfigurere dem i en bønneklasse eller via 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-skabelonklasser

Spring tilbyder et par skabelonklasser for at forenkle dataadgang med JDBC:

  • JdbcTemplate: Dette er den grundlæggende klasse i kerne-JDBC-pakken org.springframework.jdbc.core der giver den enkleste adgang til databasen gennem indekserede forespørgsler.
  • NavnetParameterJdbcTemplate: Denne skabelonklasse giver også et grundlæggende sæt af JDBC-operationer, hvor værdierne er bundet til navngivne parametre i stedet for traditionelle '?'-pladsholdere i SQL-forespørgsler.

JDBC-tilbagekaldsklasser

De vigtigste JDBC callback funktionelle grænseflader defineret i org.springframework.jdbc.core er:

  • CallableStatementCallback: Fungerer på JDBC CallableStatement. Dette tilbagekald bruges internt af JdbcTemplate og tillader udførelse på et enkelt CallableStatement f.eks. enkelt eller flere SQL-eksekvere kald med forskellige parametre.
  • Prepared StatementCallback: Fungerer på JDBC PreparedStatement. Dette tilbagekald bruges internt af JdbcTemplate og tillader udførelse af mere end én operation på en enkelt PreparedStatement såsom enkelt eller flere SQL executeUpdate-kald med forskellige parametre.
  • Erklæring tilbagekald: Fungerer på JDBC erklæring . Dette tilbagekald bruges også internt af JdbcTemplate at udføre mere end én handling på en enkelt erklæring såsom enkelt eller flere SQL executeUpdate-kald.

Et simpelt JDBC-eksempel med fjederstøvle

Lad os prøve et simpelt eksempel på forårsstøvler. Et Spring boot-projekt håndterer automatisk mange af kompleksiteten af ​​konfigurationen, hvor en udvikler er fritaget for alle problemer, når en korrekt afhængighed er inkluderet i Maven-filen pom.xml . For at holde artiklens længde kort, inkluderer vi ikke kodeforklaringer. Brug venligst referencerne i slutningen af ​​artiklen for en mere detaljeret beskrivelse.

For at arbejde med følgende eksempel skal du oprette en database og en tabel i MySQl som følger:

Log ind på MySQL-databasen og opret en database og en tabel med følgende kommando:

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

Start som et Spring-startprojekt fra Spring Tool Suite (STS) med afhængigheden JDBC og MySQL. Maven-konfigurationsfilen, pom.xml , af projektet er som følger:

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

Modelklasse: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 + "]";
   }

}

Data Access Object Interface: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();
}

Data Access Object Implementation Class: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 Class: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

Kør applikationen

For at køre programmet skal du højreklikke på projektet i Project Explorer ruden og vælg Kør som -> Spring Boot App . Det er alt.

Konklusion

Vi har tre muligheder for at arbejde med relationel databaseprogrammering med Spring:

  • Den gammeldags JDBC med Spring. Det betyder at bruge Spring-rammen til alle praktiske formål i programmet undtagen Springs dataunderstøttelse.
  • Brug af JDBC-skabelonklasser. Spring tilbyder JDBC abstraktionsklasser til at forespørge relationelle databaser; disse er langt enklere end at arbejde med indbygget JDBC-kode.
  • Spring har også fremragende understøttelse af ORM (Object Relational Mapping)-rammeværket og kan godt integreres med en fremtrædende implementering af JPA (Java Persistent Annotation) API såsom Hibernate. Den har også sin egen Spring Data JPA-assistance, der automatisk kan generere repository-implementering under kørsel.

Hvis man vælger JDBC af en eller anden grund, er det bedre at bruge Spring-skabelonunderstøttelse såsom JdbcTemplate andet end at bruge ORM.

Referencer

  • Vægge, Crag. Forår i aktion 4 , Manning Publications
  • Forår 5 API-dokumentation

  1. WHERE IN (matrix af ID'er)

  2. Sådan fungerer COLLATION() i MariaDB

  3. Kopier tabeller fra en database til en anden i SQL Server

  4. Brug "SELECT" til at kalde en funktion