実際のアプリケーションシナリオでは、データが実際に処理されてリポジトリに永続化されるバックエンドサーバーで大量の処理が実行されます。 DI(依存性注入)、アスペクト、POJO指向の開発など、Springの多くの優れた機能とは別に、Springはデータ処理を優れた方法でサポートしています。優れたデータベースアプリケーションを作成するには、さまざまな方法があります。現在でも、JDBCデータアクセス機能に基づいて多数のアプリケーションが作成されています。この記事では、Springに関連するJDBC、そのサポート、および適切な例とコードスニペットを使用した長所と短所について具体的に説明します。
JDBCの概要
ORMの世界でJDBCを引き続き使用する最大の利点の1つは、はるかに低いレベルでデータを操作する以外に、別のフレームワークのクエリ言語を習得する必要がないことです。これにより、プログラマーはデータベース独自の機能を利用できます。欠点もあります。残念ながら、不利な点はしばしば目に見えるので、言及する必要はありません。たとえば、そのうちの1つはボイラープレートコード 。用語ボイラープレートコード 基本的には、コードに値を組み込まずに同じコードを何度も書くことを意味します。これは通常、データベースからデータをクエリするときに見られます。たとえば、次のコードでは、ユーザーを取得するだけです。 データベースからの記録。
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; }
データベースと対話する必要があるたびに、接続(接続)の3つのオブジェクトを作成する必要があることに注意してください。 )、ステートメント( PreparedStatement )、および結果セット( ResultSet )。これらはすべて、課せられたtry…catch内に含める必要があります ブロック。接続の閉鎖でさえ、try…catchで囲む必要があります 。関数に実際に必要なコードがはるかに少ないため、これはばかげています。このコードは、不要ではあるが必須のコードで埋め尽くされているだけであり、データベースを操作する場所ではどこでも繰り返す必要があります。巧妙なコーディングスキームはこの混乱を減らすかもしれませんが、定型的なJDBCコードの問題を根絶することは不可能です。これは、JDBCだけでなく、JMS、JNDI、およびRESTの問題でもあります。
春のソリューション
Springフレームワークは、この混乱に対する解決策を提供し、テンプレートクラスを使用してボイラープレートコードを排除する手段を提供しました。これらのクラスはボイラープレートコードをカプセル化するため、プログラマーは安心します。これは、ボイラープレートコードがまだ存在していることを意味し、テンプレートクラスの1つを使用するプログラマーだけがそれを書く手間から解放されます。 JdbcTemplate Springが提供するのは、JDBCコアパッケージの中心的なクラスです。
JDBCの使用を簡素化し、一般的なエラーを回避するのに役立ちます。コアJDBCワークフローを実行し、SQLを提供して結果を抽出するアプリケーションコードを残します。このクラスは、SQLクエリまたは更新を実行し、ResultSetに対して反復を開始し、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の行に基づいて1行をマップします 。 RowMapper オブジェクトはステートレスであるため、再利用できます。これらは、行マッピングロジックの実装に最適です。前のコードでは、 JdbcTemplate を使用しないコードで行ったように、例外を明示的に処理しなかったことに注意してください。 。 RowMapper実装は、プログラマーが例外処理について心配することなく、各行を結果オブジェクトにマッピングする実際の実装を実行します。 JdbcTemplate を呼び出すことにより、呼び出されて処理されます。 。
例外
JDBCによって提供される例外は、多くの場合、必要以上に印象的であり、価値はほとんどありません。 Springのデータアクセス例外階層は、この点でより合理化され、合理的です。これは、 SQLException と呼ばれるすべての例外にJDBCが1つのサイズで適合するのとは対照的に、その武器庫に一貫した一連の例外クラスがあることを意味します。 データアクセスに関連するすべての問題。 Springのデータアクセス例外は、 DataAccessExceptionに基づいています。 クラス。したがって、チェックされた例外とチェックされていない例外の両方をフレームワークに組み込むことができます。ランタイムデータアクセス中に発生した問題の多くに対する解決策は実際にはなく、適切な代替手段で状況に対処できない場合にそれらを捕らえることは無意味であるため、これはより実用的に聞こえます。
データアクセスを簡素化するSpringの方法
Springが実際に行うことは、データアクセスメカニズムの固定部分と可変部分をテンプレートクラスと呼ばれる2つのクラスセットに区別することです。 およびコールバッククラス 、 それぞれ。コードの固定部分はデータアクセスの機能的な部分を表し、可変部分は変化する要件に応じて変化するデータアクセス方法です。
つまり、テンプレートクラス ハンドル:
- トランザクション制御
- リソース管理
- 例外処理
そして、コールバッククラス ハンドル:
- クエリステートメントの作成
- パラメータバインディング
- 結果セットのマーシャリング
使用する永続テクノロジーの選択に応じて、多くのテンプレートクラスから1つを選択できます。たとえば、JDBCの場合、 JdbcTemplateを選択できます。 、またはORMの場合、 JpaTemplateを選択できます 、 HibernateTemplate 、など。
これで、データベースに接続するときに、データソースを構成するための次の3つのオプションがあります。
- JDBCドライバーによって定義されます
- JNDIによって検索されました
- 接続プールから取得
実動対応アプリケーションは通常、接続プールまたはJNDIを使用します。 JDBCドライバーで定義されたデータソースは、主にテスト目的で使用されますが、はるかに単純です。 Springは、パッケージ org.springframework.jdbc.datasourceで3つのクラスを提供します。 このカテゴリの;それらは:
- DriverManagerDataSource: 標準のJDBCDataSourceの簡単な実装 インターフェイス、プレーンな古い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: このテンプレートクラスは、SQLクエリの従来の「?」プレースホルダーではなく名前付きパラメーターで値がバインドされるJDBC操作の基本セットも提供します。
JDBCコールバッククラス
org.springframework.jdbc.coreで定義されている主要なJDBCコールバック機能インターフェース は:
- CallableStatementCallback
: JDBCCallableStatement。で動作します このコールバックは、 JdbcTemplateによって内部的に使用されます 単一のCallableStatementでの実行を許可します 異なるパラメータを使用した単一または複数のSQL実行呼び出しなど。 - PreparedStatementCallback
: JDBCPreparedStatement。で動作します このコールバックは、 JdbcTemplateによって内部的に使用されます 1つのPreparedStatementで複数の操作を実行できます 異なるパラメータを使用した単一または複数のSQLexecuteUpdate呼び出しなど。 - StatementCallback
: JDBCステートメントで動作します 。このコールバックは、 JdbcTemplateによって内部的にも使用されます。 1つのステートメントに対して複数の操作を実行する 単一または複数のSQLexecuteUpdate呼び出しなど。
単純なSpringBootJDBCの例
簡単な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) );
依存関係がJDBCとMySQLであるSpringToolSuite(STS)からSpringスタータープロジェクトとして開始します。 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
アプリケーションを実行する
アプリケーションを実行するには、プロジェクトエクスプローラーでプロジェクトを右クリックします。 ペインで[名前を付けて実行]を選択します ->スプリングブートアプリ 。それだけです。
結論
Springでリレーショナルデータベースプログラミングを使用するには、次の3つのオプションがあります。
- Springを使用した昔ながらのJDBC。これは、Springのデータサポートを除くプログラムのすべての実用的な目的でSpringフレームワークを使用することを意味します。
- JDBCテンプレートクラスの使用。 Springは、リレーショナルデータベースをクエリするためのJDBC抽象化クラスを提供しています。これらは、ネイティブJDBCコードを使用するよりもはるかに簡単です。
- Springは、ORM(Object Relational Mapping)フレームワークの優れたサポートも備えており、HibernateなどのJPA(Java Persistent Annotation)APIの優れた実装とうまく統合できます。また、実行時にオンザフライでリポジトリ実装を自動的に生成できる独自のSpringDataJPAアシスタンスもあります。
何らかの理由でJDBCを選択する場合は、 JdbcTemplateなどのSpringテンプレートサポートを使用することをお勧めします。 ORMを使用する以外。
参照
- 壁、岩山。 Spring in Action 4 、マニング出版物
- Spring5APIドキュメント