Spring JDBC

tabi·2023년 6월 28일
0

Spring

목록 보기
11/15
post-thumbnail
  • 스프링으로 데이터베이스 연동을 해보자.
  • 프로젝트의 경우 myBatis를 이용하므로 애노테이션을 이용하면 편리하지만 JDBC에 대해서도 알아둘 필요가 있다.

1. 스프링의 데이터베이스 연동 지원

스프링은 JDBC를 이용한 DAO 클래스를 구현할 수 있도록 다양한 기능을 지원한다.

  • 템플릿 클래스를 통한 데이터 접근 지원
  • 의미있는 예외처리
  • 트랜젝션 처리
  1. DB 연동을 위한 템플릿 클래스
  • Connection 생성, PreparedStatement, ResultSet, Connection 자원 반환코드는 거의 모든 JDBC에서 중복되는 코드이다.
//반복해서 출현하는 코드 구조
Connection conn = null; 
...
try{
	conn = getConnection();
    ...
} finally {
	...
    if(conn != null) conn.close();
}
  • 템플릿 메서드 패턴과 전략 패턴을 사용하면 이렇게 반복되는 코드를 줄일 수 있다.
  • JDBC 관련 Template 클래스 4가지
    - JdbcTemplate : 기본적인 JDBC 템플릿 클래스로서 JDBC를 이용해 데이터에 대한 접근을 제공
    - NamedParameterJdbcTemplate : PreparedStatement에서 인덱스 기반의 파라미터가 아닌 이름을 가진 파라미터를 사용할 수 있도록 지원하는 템플릿 클래스
    - SimpleJdbcInsert

2. DataSource

  • 스프링에서 DB를 연결할 때 사용하는 빈 객체(스프링에서 DB 연결 시 무조건 데이터 소스를 사용한다.)
  • 스프링은 다음과 같이 세 가지 방식의 DataSource 설정을 지원한다.
    • 커넥션 풀을 이용한 DataSource 설정
    • JNDI를 이용한 DataSource 설정
    • DriveManager를 이용한 DataSource 설정(테스트 목적)

2-1. 커넥션 풀을 이용한 DataSource 설정

  • 스프링은 커넥션 풀 구현 클래스를 직접 제공하지 않기 때문에 c3p0과 같은 커넥션 풀 라이브러리를 이용해 커넥션 풀을 지원하는 DataSource를 설정할 수 있다.
  • pom.xml Maven 의존에 c3p0 라이브러리를 추가해준다.(우리는 hikari DBCP 이용하므로 c3p0라이브러리는 추가하지 않음)
  • pom.xml hikariCP 추가
<!-- https://mvnrepository.com/artifact/com.zaxxer/HikariCP p83 -->
<dependency>
	<groupId>com.zaxxer</groupId>
	<artifactId>HikariCP</artifactId>
	<version>5.0.1</version>
</dependency>
  • dependency 설정 후 c3p0가 제공하는 ComboPooledDataSource 클래스를 이용해 DataSource를 설정할 수 있다.
  • 설정 코드 예시(hikariDBCP 활용, root-context.xml)
	<!-- 스프링에서 DB 연결을 위해 필요한 Bean 객체 DataSource 를 hikariDBCP로 가져옴 -->
	<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
	  <!-- 	p102
	  <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property>
	  <property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:XE"></property>
	   -->
	   
	  <property name="driverClassName"	 value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"></property>
	  <property name="jdbcUrl" value="jdbc:log4jdbc:oracle:thin:@localhost:1521:xe"></property>
	  <property name="username" value="scott"></property>
	  <property name="password" value="tiger"></property> 
	</bean>
	
	<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
	  <constructor-arg ref="hikariConfig"></constructor-arg>
	</bean>
  • ComboPooledDataSource 클래스는 커넥션 풀을 관리하기 위한 다양한 프로퍼티를 제공한다.

root-context.xml, servlet-context.xml 의 차이

  • 공통점: Bean 객체를 생성한다.
  • 차이점
    • Application context라는 Bean을 만드는 공장 내에서 servlet-context는 프론트 컨트롤러에 의해 Bean 객체를 생성하는데, 스프링 컨테이너 안에서 생성한 Bean 객체는 각자의 컨테이너 안에서만 사용할 수 있다. 즉, DB 연동 작업과 같이 공통되는 기능을 주고 받을 수 없어서 컨트롤러가 필요할 때마다 매번 별도로 만들어야 한다.
    • servlet-context에서 서로 공유해서 만들고자 하는 스프링 컨테이너를 제작하고자 한다면 ContextLoaderListener 를 활용하면 된다.
    • root-context에다가는 Bean 객체를 생성하면 서로 공유할 수 있는 Bean 객체가 된다.
    • 즉, 프론트 컨트롤러를 여러개 사용해야 하는 경우 공유가 필요한 Bean 객체를 생성해야 한다면 root-context에 생성하는 것이 좋다.

2-2. 실습

  1. root-context.xml 수정
  • bean 추가
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
	xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
	
	<!-- Root Context: defines shared resources visible to all other web components -->
	<context:component-scan base-package="org.doit.ik"></context:component-scan>
	
	<!-- 스프링에서 DB 연결을 위해 필요한 Bean 객체 DataSource 를 hikariDBCP로 가져옴 -->
	<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
	  <!-- 	p102
	  <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property>
	  <property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:XE"></property>
	   -->
	   
	  <property name="driverClassName"	 value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"></property>
	  <property name="jdbcUrl" value="jdbc:log4jdbc:oracle:thin:@localhost:1521:xe"></property>
	  <property name="username" value="scott"></property>
	  <property name="password" value="tiger"></property> 
	</bean>
	
	<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
	  <constructor-arg ref="hikariConfig"></constructor-arg>
	</bean>
	
	<!-- p91 -->
	<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
	  <property name="dataSource" ref="dataSource"></property>
	</bean>
		
	<!-- p95 -->
	<mybatis-spring:scan base-package="org.doit.ik.mapper"/>
	
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
	<!-- id 명을 안 주면 class명에서 첫글자를 소문자로 바꾼 것과 동일하다 -->
      
<!-- 스프링은 dataSource로 DB를 연결한다. -->
<!-- <constructor-arg ref="dataSource"></constructor-arg> -->
<!-- 생성자를 통해 주입 -->
<!-- Impl(구현하는 클래스)에 객체가 만들어질 때 @AllArgsConstructor 를 통해 생성자가 만들어져 있기 때문에 constructor로 주입가능-->
<!-- JdbcTemplate이라는 클래스 안에 dataSource 하나를 매개변수로 받는 생성자가 없으면 오류난다. -->
		
<property name="dataSource" ref="dataSource"></property>
<!-- Impl(구현하는 클래스)에 객체가 만들어질 때 @Data에 의해 setter가 만들어져 있기 때문에 property로 주입가능 -->
	</bean>
</beans>
  1. NoticeDao.java 수정
  • 의존성 주입
	@Autowired
	private JdbcTemplate jdbcTemplate;
  • jdbcTemplate 이용해서 수정
package org.doit.ik.persistence;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import org.doit.ik.domain.NoticeVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class NoticeDao {
	
	@Autowired
	private JdbcTemplate jdbcTemplate;
	
	//검색한 결과의 총 레코드 수를 반환하는 메서드
	public int getCount(String field, String query) throws ClassNotFoundException, SQLException
	{
		String sql = "SELECT COUNT(*) CNT FROM NOTICES WHERE "+field+" LIKE ?";
		
		return this.jdbcTemplate.queryForObject(sql, Integer.class, "%" + query + "%");
	}
	
	public List<NoticeVO> getNotices(int page, String field, String query) throws ClassNotFoundException, SQLException
	{					
		
		int srow = 1 + (page-1)*15; // 1, 16, 31, 46, 61, ... an = a1 + (n-1)*d
		int erow = 15 + (page-1)*15; //15, 30, 45, 60, 75, ...
		
		String sql = "SELECT * FROM";
		sql += "(SELECT ROWNUM NUM, N.* FROM (SELECT * FROM NOTICES WHERE "+field+" LIKE ? ORDER BY REGDATE DESC) N)";
		sql += "WHERE NUM BETWEEN ? AND ?";
		
		//JdbcTemplate 사용
		List<NoticeVO> list = this.jdbcTemplate.query(
				sql //실행하고자 하는 쿼리를 첫번째 인자값으로 준다.
				, new Object[] {"%" + query + "%", srow, erow} //매개변수: LIKE 구문의 parameter값이기 때문에 앞 뒤로 붙여준 것
				// % 붙이기 싫으면 REGEX 쓰면 된다.
				, new BeanPropertyRowMapper<NoticeVO>(NoticeVO.class) //자동으로 Bean 객체에 Mapping
				//BeanPropertyRowMapper는 sql이 실행된 후의 결과값을 담는다.
				//우리는 DB의 테이블 컬럼명과 NoticeVO의 변수명을 맞추어놨기 때문에 따로 맵핑해줄 필요가 없이 자동으로 매핑 된다.
				);
		
		return list; //Arraylist 반환
	} //getNotices
	
	public int delete(String seq) throws ClassNotFoundException, SQLException
	{
		// 2. 데이터 베이스 연동을 위한 쿼리와 실행 코드 작성
		String sql = "DELETE NOTICES WHERE SEQ=?";
		
		return this.jdbcTemplate.update(sql, seq);
		//첫번째 매개변수: 쿼리, 두번째 매개변수: ?에 해당하는 parameter
	}
	
	public int update(NoticeVO notice) throws SQLException {
	    String sql = "UPDATE NOTICES SET TITLE=?, CONTENT=?, FILESRC=? WHERE SEQ=?";

	    return this.jdbcTemplate.update(
	    		sql
	    		, notice.getTitle()
	    		, notice.getContent()
	    		, notice.getFilesrc()
	    		, notice.getSeq());
	}
	
	public NoticeVO getNotice(String seq) throws ClassNotFoundException, SQLException
	{
		String sql = "SELECT * FROM NOTICES WHERE SEQ= ?";
		
		return this.jdbcTemplate.queryForObject(
				sql
				, new Object[]{seq}
				, new BeanPropertyRowMapper<NoticeVO>(NoticeVO.class)
				);
	}

	public int insert(NoticeVO notice) throws ClassNotFoundException, SQLException {
		// TODO Auto-generated method stub
		String sql = "INSERT INTO NOTICES(SEQ, TITLE, CONTENT, WRITER, REGDATE, HIT, FILESRC) VALUES( (SELECT MAX(TO_NUMBER(SEQ))+1 FROM NOTICES), ?, ?, ?, SYSDATE, 0, ?)";
		
		return this.jdbcTemplate.update(sql
				, notice.getTitle()
				, notice.getContent()
				, notice.getWriter()
				, notice.getFilesrc()
				);
	}
}

3. JdbcTemplate 클래스를 이용한 메서드

3-1. 조회를 위한 메서드 : query()

  • 게시글 목록을 가져올 때 사용 가능
  • 쿼리 실행 결과를 객체 목록으로 가져올 때 PreparedStatement용 SQL 쿼리와 RowMapper를 이용하는 query() 메서드를 이용하면 된다.
  • JdbcTemplate 클래스는 다음과 같은 query() 메서드를 제공한다.

  • 예시

3-2. 조회를 위한 메서드 : queryForList()

  • 한 개의 컬럼을 반환하는 함수
  • 쿼리 실행 결과로 읽어온 컬럼 개수가 한 개일 때 이 한 컬럼의 데이터 여러개를 SELECT 해 올 때 사용하는 queryForList()
  • elementType 파라미터는 조회할 데이터 타입을 지정할 때 사용
  • 만약 조회할 컬럼 타입이 String이라면 다음과 같이 조회하면 된다.

3-3. 조회를 위한 메서드 : queryForObject()

  • 한 개의 레코드를 반환하는 함수
  • queryForList() 대신 queryForObject()를 사용해도 무관하다.
  • 쿼리 실행 결과로 읽어온 행의 개수가 1개인 경우 이 실행 결과를 가져옴
  • 예시: 글 상세보기를 눌러 한개의 글을 조회해 올 때

3-4. 삽입, 수정, 삭제를 위한 메서드 : update()

  • INSERT, UPDATE, DELETE 쿼리를 실행할 때 사용하는 update()
  • 쿼리 실행 결과로 변경된 행의 개수를 return
  • 예시

4. NamedParameterJdbcTemplate 클래스를 이용한 JDBC 프로그래밍

  • JdbcTemplate과 동일한 기능을 제공
  • 인덱스 기반의 파라미터가 아니라 이름 기반의 파라미터를 설정할 수 있게 해준다.
  • JdbcTemplate과 동일한 이름의 메서드를 제공하지만 Map이나 SqlParameterSource를 전달받는다.
  • 기존의 쿼리에서 ?로 표시하던 파라미터 값들을 :이름 으로 표시한다.

4-1. Map을 이용한 파라미터 값 설정 메서드

  • Map 기반의 메서드는 Object 배열이 아닌 Map을 이용해 이름을 가진 파라미터 값을 설정한다.
  • 예시

4-2. SqlParameterSource를 이용한 파라미터 값 설정 메서드

  • Map 대신 SqlParameterSource 인터페이스를 이용하여 파라미터 값을 설정할 수도 있다.
  • 인터페이스를 사용하기 때문에 실제로 사용할 때에는 Impl.java(구현한 클래스)를 만들고 해당 클래스를 사용해 파라미터 값을 전달해주어야 한다.
  • SqlParameterSource를 인자로 전달받는 메서드 목록
  • 스프링이 제공하는 SqlParameterSource 구현 클래스
    • o.s.jdbc.core.namedparam.BeanPropertySqlParameterSource
    • o.s.jdbc.core.namedparam.MapSqlParameterSource
  • BeanPropertySqlParameterSource
    - 동일한 이름을 갖는 자바 객체의 프로퍼티 값을 이용해 파라미터 값 설정
  • MapSqlParameterSource
    - Map과 비슷하게 <이름, 값> 쌍을 이용해 파라미터 값 설정
    - MapSqlParameterSource 객체 생성 후 addValue()로 파라미터 이름과 값을 설정해준다.

5. 실습

5-1. 예제1

  1. NoticeDao.java 인터페이스로 만들고 기존 NoticeDao는 NoticeDaoImpl로 만들어주기
  • 인터페이스 생성
package org.doit.ik.persistence;

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

import org.doit.ik.domain.NoticeVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public interface NoticeDao {
	
	//검색한 결과의 총 레코드 수를 반환하는 메서드
	public int getCount(String field, String query) throws ClassNotFoundException, SQLException;

	//페이징+공지사항 목록
	public List<NoticeVO> getNotices(int page, String field, String query) throws ClassNotFoundException, SQLException;
	
	//공지사항 삭제
	public int delete(String seq) throws ClassNotFoundException, SQLException;
	
	//공지사항 수정
	public int update(NoticeVO notice) throws SQLException;
	 
	//공지사항 상세보기
	public NoticeVO getNotice(String seq) throws ClassNotFoundException, SQLException;

	//공지사항 작성
	public int insert(NoticeVO notice) throws ClassNotFoundException, SQLException;
	
}
  1. root-context.xml 코드 추가
  • 기존의 jdbcTemplate는 주석처리하고 namedParameterJdbcTemplate bean 추가
	<bean id = "namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
		<constructor-arg ref="dataSource"></constructor-arg>
	</bean>
  1. NoticeDaoImpl.java NamedParameterJdbcTemplate 활용해 내용 변경하기
  • NamedParameterJdbcTemplate 주입
	@Autowired
	private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
  • 코드 수정
package org.doit.ik.persistence;

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

import org.doit.ik.domain.NoticeVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.stereotype.Repository;

@Repository
public class NoticeDaoImpl implements NoticeDao{
	
	@Autowired
	private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
	
	//검색한 결과의 총 레코드 수를 반환하는 메서드
	public int getCount(String field, String query) throws ClassNotFoundException, SQLException
	{
		String sql = "SELECT COUNT(*) CNT FROM NOTICES WHERE "+field+" LIKE :query"; //이 콜론에 저장된 값을
		
		MapSqlParameterSource parameterSource = new MapSqlParameterSource();//? 파라미터를 사용할 Map객체 선언
		parameterSource.addValue("query", "%" + query + "%"); //여기에서도 사용한다.
		
		return this.namedParameterJdbcTemplate.queryForObject(sql, parameterSource, Integer.class);
	}
	
	public List<NoticeVO> getNotices(int page, String field, String query) throws ClassNotFoundException, SQLException
	{					
		
		int srow = 1 + (page-1)*15; // 1, 16, 31, 46, 61, ... an = a1 + (n-1)*d
		int erow = 15 + (page-1)*15; //15, 30, 45, 60, 75, ...
		
		String sql = "SELECT * FROM";
		sql += "(SELECT ROWNUM NUM, N.* FROM (SELECT * FROM NOTICES WHERE "+field+" LIKE :query ORDER BY REGDATE DESC) N)";
		sql += "WHERE NUM BETWEEN :srow AND :erow";
		
		Map<String, Object> paramMap = new HashMap<String, Object>();
		paramMap.put("query", "%" + query + "%");
		paramMap.put("srow", srow);
		paramMap.put("erow", erow);
		
		return this.namedParameterJdbcTemplate.query(sql, paramMap, new BeanPropertyRowMapper<NoticeVO>(NoticeVO.class));
				//첫번째 인자: 수행하고자 하는 sql
		
	} //getNotices
	
	//공지사항 삭제
	public int delete(String seq) throws ClassNotFoundException, SQLException
	{
		// 2. 데이터 베이스 연동을 위한 쿼리와 실행 코드 작성
		String sql = "DELETE NOTICES WHERE SEQ= :seq";
		
		MapSqlParameterSource parameterSource = new MapSqlParameterSource();
		parameterSource.addValue("seq", seq);
		
		return this.namedParameterJdbcTemplate.update(sql, parameterSource);
		
	}
	
	public int update(NoticeVO notice) throws SQLException {
	    String sql = "UPDATE NOTICES SET TITLE=:title, CONTENT=:content, FILESRC=:filesrc WHERE SEQ=:seq";

	  /*  MapSqlParameterSource parameterSource = new MapSqlParameterSource();
	    parameterSource.addValue("title", notice.getTitle());
	    parameterSource.addValue("content", notice.getContent());
	    parameterSource.addValue("filesrc", notice.getFilesrc());
	    parameterSource.addValue("seq", notice.getSeq()); */
	    //위처럼 할 수도 있지만 아래처럼 하는 것이 더 편리하다.
	    
	    //어차피 notiveVO 안에 변수명이 컬럼명과 동일하기 때문에 알아서 맵핑 되어 결과 가져옴!
	    SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(notice);
	    
	    return this.namedParameterJdbcTemplate.update(sql, parameterSource);
	}
	
	public NoticeVO getNotice(String seq) throws ClassNotFoundException, SQLException
	{
		String sql = "SELECT * FROM NOTICES WHERE SEQ= :seq";
		
		MapSqlParameterSource parameterSource = new MapSqlParameterSource();
		parameterSource.addValue("seq", seq);
		
		return this.namedParameterJdbcTemplate.queryForObject(sql, parameterSource, new BeanPropertyRowMapper<NoticeVO>(NoticeVO.class));
	}

	public int insert(NoticeVO notice) throws ClassNotFoundException, SQLException {
		String sql = "INSERT INTO NOTICES(SEQ, TITLE, CONTENT, WRITER, REGDATE, HIT, FILESRC) VALUES( (SELECT MAX(TO_NUMBER(SEQ))+1 FROM NOTICES), :title, :content, :writer, SYSDATE, 0, :filesrc)";
		
	    SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(notice);
	    
	    return this.namedParameterJdbcTemplate.update(sql, parameterSource);
	}
}
  1. MemberDao.java 인터페이스로 만들고 기존 MemberDao는 MemberDaoImpl로 만들어주기
  • 인터페이스 생성
package org.doit.ik.persistence;

import java.sql.SQLException;

import org.doit.ik.domain.MemberVO;

public interface MemberDao {
	public MemberVO getMember(String id) throws ClassNotFoundException, SQLException;
	
	public int insert(MemberVO member) throws ClassNotFoundException, SQLException;	
}
  1. MemberDaoImpl.java NamedParameterJdbcTemplate 활용해 내용 변경하기
  • NamedParameterJdbcTemplate 주입
@Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
  • 코드 수정
package org.doit.ik.persistence;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.doit.ik.domain.MemberVO;
import org.doit.ik.domain.NoticeVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.stereotype.Repository;

@Repository
@Log4j
public class MemberDaoImpl implements MemberDao {
	
	@Autowired
	private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
	
	public MemberVO getMember(String id) throws ClassNotFoundException, SQLException
	{
		String sql = "SELECT * FROM MEMBER WHERE \"ID\"= :id";
		
		MapSqlParameterSource parameterSource = new MapSqlParameterSource();
		parameterSource.addValue("id", id);
		
		return this.namedParameterJdbcTemplate.queryForObject(sql, parameterSource, new BeanPropertyRowMapper<MemberVO>(MemberVO.class));
		
	}
	
	public int insert(MemberVO member) throws ClassNotFoundException, SQLException
	{
		String sql = "INSERT INTO MEMBER(\"ID\", \"PWD\", \"NAME\", GENDER, BIRTH, IS_LUNAR, CPHONE, EMAIL, HABIT, REGDATE) VALUES( :id, :pwd, :name, :gender, :birth, :is_lunar, :cphone, :email, :habit, SYSDATE)";
	    
		SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(member);
	    
	    return this.namedParameterJdbcTemplate.update(sql, parameterSource);
	}
}//class

5-2. 예제2 - 회원가입 구현

  • org.doit.ik.controller 패키지 안에 JoinController만들어서 /joinus/join.htm, /joinus/login.htm 처리하기
  • 회원가입 정보를 입력하면
  • 이렇게 DB에 추가가 되어야 한다.
  1. HomeController.java 수정
package org.doit.ik;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import lombok.extern.log4j.Log4j;


@Controller
@Log4j
@RequestMapping("/")
public class HomeController {
		
		@RequestMapping("index.htm")
		public String home() throws Exception{
			return "index.jsp";
	}	
}
  1. 회원가입 GET, POST 처리
  • /joinus/login.htm + GET 방식 = 회원가입 jsp 페이지 응답
  • 회원정보 입력 후 /joinus/login.htm + POST 방식 = 회원가입 DB 연동
  1. join.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
		<title>index</title>
		<link href="join.css" type="text/css" rel="stylesheet" />
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.0/jquery.min.js"></script>
	</head>
	<body>
		<div id="header">
			<div class="top-wrapper">
				<h1 id="logo"><a href="../index.jsp"><img src="../images/logo.png" alt="뉴렉처" /></a></h1>
				<h2 class="hidden">메인메뉴</h2>
				<ul id="mainmenu" class="block_hlist">
					<li>
						<a href="">학습가이드</a>
					</li>
					<li>
						<a href="" >과정선택</a>
					</li>
					<li>
						<a href="" >인기과정</a>
					</li>
				</ul>
				<form id="searchform" action="" method="get">
					<fieldset>
						<legend class="hidden">
							과정검색폼
						</legend>
						<label for="query">과정검색</label>
						<input type="text" name="query" />
						<input type="submit" class="button" value="검색" />
					</fieldset>
				</form>
				<h3 class="hidden">로그인메뉴</h3>
				<ul id="loginmenu" class="block_hlist">
					<li>
						<a href="../index.jsp">HOME</a>
					</li>
					<li>
						<a href="../joinus/login.jsp">로그인</a>
					</li>
					<li>
						<a href="../joinus/join.jsp">회원가입</a>
					</li>
				</ul>
				<h3 class="hidden">회원메뉴</h3>
				<ul id="membermenu" class="clear">
					<li>
						<a href=""><img src="../images/menuMyPage.png" alt="마이페이지" /></a>
					</li>
					<li>
						<a href="../customer/notice.jsp"><img src="../images/menuCustomer.png" alt="고객센터" /></a>
					</li>
				</ul>
			</div>
		</div>
		<div id="visual" class="joinus">
			<div class="top-wrapper">

			</div>
		</div>
		<div id="main">
			<div class="top-wrapper clear">
				<div id="content">
					<form action="" method="post">
						<h2>회원가입</h2>
						<h3 class="hidden">방문페이지 로그</h3>
						<p id="breadscrumb" class="block_hlist clear"><img alt="Step1 개인정보 등록" src="images/step2.png" /></p>
						<h3 class="hidden">회원가입 폼</h3>
						<div id="join-form" class="join-form margin-large" >						
							<dl class="join-form-row">
								<dt class="join-form-title">
									아이디
								</dt>
								<dd class="join-form-data">
									<input type="text" name="id" />
									<input id="btnCheckid" class="button" type="button" value="중복확인" />
								</dd>
							</dl>
							<dl class="join-form-row">
								<dt class="join-form-title">
									비밀번호
								</dt>
								<dd class="join-form-data">
									<input type="password" name="pwd" />
								</dd>
							</dl>
							<dl class="join-form-row">
								<dt class="join-form-title">
									비밀번호 확인
								</dt>
								<dd class="join-form-data" >
									<input type="password" name="pwd2" />
								</dd>
							</dl>
							<dl class="join-form-row">
								<dt class="join-form-title">
									이름
								</dt>
								<dd class="join-form-data">
									<input type="text" name="name" />
								</dd>
							</dl>
							<dl class="join-form-row">
								<dt class="join-form-title">
									성별
								</dt>
								<dd class="join-form-data">
									<select name="gender">
										<option>남성</option>
										<option>여성</option>
									</select>
								</dd>
							</dl>
							<dl class="join-form-row birthday">
								<dt class="join-form-title">
									생년월일
								</dt>
								<dd class="join-form-data">								
	                                <span>
	                                    <input type="text" id="year" /><input type="text" id="month" /><input type="text" id="day" /><input type="hidden" name="birth" id="birth" />
	                                    <script>
	                                    //focus, blur 이벤트
	                                    $("#day").blur(function() {
											var year = $("#year").val();
											var month = $("#month").val();
											var day = $("#day").val();
											//이걸 birth에 저장
											var birth = year + "-" + month + "-" + day;
											$("#birth").val(birth);
										});
	                                    </script>
	                                </span>
	                                <span class="moon">
	                                    <input type="radio" name="is_lunar" value="Solar" id="IsSolar" checked />양력
	                                    <input type="radio" name="is_lunar" value="Lunar" id="IsLunar" />음력
	                                </span>
								</dd>
							</dl>
							<dl class="join-form-row">
								<dt class="join-form-title">
									핸드폰 번호
								</dt>
								<dd class="join-form-data">
									<input type="text" name="cphone" /><span>[대시(-)를 포함할 것: 예) 010-3456-2934]</span>
								</dd>
							</dl>
							<dl class="join-form-row">
								<dt class="join-form-title">
									이메일
								</dt>
								<dd class="join-form-data">
									<input type="text" name="email" />
								</dd>
							</dl>
							<dl class="join-form-row">
								<dt class="join-form-title">
									취미
								</dt>
								<dd class="join-form-data habit">
									<input type="checkbox" name="habit" id="music" value="music"/><label for="music">음악</label>
									<input type="checkbox" name="habit" id="movie"  value="movie" /><label for="movie">영화</label>
									<input type="checkbox" name="habit" id="trip"  value="trip" /><label for="trip">여행</label>
								</dd>
							</dl>						
						</div>
					<div id="buttonLine">
						<input class="btn-okay button" type="submit" value="가입" />
					</div>
					<input type="hidden" name="${ _csrf.parameterName }" value="${ _csrf.token }">
					</form>	

				</div>
				<div id="navi">
					<h2>회원가입</h2>
					<h3 class="hidden">회원메뉴</h3>
					<ul id="navi-menu">
						<li>
							<a href="">로그인</a>
						</li>
						<li>
							<a href="" class="current">회원가입</a>
						</li>
						<li>
							<a href="">아이디찾기</a>
						</li>
						<li>
							<a href="">비밀번호 재발급</a>
						</li>
					</ul>
                       
					<h3 id="fav-title">추천사이트</h3>
					<ul class="margin-small">
						<li>
							<a href="http://www.answeris.net"><img src="../images/answeris.png" alt="앤서이즈" /></a>
						</li>
						<li>
							<a href="http://www.microsoft.com"><img src="../images/microsoft.png" alt="마이크로소프트" /></a>
						</li>
						<li>
							<a href="http://www.w3c.org"><img src="../images/w3c.png" alt="W3C" /></a>
						</li>
					</ul>
				</div>
			</div>
		</div>
		<div id="footer">
			<div class="top-wrapper">
				<h2><img src="../images/footerLogo.png" alt="뉴렉처"/></h2>
				<p>				
					<address id="ad">
						사업자등록번호 : 000-00-00000000 통신판매업신고 : 서울 0000-000 관리자 : 홍길동
						<br/>
						주소 : 서울시 000구 001동 000-0 00빌딩 0층 전화 : 02-000-0000 팩스 : 02-000-0000
					</address>
				</p>				
				<p>
					Copyright ⓒ newlecture.com 2012-2012 All Right Reserved. Contact master@newlecture.com for more information
				</p>
			</div>
		</div>
	</body>
</html>
  1. JoinController.java 코드 수정
package org.doit.ik.controller;

import org.doit.ik.domain.MemberVO;
import org.doit.ik.persistence.MemberDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import lombok.NoArgsConstructor;

@Controller
@RequestMapping("/joinus/*")
@NoArgsConstructor //디폴트 생성자 생성
public class JoinController {
	@Autowired
	private MemberDao memberDao;
	
	@GetMapping("/join.htm")
	public String join() throws Exception{
		return "join.jsp";
	}
	
	@GetMapping("/login.htm")
	public String login() throws Exception{
		return "login.jsp";
	}
	
	//회원가입 후 DB에 입력 후 home으로 이동
	@PostMapping("/join.htm")
	public String join(MemberVO member) throws Exception{
		int insertcount = this.memberDao.insert(member);
		if(insertcount ==1) {
			return "redirect:../index.htm"; //redirect: == response.sendRedirect()
		} else
		return "join.jsp?error";
	}
  }//class
profile
개발 공부중

0개의 댓글