sql >> Database >  >> RDS >> Database

Werken met JDBC en Spring

In een real-world toepassingsscenario wordt een enorme hoeveelheid verwerking gedaan op de backend-server waar de gegevens daadwerkelijk worden verwerkt en bewaard in een repository. Afgezien van veel prominente functies van Spring, zoals DI (Dependency Injection), Aspects en POJO-georiënteerde ontwikkeling, biedt Spring uitstekende ondersteuning voor gegevensverwerking. Er zijn verschillende manieren om goede database applicaties te schrijven. Nog steeds wordt een groot aantal applicaties geschreven op basis van JDBC-gegevenstoegangscapaciteit. Dit artikel gaat specifiek in op de JDBC in verband met Spring, de ondersteuning ervan, en de voor- en nadelen met passende voorbeelden en codefragmenten.

JDBC-overzicht

Een van de grootste voordelen van het nog steeds gebruiken van JDBC in de wereld van ORM is dat het niet nodig is om de querytaal van een ander framework te beheersen, afgezien van het werken met gegevens op een veel lager niveau. Het stelt een programmeur in staat om te profiteren van de eigen functies van de database. Het heeft ook zijn nadelen. Helaas zijn de nadelen vaak zo zichtbaar dat ze niet genoemd hoeven te worden. Een daarvan is bijvoorbeeld boilerplate-code . De term boilerplate code betekent in feite het steeds opnieuw schrijven van dezelfde code zonder enige waarde in de code op te nemen. Dit is meestal te zien wanneer we gegevens uit een database opvragen; in de volgende code halen we bijvoorbeeld eenvoudig een Gebruiker . op record uit de database.

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

Merk op dat we elke keer dat we met de database moeten communiceren, drie objecten moeten maken:een verbinding (Verbinding ), verklaring (PreparedStatement ), en resultatenset (ResultSet ). Al deze moeten ook worden ingesloten in de opgelegde try…catch blok. Zelfs het sluiten van de verbinding moet worden ingesloten in try…catch . Dit is belachelijk omdat de eigenlijke vereiste code voor de functie veel minder is. De code is gewoon opgeblazen met onnodige maar verplichte code en moet worden herhaald waar we ook met de database omgaan. Een slim coderingsschema kan deze puinhoop verminderen, maar het is onmogelijk om het probleem van de standaard JDBC-code uit te roeien. Dit is niet alleen het probleem met JDBC, maar ook met JMS, JNDI en REST.

De oplossing van de lente

Het Spring-framework bood een oplossing voor deze puinhoop en bood een middel om standaardtekstcode te elimineren door sjabloonklassen te gebruiken. Deze klassen kapselen de standaardcode in, waardoor de programmeur wordt ontlast. Dit betekent dat de boilerplate-code er nog steeds is, alleen de programmeur die een van de sjabloonklassen gebruikt, is verlost van de moeite om deze te schrijven. De JdbcTemplate geleverd door Spring is de centrale klasse van het JDBC-kernpakket.

Het vereenvoudigt het gebruik van JDBC en helpt veelvoorkomende fouten te voorkomen. Het voert de kern JDBC-workflow uit en laat de applicatiecode achter om SQL te leveren en resultaten te extraheren. Deze klasse voert SQL-query's of -updates uit, initieert iteratie over ResultSets en onderschept JDBC-uitzonderingen en vertaalt deze naar de generieke, meer informatieve uitzonderingshiërarchie gedefinieerd in de org.springframework.dao pakket.

We hoeven alleen de call-back-interfaces te implementeren en ze een duidelijk, gedefinieerd contract te geven. Bijvoorbeeld de PreparedStatementCreator callback-interface wordt gebruikt voor het maken van een voorbereide verklaring. De ResultsetExtractor interface werkt als een ResultSet .

Daarom kan het voorgaande codefragment worden herschreven met JdbcTemplate als volgt:

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

De RowMapper is een interface die doorgaans wordt gebruikt door de JdbcTemplate om één rij toe te wijzen per basis van rijen van de ResultSet . De RowMapper objecten zijn staatloos en dus herbruikbaar. Ze zijn perfect voor het implementeren van elke logica voor rijtoewijzing. Merk op dat we in de vorige code uitzonderingen niet expliciet hebben behandeld zoals we hebben gedaan in de code die geen gebruik maakt van JdbcTemplate . De RowMapper-implementatie voert de daadwerkelijke implementatie uit van het toewijzen van elke rij aan het resultaatobject zonder dat de programmeur zich zorgen hoeft te maken over het afhandelen van uitzonderingen. Het wordt aangeroepen en afgehandeld door JdbcTemplate . te bellen .

De uitzonderingen

De uitzonderingen die JDBC biedt, zijn vaak te ingrijpend dan nodig, met weinig waarde. De uitzonderingshiërarchie voor gegevenstoegang van Spring is in dit opzicht meer gestroomlijnd en redelijk. Dit betekent dat het een consistente set uitzonderingsklassen in zijn arsenaal heeft in tegenstelling tot JDBC's one size fit to all exception genaamd SQLException voor alle problemen met betrekking tot gegevenstoegang. De gegevenstoegangsuitzonderingen van Spring zijn geroot met de DataAccessException klas. Daarom kunnen we zowel de keuze van een gecontroleerde als een niet-gecontroleerde uitzondering in het raamwerk hebben ingebakken. Dit klinkt praktischer omdat er echt geen oplossingen zijn voor veel van de problemen die zich hebben voorgedaan tijdens runtime-gegevenstoegang en het is zinloos om ze te pakken te krijgen als we de situatie niet kunnen aanpakken met een geschikt alternatief.

Springs manier om gegevenstoegang te vereenvoudigen

Wat Spring eigenlijk doet, is dat het het vaste en variabele deel van het gegevenstoegangsmechanisme onderscheidt in twee sets klassen die sjabloonklassen worden genoemd. en terugbellessen , respectievelijk. Het vaste deel van de code vertegenwoordigt het plichtmatige deel van gegevenstoegang en het variabele deel is die gegevenstoegangsmethode die varieert afhankelijk van de veranderende behoefte.

Kortom, de sjabloonklassen handvat:

  • Transactiebeheer
  • Bronnenbeheer
  • Afhandeling van uitzonderingen

En de callback-klassen handvat:

  • Een query-instructie maken
  • Parameterbinding
  • Resultatenset marshalling

We kunnen een van de vele sjabloonklassen kiezen, afhankelijk van de keuze van de gebruikte persistente technologie. Voor JDBC kunnen we bijvoorbeeld kiezen voor JdbcTemplate , of voor ORM kunnen we kiezen voor JpaTemplate , HibernateTemplate , enzovoort.

Nu we verbinding maken met de database, hebben we drie opties om de gegevensbron te configureren, zoals:

  • Gedefinieerd door het JDBC-stuurprogramma
  • Opgezocht door JNDI
  • Opgehaald uit verbindingspool

Een productieklare applicatie maakt doorgaans gebruik van een verbindingspool of JNDI. De door het JDBC-stuurprogramma gedefinieerde gegevensbronnen zijn verreweg de eenvoudigste, hoewel deze meestal voor testdoeleinden worden gebruikt. Spring biedt drie klassen in het pakket org.springframework.jdbc.datasource van deze categorie; ze zijn:

  • DriverManagerDataSource: Eenvoudige implementatie van de standaard JDBC DataSource interface, het configureren van de gewone oude JDBC DriverManager via bean-eigenschappen en het retourneren van een nieuwe Verbinding van elk verzoek.
  • SingleConnectionDataSource: Retourneert bij elk verzoek dezelfde verbinding. Dit type verbinding is voornamelijk bedoeld om te testen.
  • SimpleDriverDataSource: Hetzelfde als DriverManagerDataSource behalve dat het speciale problemen heeft met het laden van klassen, zoals OSGi; deze klasse werkt rechtstreeks met JDBC Driver.

Het configureren van deze gegevensbronnen is vergelijkbaar. We kunnen ze configureren in een bean class of 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-sjabloonklassen

Spring biedt een aantal sjabloonklassen om gegevenstoegang met JDBC te vereenvoudigen:

  • JdbcTemplate: Dit is de basisklasse van het kern-JDBC-pakket org.springframework.jdbc.core die de eenvoudigste toegang tot de database biedt via geïndexeerde zoekopdrachten.
  • NamedParameterJdbcTemplate: Deze sjabloonklasse biedt ook een basisset van JDBC-bewerkingen waarbij de waarden zijn gebonden aan benoemde parameters in plaats van traditionele '?'-plaatsaanduidingen in SQL-query's.

JDBC-terugbellessen

De belangrijkste JDBC-callback-functionele interfaces gedefinieerd in org.springframework.jdbc.core zijn:

  • CallableStatementCallback: Werkt op de JDBC CallableStatement. Deze callback wordt intern gebruikt door JdbcTemplate en staat uitvoering toe op een enkele CallableStatement zoals enkele of meerdere SQL-aanroepen met verschillende parameters.
  • PreparedStatementCallback: Werkt op JDBC PreparedStatement. Deze callback wordt intern gebruikt door JdbcTemplate en staat uitvoering van meer dan één bewerking toe op een enkele PreparedStatement zoals enkele of meerdere SQL executeUpdate-aanroep met verschillende parameters.
  • StatementCallback: Werkt op de JDBC Statement . Deze callback wordt ook intern gebruikt door JdbcTemplate om meer dan één bewerking uit te voeren op een enkele Statement zoals enkele of meerdere SQL executeUpdate-aanroepen.

Een eenvoudig Spring Boot JDBC-voorbeeld

Laten we een eenvoudig voorbeeld van een springboot proberen. Een Spring boot-project handelt automatisch veel van de complexiteiten van de configuratie af, waarbij een ontwikkelaar van alle problemen wordt verlost zodra een juiste afhankelijkheid is opgenomen in het Maven-bestand pom.xml . Om de lengte van het artikel kort te houden, zullen we geen code-uitleg opnemen. Gebruik de referenties aan het einde van het artikel voor een meer gedetailleerde beschrijving.

Om aan het volgende voorbeeld te werken, maakt u als volgt een database en een tabel in MySQl:

Log in op de MySQL-database en maak een database en een tabel aan met het volgende commando:

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

Begin als een Spring-starterproject vanuit Spring Tool Suite (STS) met de afhankelijkheid JDBC en MySQL. Het Maven-configuratiebestand, pom.xml , van het project is als volgt:

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

}

Interface gegevenstoegangsobject: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-klasse: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);
      }

   }

}

Applicatie.properties

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

Voer de applicatie uit

Om de toepassing uit te voeren, klikt u met de rechtermuisknop op het project in de Projectverkenner en selecteer Uitvoeren als -> Spring Boot-app . Dat is alles.

Conclusie

We hebben drie opties om te werken met relationele database-programmering met Spring:

  • De ouderwetse JDBC met Spring. Dit betekent dat je het Spring-framework moet gebruiken voor alle praktische doeleinden in het programma, behalve de gegevensondersteuning van Spring.
  • JDBC-sjabloonklassen gebruiken. Spring biedt JDBC-abstractieklassen om relationele databases te doorzoeken; deze zijn veel eenvoudiger dan werken met native JDBC-code.
  • Spring biedt ook uitstekende ondersteuning voor het ORM-framework (Object Relational Mapping) en kan goed worden geïntegreerd met een prominente implementatie van de JPA-API (Java Persistent Annotation), zoals Hibernate. Het heeft ook zijn eigen Spring Data JPA-assistentie die tijdens runtime automatisch repository-implementatie kan genereren.

Als men om wat voor reden dan ook voor JDBC kiest, is het beter om Spring-sjabloonondersteuning te gebruiken, zoals JdbcTemplate anders dan het gebruik van ORM.

Referenties

  • Muren, rots. Lente in actie 4 , Manning-publicaties
  • Spring 5 API-documentatie

  1. SQL Server Geheel getal converteren naar binaire tekenreeks

  2. Hoe kan ik een JSON-object in Postgres invoegen met behulp van Java PreparedStatement?

  3. SIGN() Voorbeelden in SQL Server

  4. Is het mogelijk om naar één kolom te verwijzen als meerdere externe sleutels?