관리 메뉴

거니의 velog

(9) 스프링 JDBC 기능 본문

Java_Spring Framework part1

(9) 스프링 JDBC 기능

Unlimited00 2023. 10. 28. 15:25

1. 스프링 JDBC로 데이터베이스와의 연동 설정하기

* JDBC(Java Database Connectivity)는 자바 데이터 접근 기술의 근간이라 할 정도로 대부분의 개발자가 쉽게 이해할 수 있어 많이 사용하는 데이터 엑세스 기술이다. 그러나 시간이 지남에 따라 SQL 문이 지나치게 복잡해지면서 개발이나 유지관리에 어려움이 생기기 시작했다. 특히 Connection 객체 같은 공유 리소스를 제대로 처리해 주지 않으면 버그를 발생시키는 원인이 되곤 했다.
* 스프링에서 제공하는 JDBC는 이러한 기존 JDBC의 장점과 단순함을 유지하면서 단점을 보완했다. 간결한 API뿐만 아니라 확장된 JDBC의 기능도 제공한다.

* 물론 실제 개발을 진행할 때는 스프링 JDBC 기능보다는 마이바티스나 하이버네이트 같은 데이터베이스 연동
  관련 프레임워크를 사용하지만 스프링 JDBC의 기본적인 기능을 알아주면 도움이 된다.
  따라서 이번에는 아주 기초적인 것만 짚고 넘어갈 것이다. 마이바티스는 추후에 다룰 것이다.

1. 새 프로젝트 pro22를 만들고 pro21의 lib 폴더의 라이브러리와 web.xml, action-servlet.xml을 복사한 후 동일한 위치에 붙여 넣는다.

2. 그리고 스프링 JDBC 실습에 필요한 XML 파일들을 다음과 같이 만들어 준비한다.

* 각 XML 파일들에 대한 설명은 다음 표와 같다.
< 스프링 JDBC 설정 파일들 >

파일설명
web.xmlContextLoaderListener를 이용해 빈 설정 XML 파일들을 읽어 들인다.
action-servlet.xml스프링에서 필요한 여러 가지 빈을 설정한다.
action-dataSource.xml스프링 JDBC 설정에 필요한 정보를 설정한다.
jdbc.properties데이터베이스 연결 정보를 저장한다.
action-service.xml서비스 빈 생성을 설정한다.

3. action-servlet.xml과 action-dataSource.xml의 <beans> 태그는 전에 실습한 action-servlet.xml의 것을 복사해서 붙여 넣으면 된다.

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

4. web.xml을 다음과 같이 작성한다. 한 개의 XML 파일에서 모든 빈을 설정하면 복잡해서 관리하기 어려우므로 빈의 종류에 따라 XML 파일에 나누어서 설정한다. 그러면 톰캣 실행시 web.xml에서 스프링의 ContextLoaderListener를 이용해 빈 설정 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_3_1.xsd" id="WebApp_ID" version="3.1">

   <listener>
      <listener-class>
         org.springframework.web.context.ContextLoaderListener
     </listener-class>
   </listener> <!-- 여러 설정 파일을 읽어 들이기 위해 스프링의 ContextLoaderListener를 설정한다. -->

   <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>
          /WEB-INF/config/action-service.xml
          /WEB-INF/config/action-dataSource.xml <!-- 애플리케이션 실행 시 ContextLoaderListener로 해당 위치의 설정 파일을 읽어 들인다. -->
      </param-value>
   </context-param>
   
   <servlet>
      <servlet-name>action</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <load-on-startup>1</load-on-startup>
   </servlet>

   <servlet-mapping>
      <servlet-name>action</servlet-name>
      <url-pattern>*.do</url-pattern>
   </servlet-mapping>
   
</web-app>

5. action-servlet.xml을 다음과 같이 작성한다. 여기서는 뷰 관련 빈과 요청을 처리할 빈 그리고 메서드를 설정한다. 이때 주의할 점은 빈 주입 시 주입 받는 클래스에서는 주입되는 빈에 대한 setter를 반드시 구현해야 한다는 것이다.

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">


	<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
		<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
		<property name="prefix" value="/WEB-INF/views/" /> <!-- URI 요청명에 대해 /WEB-INF/views 폴더의 jsp를 선택한다. -->
		<property name="suffix" value=".jsp" />
	</bean>
   
	<bean id="memberController" class="com.spring.member.controller.MemberControllerImpl">
		<property name="methodNameResolver">
			<ref local="methodResolver"/>
		</property>
		<property name="memberService" ref="memberService"/> <!-- memberController 빈에 methodNameResolver 빈과 memberService 빈을 주입한다. -->
	</bean>

	<bean id="methodResolver" class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver" >
		<property name="mappings" >
			<props>
				<prop key="/member/listMembers.do" >listMembers</prop>
				<prop key="/member/addMember.do" >addMember</prop>
				<prop key="/member/memberForm.do" >memberForm</prop>
				<prop key="/member/memberDetail.do">memberDetail</prop> <!-- URI 요청명에 대해 동일한 이름의 메서드를 호출하도록 설정한다. -->
			</props>
		</property>
	</bean>
   
	<bean id="userUrlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
		<property name="mappings">
			<props>
				<prop key="/member/*.do">memberController</prop>
			</props>
		</property>
	</bean>
	
</beans>

6. action-service.xml에서 memberService 빈을 설정하도록 작성한다. 데이터베이스와 연동할 때 필요한 memberDAO 빈을 주입하는 기능을 한다.

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 
	<bean id="memberService" class="com.spring.member.service.MemberServiceImpl">
		<property name="memberDAO" ref="memberDAO"/> <!-- memberService 빈의 memberDAO 속성에 memberDAO 빈을 주입한다. -->
	</bean>
   
</beans>

7. 다음으로 action-dataSource.xml을 작성한다. 이 파일은 스프링에서 사용할 데이터베이스 연동 정보를 설정한다. 먼저 jdbc.properties 파일에서 데이터베이스 연결 정보를 가져온 후 이 연결 정보를 이용해 스프링에서 제공하는 SImpleDriverDataSource로 id가 dataSource인 빈을 생성한다. 그리고 dataSource 빈을 memberDAO 빈으로 주입한다.

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

  
	<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" >
		<property name="locations" >
			<list>
				<value>/WEB-INF/config/jdbc.properties</value> <!-- jdbc.properties 파일에서 데이터베이스 연결에 필요한 4가지 설정 정보를 가져온다. -->
			</list>
		</property>
	</bean>
	
	<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
		<property name="driverClass" value="${jdbc.driverClassName}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean> <!-- jdbc.properties 파일의 4가지 설정 정보로 스프링의 SimpleDriverDataSource 빈 생성 시 데이터베이스에 연결한다. -->
	
	<bean id="memberDAO" class="com.spring.member.dao.MemberDAOImpl" >
		<property name="dataSource" ref="dataSource" /> <!-- 생성된 dataSource 빈을 memberDAO 빈에 주입한다. -->
	</bean> 
   
</beans>

8. config 폴더를 선택하고 마우스 오른쪽 버튼을 클릭한 후 New > File을 선택하여 파일 이름을 jdbc.properties로 입력한다. 그리고 다음과 같이 데이터베이스 연결 정보를 작성한다.

jdbc.driverClassName=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@localhost:1521:XE
jdbc.username=scott
jdbc.password=tiger

2. JdbcTemplate 클래스 이용해 회원 정보 조회하기

* 이제 자바 클래스와 JSP를 이용해 회원 테이블에서 조회한 회원 정보를 JSP에 출력해 보자.
1. 실습에 필요한 파일들을 다음과 같이 준비한다.

* 우선 이 파일들이 어떤 역할을 하는지, 즉 스프링을 이용해 회원 정보 테이블에 접근하여 회원 정보를 조회하는 과정을 그림으로 나타내었다.

* DAO 클래스에서 실제 스프링의 JDBC 기능을 제공하는 클래스는 JdbcTemplate이다.
* 다음 표는 이 클래스가 제공하는 여러 가지 SQL 관련 메서드들을 정리한 것이다.
< JdbcTemplate 클래스에서 제공하는 SQL 관련 메서드 >

기능메서드
insert, update, delete 관련 메서드int update(String query)
int update(String query, Object[] args)
int update(String query, Object[] args, int[] argTypes)
select 기능 메서드int queryForInt(String query)
int queryForInt(String query, Object[] args)
long queryForLong(String query)
long queryForLong(String query, Object[] args)
Object queryForObject(String query, Class requiredType)
List queryForList(String query)
List queryForList(String query, Object[] args)

* 그럼 본격적으로 JdbcTemplate 클래스를 이용해 회원 정보 조회를 실습해보자.
1. MemberControllerImpl 클래스를 다음과 같이 구현한다. 자신의 속성 memberService에 설정 파일에서 생성된 memberService 빈을 주입하기 위해 반드시 setter를 구현해야 한다.

package com.spring.member.controller;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;

import com.spring.member.service.MemberService;

public class MemberControllerImpl extends MultiActionController implements MemberController {
	
	private MemberService memberService;

	public void setMemberService(MemberService memberService) {
		this.memberService = memberService;
	} // 반드시 setter를 구현해야 한다.

	public ModelAndView listMembers(HttpServletRequest request, HttpServletResponse response) throws Exception {
		String viewName = getViewName(request);
		List membersList = memberService.listMembers();
		ModelAndView mav = new ModelAndView(viewName);
		mav.addObject("membersList", membersList); // addObject() 메서드를 이용해 조회한 회원 정보를 바인딩한다.
		return mav;
	} // ==> /member/listmembers.do로 요청 시 호출된다.
	
	private String getViewName(HttpServletRequest request) throws Exception {
		String contextPath = request.getContextPath();
		String uri = (String) request.getAttribute("javax.servlet.include.request_uri");
		if (uri == null || uri.trim().equals("")) {
			uri = request.getRequestURI();
		}

		int begin = 0;
		if (!((contextPath == null) || ("".equals(contextPath)))) {
			begin = contextPath.length();
		}

		int end;
		if (uri.indexOf(";") != -1) {
			end = uri.indexOf(";");
		} else if (uri.indexOf("?") != -1) {
			end = uri.indexOf("?");
		} else {
			end = uri.length();
		}

		String fileName = uri.substring(begin, end);
		if (fileName.indexOf(".") != -1) {
			fileName = fileName.substring(0, fileName.lastIndexOf("."));
		}
		if (fileName.lastIndexOf("/") != -1) {
			fileName = fileName.substring(fileName.lastIndexOf("/"), fileName.length());
		}
		return fileName;
	}
	
}

2. MemberServiceImpl 클래스에서는 자신의 속성인 memberDAO에 빈을 주입하기 위해 setter를 구현한다.

package com.spring.member.service;

import java.util.List;

import org.springframework.dao.DataAccessException;

import com.spring.member.dao.MemberDAO;

public class MemberServiceImpl implements MemberService {
	
	private MemberDAO memberDAO;

	public void setMemberDAO(MemberDAO memberDAO) {
		this.memberDAO = memberDAO;
	} // memberDAO 속성에 setter를 이용하여 설정 파일에서 생성된 memberDAO 빈을 주입한다.

	@Override
	public List listMembers() throws DataAccessException {
		List membersList = null;
		membersList = memberDAO.selectAllMembers();
		return membersList;
	}

}

3. MemberDAOImpl 클래스에서는 자신의 속성 jdbcTemplate에 dataSource 빈을 주입하기 위해 setter를 이용한다.

package com.spring.member.dao;

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

import javax.sql.DataSource;

import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import com.spring.member.vo.MemberVO;


public class MemberDAOImpl implements MemberDAO {
	
	private JdbcTemplate jdbcTemplate;
	public void setDataSource(DataSource dataSource) {
		this.jdbcTemplate = new JdbcTemplate(dataSource);
	} // 설정 파일에서 생성한 dataSource 빈을 setter를 이용해 JdbcTemplate 클래스 생성자의 인자로 입력한다.

	@Override
	public List selectAllMembers() throws DataAccessException {
		
		String query = "select id,pwd,name,email,joinDate" + " from t_member " + " order by joinDate desc";
		List membersList = new ArrayList();

		membersList = this.jdbcTemplate.query(query, new RowMapper() {
			public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
				MemberVO memberVO = new MemberVO();
				memberVO.setId(rs.getString("id"));
				memberVO.setPwd(rs.getString("pwd"));
				memberVO.setName(rs.getString("name"));
				memberVO.setEmail(rs.getString("email"));
				memberVO.setJoinDate(rs.getDate("joinDate"));
				return memberVO;
			}
		}); // JdbcTemplate 클래스의 query() 메서드 인자로 select 문을 전달해 조회한 레코드 개수만큼 MemberVO 객체를 생성한다. 각 레코드의 값을 속성에 저장하고 다시 memberList에 저장한다.
		
		return membersList;
	}

	@Override
	public int addMember(MemberVO memberVO) throws DataAccessException {
		
		String id = memberVO.getId();
		String pwd = memberVO.getPwd();
		String name = memberVO.getName();
		String email = memberVO.getEmail();
		String query = "insert into t_member(id,pwd, name,email) values  ("
		                 + "'" + id + "' ,"
	 	                 + "'" + pwd + "' ,"
		                 + "'" + name + "' ,"
		                 + "'" + email + "') ";
		System.out.println(query);
		int result = jdbcTemplate.update(query); // JdbcTemplate 클래스의 update() 메서드로 회원 정보를 추가한다.
		System.out.println(result);
		return result;
		
	}

}

4. listMembers.jsp 에서는 조회한 회원 정보를 표시해 준다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" 
    isELIgnored="false"  %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"  %>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />

<%
  request.setCharacterEncoding("UTF-8");
%>

<!DOCTYPE html>
<html lang="ko">

    <head>
        <meta charset=UTF-8">
        <title>회원 정보 출력창</title>
    </head>

    <body>
        <table border="1" align="center" width="80%">
            <tr align="center" bgcolor="lightgreen">
                <td><b>아이디</b></td>
                <td><b>비밀번호</b></td>
                <td><b>이름</b></td>
                <td><b>이메일</b></td>
                <td><b>가입일</b></td>
            </tr>

			<%-- 컨트롤러에서 바인딩한 memberList로 접근한다. --%>
            <c:forEach var="member" items="${membersList}">
                <tr align="center">
                    <td>${member.id}</td>
                    <td>${member.pwd}</td>
                    <td>${member.name}</td>
                    <td>${member.email}</td>
                    <td>${member.joinDate}</td>

                </tr>
            </c:forEach>
        </table>
        <a href="${contextPath}/member/memberForm.do">
            <h1 style="text-align:center">회원가입</h1>
        </a>
    </body>

</html>

5. 다음 주소로 요청하여 회원 목록을 표시한다.
- http://localhost:8090/pro22/member/listMembers.do

[회원가입] 링크를 클릭한 후 회원 가입창을 열어 회원 정보를 추가하는 기능은 각자 구현해 보자.

* 실제 개발에서는 스프링의 JDBC 보다는 다음에 배울 마이바티스를 이용해서 데이터베이스와 연동한다. 이에 대해서는 다음에 알아보도록 하자.