ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [SPRING] DB 사용을 위한 환경설정과 JDBC Template(service와 serviceImpl)
    SPRING 2021. 8. 26. 16:38

     

    *일단 jdbc 연동을 위해 라이브러리를 메이븐 방식으로 받는다.

    spring JDBC

     

     

    commons-dbcp

     

     

     

    Spring JDBC 사용과정

    1. DataSource 설정

     

    DB 연결을 위한 DB Server에 관한 정보(Property) 를 설정한다. (driver, url, username, password)

     

     

     

    1. <context:property-placeholder location="classpath:config/database.properties"/>

     : property file 의 위치를 지정하고, 반드시 필요한 parameter를 속성으로 지정한다. 그리고 datasource 를 bean으로 등록한다.

    2. bean 태그

     : id="dataSource"를 통해 나중에 Spring JDBC에 주입한다. 

     : 해당 property file에 있는 값을 placeholder를 통해 datasource의 속성으로 설정한 후 해당 BasicDataSource를 bean으로 등록한다.

     : spring JDBC 를 사용하기 위해 DB Connection 을 가져오는 DataSource를 spring IoC 컨테이너에 공유 가능한 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"
    	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-4.3.xsd">
    
    	<context:property-placeholder location="classpath:config/database.properties"/>
    	<context:component-scan base-package="com.springbook.biz"></context:component-scan>	
    	
    	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    		<property name="driverClassName" value="${jdbc.driver}"></property>
    		<property name="url" value="${jdbc.url}"/>
    		<property name="username" value="${jdbc.username}"/>
    		<property name="password" value="${jdbc.password}"/>
    		<property name="maxActive" value="8"/>
    		<property name="maxIdle" value="8"/>
    	</bean>
    	
        
    	<!-- @Component : DAO 클래스를 id를 jdbcTemplate 로 하는 bean으로 직접 등록 -->
    	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    		<!-- @Autowired : id가 dataSource(ref값) 인 bean을 찾아 DAO의 setDataSource에 주입 -->
    		<property name="dataSource" ref="dataSource"></property>
    	</bean>
    	
    </beans>

     

    1 : <context:property-placeholder location="classpath:config/database.properties"/> 스프링 프로퍼티 파일을 이용한 값을 설정하기. 설정정보를 XML로 분리해두면 빈 클래스나 의존관계 정보를 소스코드 수정 없이도 간단히 조작할 수 있다. 그리고 @Value를 효과적으로 사용할 수 있는데, 프로퍼티 파일의 내용을 참조하게 해주면 소스코드 수정 없이 @Value를 통해 프로퍼티에 주입되는 값을 변경할 수 있다.

    1-1 : 스프링 컨테이너는 초기화 작업 중에 datavase.properties 파일을 읽고, 각 key에 ${} 를 붙인 값과 동일한 value를 찾아서 프로퍼티 파일에 정해둔 값으로 바꿔 읽는다. 이러한 치환은 <context:property-placeholder> 태그에 의해 자동으로 등록되는 PropertyPlaceHolderConfiguer 빈이 담당한다.

    1-2 : 프로퍼티 치환자는 프로퍼티 파일의 key 값을 ${} 안에 넣어서 value 애트리뷰트에 넣어준다. 이러한 치환은 <context:property-placeholder> 태그에 의해 자동으로 등록되는 PropertyPlaceHolerCongifuer 빈이 담당한다.  (수동방식이여서 프로퍼티에 $가 없으면 name에 해당하는 value에 $ 가 그대로 들어가게된다.

    1-3 : 사용하기 위해 이클림스 namespaces에서 context 네임스페이스를 추가한다.

     

    2-1 : JDBC(java database connectivity) : db에 접근할 수 있도록 java에서 제공하는 api이다.

    2-2 : JDBC Template :  dao 객체에서 db와 연동하기 위해 sql 연산들을 수행할 수 있도록 도와주는 기술!

     - spring JDBC 접근 방법 중 하나이다.

     - spring 에서 제공하는 sql 연산들을 수행할 수 있도록 해주는 JDBC 코드용 기본 템플릿이다.

     -spring 은 jdbc를 이용하는 dao에서 사용할 수 있도록 다양한 템플릿과 콜백을 제공하는데 jdbc template는 그 중 하나이다.

    - JdbcTemplate 에는 update(), queryforInt(), queryForObject(), query() 가 있다.

    *update() : insert, delete, updqte 쿼리문을 실행할 때 사용한다.

    *queryForObject() : select 쿼리문을 실행했을 때, 하나의 객체(Object) 결과값이 나올 떄 사용하는 메소드이다. queryForObject 는 데이터형만 반환이 가능하다. 그래서 "select * from board1" 구문으로 board1 객체 자체를 반환 받기 위해 RowMapper 인터페이스가 필요하다.

     - RowMapper : 원하는 형태의 결과값 반환 가능 (순수 JDBC를 사용할 때 ResultSet과 비슷한 기능이다)

     (여기서는 List<BoardVO> 형태로 반환받기 위해  RowMapper를 사용해본다)

    * query() : 많은 결과값을 처리할 수 있는 메소드이다. (여기서는 List 형식을 활용하여 여러개의 로우 값을 저장시킨다)

    JdbcTemplate 객체를 생성하기 위해 DataSource를 생성자에 전달하면 된다.

    - spring sjbc에 대한 의존성을 추가해야한다. <version>값을 ${org.springframework-version}이라 작성하면 상단부에 있는 <org.springframework-version> 엘리먼트의 값인 x.x.x.release와 동일하게 적용 가능하다.

     

     

    <JDBC Template 사용방법>

     

    - vo 만들기. DB에 생성한 테이블의 컬럼들와 대응하도록 멤버변수를 갖고 getter/setter/toString 까지 만들어준다.

    package com.springbook.biz.board;
    
    import java.sql.Date;
    
    public class BoardVO {
    	private int seq;
    	private String title;
    	private String writer;
    	private String content;
    	private Date regdate;
    	private int cnt;
    	public int getSeq() {
    		return seq;
    	}
    	public void setSeq(int seq) {
    		this.seq = seq;
    	}
    	public String getTitle() {
    		return title;
    	}
    	public void setTitle(String title) {
    		this.title = title;
    	}
    	public String getWriter() {
    		return writer;
    	}
    	public void setWriter(String writer) {
    		this.writer = writer;
    	}
    	public String getContent() {
    		return content;
    	}
    	public void setContent(String content) {
    		this.content = content;
    	}
    	public Date getRegdate() {
    		return regdate;
    	}
    	public void setRegdate(Date regdate) {
    		this.regdate = regdate;
    	}
    	public int getCnt() {
    		return cnt;
    	}
    	public void setCnt(int cnt) {
    		this.cnt = cnt;
    	}
    	@Override
    	public String toString() {
    		return "BoardVO [seq=" + seq + ", title=" + title + ", writer=" + writer + ", content=" + content + ", regdate="
    				+ regdate + ", cnt=" + cnt + "]";
    	}
    }

     

    service 인터페이스 작성 : 메소드 정의하기

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package com.springbook.biz.board;
     
    import java.util.List;
     
    public interface BoardService {
        
        void insertBoard(BoardVO vo);
        void update(BoardVO vo);
        void delete(BoardVO vo);
        BoardVO get(BoardVO vo);
        List<BoardVO> getList(BoardVO vo);
    }
     
    cs

     

     

     

     

     

     DAO클래스에 JdbcTemplate 객체를 생성해야한다.

     

    package com.springbook.biz.board.impl;
    
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.stereotype.Repository;
    
    import com.springbook.biz.board.BoardVO;
    
    @Repository
    public class BoardDAOSpring {
    
    	@Autowired
    	private JdbcTemplate jdbcTemplate;
    	
    	private final String insert = "insert into board1(seq,title,writer,content) values((select nvl(max(seq),0)+1 from board1),?,?,?)";
    	private final String update = "update board1 set title=?,content=? where seq=?";
    	private final String delete = "delete board1 where seq=?";
    	private final String get = "select * from board1 where seq=?";
    	private final String list = "select * from board1 order by seq desc";
    	
    	//CRUD
    	
    	public void insertBoard(BoardVO vo) {
    		System.out.println("--> Spring JdBC로 insertBoard() 실행");
    		jdbcTemplate.update(insert,vo.getTitle(),vo.getWriter(),vo.getContent());
    	}
    	
    	public void updateBoard(BoardVO vo) {
    		System.out.println("--> Spring JDBC로 updateBoard() 실행");
    		jdbcTemplate.update(update,vo.getTitle(),vo.getContent(),vo.getSeq());
    	}
    	
    	public void deleteBoard(BoardVO vo) {
    		System.out.println("-->Spring JDBC로 deleteBoard() 실행");
    		jdbcTemplate.update(delete,vo.getSeq());
    	}
        
        
    	public List<BoardVO> boardList(BoardVO vo){
    		System.out.println("--> Spring JDBC로 boardList() 실행");		
    		return jdbcTemplate.query(list, new BoardRowMapper());	
    	}
        
    	public BoardVO get(BoardVO vo) {
    		System.out.println("-->Spring JDBC로 get() 실행");		
    		Object[] args = {vo.getSeq()};
    		return jdbcTemplate.queryForObject(get, args,new BoardRowMapper());
            //return jdbcTemplate.queryForObject(get, new BoardRowMapper(),vo.getSeq());
    	}
    	
    	
    	
    }

     

     

    serviceImpl 클래스 작성 : Impl 클래스는 각 인터페이스를 상속받아서 정의해두었던 메소드를 작성한다. DAO클래스에 JdbcTemplate 객체를 생성해야한다.

    package com.springbook.biz.board.impl;
    
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import com.springbook.biz.board.BoardService;
    import com.springbook.biz.board.BoardVO;
    
    @Service("boardService")
    public class BoardServiceImpl implements BoardService {
    	
    	@Autowired  
    	private BoardDAOSpring boardDaoSpring;
    	
    	@Override
    	public void insertBoard(BoardVO vo) {
    		boardDaoSpring.insertBoard(vo);
    	}
    	
    	@Override
    	public void update(BoardVO vo) {
    		boardDaoSpring.updateBoard(vo);
    	}
    
    	@Override
    	public List<BoardVO> getList(BoardVO vo) {
    		return boardDaoSpring.boardList(vo);
    	}
    
    	@Override
    	public void delete(BoardVO vo) {
    		boardDaoSpring.deleteBoard(vo);
    	}
    
    	@Override
    	public BoardVO get(BoardVO vo) {
    		return boardDaoSpring.get(vo);
    	}
    
    }

     

    package com.springbook.biz.board.impl;
    
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    import org.springframework.jdbc.core.RowMapper;
    
    import com.springbook.biz.board.BoardVO;
    
    public class BoardRowMapper implements RowMapper<BoardVO> {
    
    	@Override
    	public BoardVO mapRow(ResultSet rs, int rowNum) throws SQLException {
    		BoardVO boardvo = new BoardVO();
    		
    		boardvo.setTitle(rs.getString("title"));
    		boardvo.setContent(rs.getString("content"));
    		boardvo.setWriter(rs.getString("writer"));
    		boardvo.setCnt(rs.getInt("cnt"));
    		boardvo.setRegdate(rs.getDate("regdate"));
    		boardvo.setSeq(rs.getInt("seq"));
    		
    		return boardvo;
    	}
    
    }

    이렇게 BoardRowMapper를 만들면

    ResultSet에 값을 담아와서 BoardVO 객체에 저장하고 그것을 rowNum만큼 반복한다는 뜻이다.

     

     

    실행

    package com.springbook.biz.board;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Scanner;
    
    import org.springframework.context.support.AbstractApplicationContext;
    import org.springframework.context.support.GenericXmlApplicationContext;
    
    public class BoardServiceClient {
    
    	public static void main(String[] args) {
    		
    		AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
    		BoardService bs = (BoardService) factory.getBean("boardService");
    		BoardVO vo = new BoardVO();
    		Scanner sc = new Scanner(System.in);
    		Boolean test = true;
    		List<BoardVO> list = new ArrayList<>();
    		
    		while(test) {
    			System.out.println("1.글추가 2.전체글조회 3.특정글조회 4.글정보수정 5.글삭제 6.프로그램종료");
    			System.out.println("메뉴선택 >>");
    			int num = sc.nextInt();
    			
    			switch(num) {
    				case 1 :
    					System.out.println("등록할 글 정보를 입력하시오. \n글제목 : ");
    					vo.setTitle(sc.next());
    					System.out.println("작성자 : ");
    					vo.setWriter(sc.next());
    					System.out.println("글내용 : ");
    					vo.setContent(sc.next());
    					
    					bs.insertBoard(vo);
    					
    					continue;
    				case 2:
    					
    					list = bs.getList(vo);
    					for(int i = 0 ; i<list.size();i++) {
    						System.out.println(list.get(i).toString());
    					}					
    					continue;
    				case 3:
    					System.out.println("특정번호 입력");
    					vo.setSeq(sc.nextInt());
    					
    					System.out.println(bs.get(vo).toString());
    					
    					continue;
    				case 4:
    					System.out.println("업데이트 원하는 번호 입력");
    					vo.setSeq(sc.nextInt());
    					
    					System.out.println("수정 타이틀 입력");
    					vo.setTitle(sc.next());
    					
    					System.out.println("수정 내용 입력");
    					vo.setContent(sc.next());
    					
    					bs.update(vo);
    					
    					list = bs.getList(vo);
    					for(int i = 0 ; i<list.size();i++) {
    						System.out.println(list.get(i).toString());
    					}
    					
    					continue;
    				case 5:
    					System.out.println("지우고싶은 번호 입력");
    					vo.setSeq(sc.nextInt());
    					
    					bs.delete(vo);
    					
    					continue;
    				case 6:
    					System.out.println("프로그램 종료");
    					test = false;
    					factory.close();
    					break;					
    			}
    		}
    	}
    }

     

     

     

     

    ----

    여기서 service에 대해 interface를 만들고 이에 대해 serviceImpl를 구현한 이유는

    1. OOP의 인터페이스와 Loose coupling 

    interface는 객체의 사용 방법을 정의한 타입이다. 사용하는 객체를 개발코드를 수정하지 않고 변경할 수 있어서, 객체의 교환성을 높여주기 때문에 다형성을 구현하는 매우 중요한 역할을 한다. 

    interface 타입에 어떤 구현 객체를 대입하는지에 따라 실행결과가 달라지도록 만드는 기술이므로 기존 구현 객체(ServiceImpl1)와 비즈니스 로직이 다른 기능을 추가해야할 경우 다른 구현 객체(ServiceImpl2)를 만들어 사용하면 된다. 따라서 유지보수 측면에서 매우 좋다.

    그리고 객체지향 프로그래밍에서는 클래스를 작게 나누어 다른 클래스와 의존관계를 낮추는 것이 중요하다. 즉, 결합도를 낮추는 것인데 이 또한 유지보수가 쉽다는 장점을 갖는다.

     

    2. AOP

    AOP와 트랜잭션은 서비스 인터페이스에서 처리한다.

    spring에서 AOP를 구현할 때 JDK의 기본 프록시를 사용하는데, 이 프록시는 인터페이스 기반으로 동작하기 때믄에 Service 인터페이스를 만들어 사용한다.

     

     

    궁금함을 해소시켜준 https://velog.io/@aquarius1997/Service%EC%99%80-ServiceImpl 님 감사합니다.

     

     


     

     

     

    728x90

    'SPRING' 카테고리의 다른 글

    [SPRING] 03SEP21_2 / @annotation  (0) 2021.09.03
    [SPRING]MVC 프레임워크(01SEP21 )  (0) 2021.09.02
    [SPRING] pointcut 표현식  (0) 2021.08.25
    [SPRING] AOP 실습  (0) 2021.08.25
    [SPRING]어노테이션 기반 AOP 설정하기 (.)  (0) 2021.08.25
Designed by Tistory.