コンピュータクワガタ

かっぱのかっぱによるコンピュータ関連のサイトです

Spring JDBCの紹介 No. 1

Spring FrameworkのSpring JDBCを使っていたのですが、

という気分になってきたので、紹介させてもらいます。

Spring Frameworkを使っていてちょっとDBアクセスしたいという時には非常に便利に使えると思います。

動くサンプルはhttps://github.com/kuwalab/SpringSampleのspring_db40に作っています。

導入

導入は例のごとくMavenです。Springが依存関係が結構深いのでMaven等を使わないと面倒です。pom.xmlは以下のようにします。今回はデータベースにH2を使うので合わせて導入します。

Spring JDBCを使うためには、spring-contextの他にspring-jdbcが必要になります。

<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/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.example.spring_db40</groupId>
 <artifactId>spring_db40</artifactId>
 <packaging>war</packaging>
 <version>0.0.1-SNAPSHOT</version>
 <name>mvcjsontest Maven Webapp</name>
 <url>http://maven.apache.org</url>
 <build>
  <finalName>spring_db40</finalName>
  <pluginManagement>
   <plugins>
    <plugin>
     <artifactId>maven-compiler-plugin</artifactId>
     <configuration>
      <source>1.7</source>
      <target>1.7</target>
      <encoding>UTF-8</encoding>
     </configuration>
    </plugin>
   </plugins>
  </pluginManagement>
 </build>
 <dependencies>
  <!-- Spring Framework -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-jdbc</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <!-- AspectJ -->
  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>${aspectJ.version}</version>
  </dependency>
  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjrt</artifactId>
   <version>${aspectJ.version}</version>
  </dependency>
  <dependency>
   <groupId>ch.qos.logback</groupId>
   <artifactId>logback-classic</artifactId>
   <version>1.0.11</version>
  </dependency>
  <dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>jcl-over-slf4j</artifactId>
   <version>1.7.5</version>
  </dependency>
  <dependency>
   <groupId>com.h2database</groupId>
   <artifactId>h2</artifactId>
   <version>1.4.177</version>
  </dependency>
 </dependencies>
 <properties>
  <spring.version>4.0.3.RELEASE</spring.version>
  <aspectJ.version>1.7.1</aspectJ.version>
  <junit.version>4.11</junit.version>
 </properties>
</project>

Spring JDBCの設定

データソースは適当に用意してください。今回は、組み込みのH2を用意します。Springの設定ファイルに以下のように記述します。

<jdbc:embedded-database id="dataSource" type="H2">
 <jdbc:script location="classpath:/ddl/db01/schema.sql" />
</jdbc:embedded-database>

jdbc:scriptのlocation属性で起動時に読み込むscriptを指定できます。毎回新規に作成されるようなので、何度実行しても起動時は同じ状態になっています。

scriptは単純にSQLを書けばいいです。今回はbookテーブルを作成しテスト用のデータを入れておきます。

CREATE TABLE book(
  book_id INTEGER PRIMARY KEY,
  book_name VARCHAR(20),
  price INTEGER
);

INSERT INTO book VALUES(1,'よくわかるSpring',2000);
INSERT INTO book VALUES(2,'すぐわかるSpring',2980);

Spring JDBCはJdbcTemplateクラスを介してアクセスします。そのためのBeanも設定しておきます。

<bean class="org.springframework.jdbc.core.JdbcTemplate">
 <constructor-arg ref="dataSource" />
</bean>

最低限必要な設定は以上です。

基本的なCRUD

最低限のCRUDを面倒な方法で実装してみます。一番ロジカルでわかりやすいですが、非常に面倒な方法です。まずDaoクラスの全体像を見てみます。

package com.example.spring.db01;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

@Repository
public class BookDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public List<Book> selectBookList() {
        return jdbcTemplate.query("SELECT * FROM book", new RowMapper<Book>() {
            public Book mapRow(ResultSet rs, int rowNum) throws SQLException {
                Book book = new Book();
                book.setBookId(rs.getInt(1));
                book.setBookName(rs.getString(2));
                book.setPrice(rs.getInt(3));
                return book;
            }
        });
    }

    public int insert(Book book) {
        return jdbcTemplate.update(
                "INSERT INTO BOOK(book_id,book_name,price) VALUES(?,?,?)",
                book.getBookId(), book.getBookName(), book.getPrice());
    }

    public int update(Book book) {
        return jdbcTemplate.update(
                "UPDATE book SET book_name=?,price=? WHERE book_id=?",
                book.getBookName(), book.getPrice(), book.getBookId());
    }

    public int delete(Book book) {
        return jdbcTemplate.update("DELETE FROM book WHERE book_id=?",
                book.getBookId());
    }
}

Bookクラスは単純なJavaBeansです。

public class Book {
    private Integer bookId;
    private String bookName;
    private Integer price;

    @Override
    public String toString() {
        return "Book [bookId=" + bookId + ", bookName=" + bookName + ", price="
                + price + "]";
    }

    public Integer getBookId() {
        return bookId;
    }

    public void setBookId(Integer bookId) {
        this.bookId = bookId;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public Integer getPrice() {
        return price;
    }

    public void setPrice(Integer price) {
        this.price = price;
    }

}
SELECT

最初にSELECT文を見ていきます。

public List<Book> selectBookList() {
    return jdbcTemplate.query("SELECT * FROM book", new RowMapper<Book>() {
        public Book mapRow(ResultSet rs, int rowNum) throws SQLException {
            Book book = new Book();
            book.setBookId(rs.getInt(1));
            book.setBookName(rs.getString(2));
            book.setPrice(rs.getInt(3));
            return book;
        }
    });
}

queryメソッドはいろいろな引数でオーバーロードされています。今回は次のものを使います。

<T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException

SQLにはパラメータを渡さない例になっています。引数のsqlを実行し、その結果をRowMapperで処理して型TのListを返します。

RowMapperはインターフェイスのため、そこで定義されているmapRowメソッドを実装する必要があります。

mapRowメソッドの定義は次のとおりです。

T mapRow(ResultSet rs, int rowNum) throws SQLException

戻り値は行ごとに取得した結果から作成するオブジェクトになります。SQLを発行した結果の行単位にmapRowメソッドが呼び出されるため、ResultSetとrowNumから必要なデータを作成します。

例では、生のJDBCの如く処理をしています。

更新

更新処理はupdateメソッドで実装します。

public int insert(Book book) {
    return jdbcTemplate.update(
            "INSERT INTO BOOK(book_id,book_name,price) VALUES(?,?,?)",
            book.getBookId(), book.getBookName(), book.getPrice());
}

public int update(Book book) {
    return jdbcTemplate.update(
            "UPDATE book SET book_name=?,price=? WHERE book_id=?",
            book.getBookName(), book.getPrice(), book.getBookId());
}

public int delete(Book book) {
    return jdbcTemplate.update("DELETE FROM book WHERE book_id=?",
            book.getBookId());
}

1番目の引数にSQL文、2番目の引数は可変長引数になっていて、?の割り当てる値を?の順番通りに指定してきます。

まとめ

今回の分はJDBCとそれほど変わらないためSpring JDBCめんどくせーという感じですが、次回からもっと便利な方法を紹介します。

Spring3入門 ――Javaフレームワーク・より良い設計とアーキテクチャ

Spring3入門 ――Javaフレームワーク・より良い設計とアーキテクチャ

まとめ http://kuwalab.hatenablog.jp/entry/spring
最初
次回 http://kuwalab.hatenablog.jp/entry/spring_jdbc/2