※当サイトの記事には、広告・プロモーションが含まれます。

Apache Commons DbUtilsでデータベースとやり取りしてみる

thinkit.co.jp

⇧ 対応方法が分かりにくいとか、アプリケーションの構成によっては対応しようがないパターンもあるかもしれんので何とも言えませんな...

Apache Commons DbUtilsとは?

公式のドキュメントによりますと、

commons.apache.org

The Commons DbUtils library is a small set of classes designed to make working with JDBC easier. JDBC resource cleanup code is mundane, error prone work so these classes abstract out all of the cleanup tasks from your code leaving you with what you really wanted to do with JDBC in the first place: query and update data.

https://commons.apache.org/proper/commons-dbutils/index.html

Some of the advantages of using DbUtils are:

  • No possibility for resource leaks. Correct JDBC coding isn't difficult but it is time-consuming and tedious. This often leads to connection leaks that may be difficult to track down.
  • Cleaner, clearer persistence code. The amount of code needed to persist data in a database is drastically reduced. The remaining code clearly expresses your intention without being cluttered with resource cleanup.
  • Automatically populate JavaBean properties from ResultSets. You don't need to manually copy column values into bean instances by calling setter methods. Each row of the ResultSet can be represented by one fully populated bean instance.

https://commons.apache.org/proper/commons-dbutils/index.html

JDBCJava DataBase Connectivity)をラップして使いやすくしたような感じなんですかね。

Entityとのマッピングはどうする?

データベースのテーブルをJavaで表現したEntityとのマッピングはどうするか?

stackoverflow.com

syntaxfix.com

⇧ 見た感じでは、Apache Commons DbUtilsAPIで対応できるらしい、ただ、JOINとかしたテーブルのケースについては言及されてないので、JOINしたテーブルはどう扱うべきかは謎です...。

2023年3月2日(木)追記:↓ ここから

どうやら、

stackoverflow.com

Based on second solution you can use GenerousBeanProcessor which is a subclass of BeanProcessor it ignores underscore & case sensitivity from column name. You don't have to implement your own custom BeanProcessor

GenerousBeanProcessor is available since version 1.6 of commons-dbutils.

https://stackoverflow.com/questions/24563564/dbutils-fails-to-fill-fields-of-a-java-bean

Apache Commons DBUtilsAPIで、テーブルのカラム名Javaのクラスのフィールド名をマッピングする際に、snakeCaseとlowerCamelCaseの違いとかを気にしなくても良いようです。

つまり、

  • DBのテーブルのカラム名
    user_full_name
  • Javaのクラスのフィールド
    userFullName

のような場合は、Apache Commons DBUtilsAPIが良しなにマッピングしてくれるということらしい。

2023年3月2日(木)追記:↑ ここまで

Apache Commons DbUtilsでデータベースとやり取りしてみる

というわけで、使ってみる。

利用するプロジェクトは、

ts0818.hatenablog.com

⇧ 前回の記事のもので。

まずは、pom.xmlで依存関係を追加します。

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com</groupId>
	<artifactId>spring-mvc-jsp</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<maven.compiler.target>${java.version}</maven.compiler.target>
		<maven.compiler.source>${java.version}</maven.compiler.source>
		<org.springframework-version>5.3.24</org.springframework-version>
	</properties>
	<dependencies>
		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>

		<!-- Servlet -->
		<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>4.0.1</version>
			<scope>provided</scope>
		</dependency>
		<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>javax.servlet.jsp-api</artifactId>
			<version>2.3.3</version>
			<scope>provided</scope>
		</dependency>
		<!-- https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
		
		<!-- Database -->
		<!-- https://mvnrepository.com/artifact/commons-dbutils/commons-dbutils -->
		<dependency>
			<groupId>commons-dbutils</groupId>
			<artifactId>commons-dbutils</artifactId>
			<version>1.7</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/com.oracle.database.jdbc/ojdbc11 -->
		<dependency>
			<groupId>com.oracle.database.jdbc</groupId>
			<artifactId>ojdbc11</artifactId>
			<version>21.8.0.0</version>
		</dependency>
		
		<!-- Lombok -->
		<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.24</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<version>3.3.2</version>
				<configuration>
					<webXml>src/main/webapp/WEB-INF/web.xml</webXml>
					<warSourceDirectory>src/main/webapp</warSourceDirectory>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

ポイントとしては、Spring FrameworkApache Commons DbUtilsを利用する場合は、spring-jdbcの依存関係も追加したほうが良さそうってことですかね。

続いて、データベースの接続情報の設定ファイルを用意。

jdbc.url=jdbc:oracle:thin:@//localhost:1519/orcldb_19c
jdbc.username=ts0818
jdbc.password=password
jdbc.driverClassName=oracle.jdbc.driver.OracleDriver

⇧ 今回はデータベースにOracle Database 19cを使用しています。利用するデータベースなどはご自分の環境に合わせてください。

ちなみに、今回利用していくテーブルなどは、

ts0818.hatenablog.com

⇧ 上記の記事のときのものになります。

Spring Frameworkだけだと、Spring Bootのように良しなにデータベース接続情報を読み込んでくれないので、XMLファイルを用意してdataSourceのBeanをDI(Dependency injection)してあげる必要があるようです。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="
        http://www.springframework.org/schema/beans     
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">

	<context:component-scan base-package="com" />
	<context:property-placeholder
		location="classpath:database.properties" />

	<!-- <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" 
		destroy-method="close"> -->
	<!-- <bean id="dataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy"> -->
	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName"
			value="${jdbc.driverClassName}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>

	<!-- similarly, don't forget the TransactionManager -->
	<!-- <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
		<property name="dataSource" ref="dataSource" /> </bean> -->
	<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner"> 
		<constructor-arg ref="dataSource" /> </bean>
</beans>

そしたらば、

blog.rutake.com

qiita.com

stackoverflow.com

jp.coderbridge.com

⇧ 上記サイト様を参考に、Spring FrameworkJDBCJava DataBase Conectivity)でデータベースに接続するための設定を各ファイルに追加する必要があります。

まずは、Spring Frameworkのcontextを認識できるようにと、Spring Framework用の設定のXMLファイルを読み込むにしてあげる必要があるようです。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
	id="WebApp_ID" version="4.0">
	<display-name>spring-mvc-jsp</display-name>
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
		<welcome-file>index.jsp</welcome-file>
		<welcome-file>index.htm</welcome-file>
		<welcome-file>default.html</welcome-file>
		<welcome-file>default.jsp</welcome-file>
		<welcome-file>default.htm</welcome-file>
	</welcome-file-list>
	<!-- spring.xml -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/spring/spring.xml</param-value>
	</context-param>

	<!-- Start Spring -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<servlet>
		<servlet-name>spring-dispatcher</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>spring-dispatcher</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
</web-app>    

そしたらば、Spring Framework側で、データベース接続などに関する設定のXMLファイルを認識できるようにimportしてあげる必要があるようです。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	<import resource="../database.xml" />
	<import resource="../spring-dispatcher-servlet.xml" />

</beans>    

で、後は、Javaファイルやjspファイルにデータベースの情報を引っ張てくる処理を記述していきますが、プロジェクトの全体的な構成は以下のようになっています。

では、Javaファイルから。

■/spring-mvc-jsp/src/main/java/com/entity/UserDetail.java

package com.entity;

import lombok.Data;

@Data
public class UserDetail {

	private String userId;
	
	private String lastName;
	
	private String firstName;
}    

⇧ テーブルの全てのカラムに合わせたフィールドを用意しなくても良いらしいけど、普通に使う分には、テーブルのカラムに合わせたフィールドをすべて用意した方が良いような気はする、分からんけど。

■/spring-mvc-jsp/src/main/java/com/dao/UserDetailDao.java

package com.dao;

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

import org.apache.commons.dbutils.BasicRowProcessor;
import org.apache.commons.dbutils.GenerousBeanProcessor;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import com.entity.UserDetail;

@Component
//@RequiredArgsConstructor
public class UserDetailDao {
	
	@Autowired
	@Qualifier(value = "queryRunner")
	private QueryRunner queryRunner;
	
	public List<UserDetail> findAll() throws SQLException {
		ResultSetHandler<List<UserDetail>> resultSetHandler =
                new BeanListHandler<UserDetail>(UserDetail.class, new BasicRowProcessor(new GenerousBeanProcessor()));
		final List<UserDetail> userDetailList = queryRunner.query("select * from user_detail", resultSetHandler);
		return userDetailList;
	}
}    

JPAJava Persistence API)だと、repositoryパッケージとかの役割に該当するのが、daoパッケージですかね。ネットの情報を見ると、Apache Common DbUtilsを利用するプロジェクトだとDAOクラスとすることが多そうだったので、daoパッケージにしてます。

■/spring-mvc-jsp/src/main/java/com/service/UserDetailServiceImpl.java

package com.service;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.dao.UserDetailDao;
import com.entity.UserDetail;

@Service
public class UserDetailServiceImpl {

	@Autowired
	private UserDetailDao userDetailDao;
	
	public List<UserDetail> findAll() throws SQLException {
		return userDetailDao.findAll();
	}
	
}    

■/spring-mvc-jsp/src/main/java/com/controller/HomeController.java

package com.controller;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;

import com.entity.UserDetail;
import com.service.UserDetailServiceImpl;

@Controller
public class HomeController {
	
	@Autowired
	private UserDetailServiceImpl userDetailServiceImpl;

	@GetMapping("home")
	public String index(Model model) {
		List<String> list = new ArrayList<>();
		list.add("リスト1");
		list.add("リスト2");
		list.add("リスト3");
		model.addAttribute("strList", list);
		return "home/index";
	}
	
	@GetMapping("find-all")
	public ModelAndView findAll() throws SQLException {
		List<UserDetail> userDetailList = userDetailServiceImpl.findAll();
		ModelAndView mv = new ModelAndView("home/index");
		mv.addObject("userDetailList", userDetailList);
		return mv;
	}
}  

最後に、jspファイルの修正。

■/spring-mvc-jsp/src/main/webapp/WEB-INF/views/home/index.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ page language="java" contentType="text/html; charset=utf-8"%>
<html>
<head>
<title>Home</title>
</head>
<body>
	<c:forEach var="str" items="${strList}">
		<p>${str}</p>
	</c:forEach>
	<c:forEach var="userDetail" items="${userDetailList}">
		<p>
		  <span>${userDetail.lastName}</span>
		  <span>${userDetail.firstName} </span>
		</p>
	</c:forEach>
</body>
</html>    

で、編集と保存し終わったら、サーバーで実行します。

で、ブラウザから、http://localhost:8080/[プロジェクト名]/Controllerクラスのメソッドで設定したエンドポイント」にアクセスします。

⇧ データベースのテーブルからの情報が取得できているようです。

Apache Commons DbUtilsを利用する場合、

stackoverflow.com

⇧ データベースの接続を閉じるのは、Apache Commons DbUtilsが良しなに行ってくれるらしいです。

あとは、

shinsan.hatenablog.jp

ManyToOneとか関連を追うようなアプリならHibernateなどJPA系で。それ以外でフラットに結果セットをつめるだけならこっち系で。

第23夜 JavaSE5対応したDBUtilsを試す - しんさんの出張所 はてなブログ編

JPAJava Persistence API)のように、ManyToOneなどは実現できない模様。

JPAJava Persistence API)のManyToOneなどについては、

qiita.com

⇧ 上記サイト様が詳しいです。

Spring Framework用のXMLファイルの定義について情報が見つかりにくいのもあって、Spring Frameworkを利用するならSpring Bootを使いたいところですね...

何て言うか用意するXMLファイルが分かり辛いんだわさ...

レガシーなシステムだとSpring Bootが利用できない縛りもあるとは思うので何とも言えないですが...

毎度モヤモヤ感が半端ない...

今回はこのへんで。