⇧ 対応方法が分かりにくいとか、アプリケーションの構成によっては対応しようがないパターンもあるかもしれんので何とも言えませんな...
Apache Commons DbUtilsとは?
公式のドキュメントによりますと、
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
⇧ JDBC(Java DataBase Connectivity)をラップして使いやすくしたような感じなんですかね。
Entityとのマッピングはどうする?
データベースのテーブルをJavaで表現したEntityとのマッピングはどうするか?
⇧ 見た感じでは、Apache Commons DbUtilsのAPIで対応できるらしい、ただ、JOINとかしたテーブルのケースについては言及されてないので、JOINしたテーブルはどう扱うべきかは謎です...。
2023年3月2日(木)追記:↓ ここから
どうやら、
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 DBUtilsのAPIで、テーブルのカラム名とJavaのクラスのフィールド名をマッピングする際に、snakeCaseとlowerCamelCaseの違いとかを気にしなくても良いようです。
つまり、
のような場合は、Apache Commons DBUtilsのAPIが良しなにマッピングしてくれるということらしい。
2023年3月2日(木)追記:↑ ここまで
Apache Commons DbUtilsでデータベースとやり取りしてみる
というわけで、使ってみる。
利用するプロジェクトは、
⇧ 前回の記事のもので。
まずは、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 FrameworkでApache 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を使用しています。利用するデータベースなどはご自分の環境に合わせてください。
ちなみに、今回利用していくテーブルなどは、
⇧ 上記の記事のときのものになります。
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>
そしたらば、
⇧ 上記サイト様を参考に、Spring FrameworkでJDBC(Java 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; } }
⇧ JPA(Java 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を利用する場合、
⇧ データベースの接続を閉じるのは、Apache Commons DbUtilsが良しなに行ってくれるらしいです。
あとは、
⇧ JPA(Java Persistence API)のように、ManyToOneなどは実現できない模様。
JPA(Java Persistence API)のManyToOneなどについては、
⇧ 上記サイト様が詳しいです。
Spring Framework用のXMLファイルの定義について情報が見つかりにくいのもあって、Spring Frameworkを利用するならSpring Bootを使いたいところですね...
何て言うか用意するXMLファイルが分かり辛いんだわさ...
レガシーなシステムだとSpring Bootが利用できない縛りもあるとは思うので何とも言えないですが...
毎度モヤモヤ感が半端ない...
今回はこのへんで。