본문 바로가기
Spring/이론

DB 연동(with Spring JDBC)

by 모스키토끼 2020. 3. 9.

1. Application Architecture

DAO와 DB 사이에 집중

- 서버와 클라이언트 사이에 TCP라는 프로토콜이 존재하여 커넥션을 만들어주어 통신을 가능하게 해 준다.

- 같은 개념으로 서버와 DB사이에도 커넥션을 만들어주어야만 통신이 가능하다.


2. Data Access Layer

각 Layer에 필요한 라이브러리에 집중

- 요구되는 라이브러리

  • JDBC Template (Spring에서 제공되는 클래스): spring-jdbc
  • DataSource (Apache): commons-dbcp
  • JDBC Driver: mysql (mySQL)-connector-java

- JDBC Template를 사용하려면 DataSource가 주입되어야 한다.

ex) A대학의 DataSource를 만들어서 JDBC Template에 주입하면 A대학 DB에 접근하게 된다.

 

- Maven을 사용하여 라이브러리를 다운로드한다.

ex)

<dependency>

    <groupId>org.apache.commons</groupId>

    <artifactId>commons-dbcp2</artifactId>

    <version>2.1.1</version>

</dependency>

 

Maven의 장점:

- 라이브러리와 라이브러리 사이에 의존성이 존재하는 경우 -> 의존된 라이브러리까지 한꺼번에 가져온다.

-> 라이브러리 관리가 편리(자동으로 원격 저장소에 있는 라이브러리를 로컬 저장소로 가져온다.)


3. Configuring Data Source

  • Database의 데이터로 작업을 하기 위해서는 Datasource로부터 Database의 커넥션을 얻어야 한다.
  • DataSource의 implementation 예시
    • BasicDataSource
    • PoolingDataSource
    • SingleConnectionDataSource
    • DriverManagerDataSource

Apache Common DBCP 라이브러리 안에 있는 BasicDataSource.class

-> Database 커넥션 풀을 제공해준다.

Thread 관리

그림 설명:

- Thread Pool이란?

  • 요청이 올 때마다 Thread를 만들고 작업이 끝나면 삭제하고를 반복하면 오버헤드 발생
  • Thread를 미리 만들어 놓고 request가 들어올 때마다 할당해주고 request가 끝나면 다시 Thread를 대기시킨다.
  • Thread를 만드는 것은 Stack 메모리를 사용하는 것 -> Thread를 미리 너무 많이 만들어 놓으면 메모리 낭비
    -> 적절한 양의 Thread만 생성해야 함

- Tomcat은 Thread Pool 방식으로 request가 들어올 때마다 각 request에 하나의 Thread를 할당해준다.

 

- DB 커넥션 풀(DB Connection Pool)이란?

  • Database Connection을 열고 닫고 할 때의 오버헤드를 줄이기 위해 사용
  • Thread pool처럼 미리 Connection들을 만들어 놓는다.
  • 만들어 놓은 Connections 보다 더 많은 request가 들어오면 maxWait만큼 BLOCK 시킨다.
  • Apache Common DBCP를 사용하면 Connection Pool 사용

 

XML 파일에 DataSource bean을 등록(설정)

  • root에 바로 값을 넣는 hard coding을 하지 말아야 한다.
  • properties 파일을 만들어 db 연결에 필요한 정보(username, password, DriverClassName, url...)를 관리한다.
  • properties는 이름을 마음대로 정할 수 있기 때문에 XML에 placeholder에 위치를 넣어주어야 적용된다.

예시)

//jdbc.properties
jdbc.username= kwon
jdbc.password= tiger
jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/testDB

//beans.xml
<context:property-placeholder
	location="패키지명/jdbc.properties"/>
    
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
	<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>

4. Data Access Object(DAO)

  • 관계형 데이터베이스에 액세스 하기 위한 객체 지향 API를 제공하는 객체
  • DAO는 Application Service 객체와 데이터베이스 간의 중개자 역할을 한다.(맨 위 그림 참고)

예시)

package ;

@Component("offersDao")
public class OfferDAO{
	private JdbcTemplate jdbcTemplateObject;
    
    @Autowired
    public void setDataSource(DataSource dataSource){
    	this.jdbcTemplateObject = new JdbcTemplate(dataSource);
    }
    
    public int getCount(){
    	return jdbcTemplate.queryForInt("select count(*) from testTable");
    }
}

- @Autowired로 dataSource를 DI(의존성 주입)하고 jdbcTemplateObject에 dataSource를 넣어 객체로 만든다.

- DAO는 jdbcTemplateObject를 사용하여 DB를 제공한다.


5. Spring JDBC Framework

  • Plain JDBC: 예외처리, database 커넥션 열고 닫기 등의 불필요한 코드를 작성해야 한다.
  • Spring JDBC: 모든 로우 레벨을 관리
    • 커넥션 열기
    • SQL문 준비하고 실행
    • 예외처리
    • 트랜잭션 처리
    • 커넥션 닫기

JDBC 데이터베이스 액세스의 기초를 형성하기 위한 접근 방식을 선택하기 위한 여러 옵션들 존재

  • JdbcTemplate
    - JDBC 프레임 워크의 중앙 클래스
    - 모든 데이터베이스 통신 및 예외 처리 관리
    • SQL 쿼리를 실행
    • ResultSet에 대한 반복 및 리턴된 매개 변수 값 추출
    • JDBC 예외를 포착하여 "org.springframework.dao"패키지에 정의된 보다 유익하고 일반적인 예외 계층 구조로 변환
  • NamedParameterJdbcTemplate
  • SimpleJdbcTemplate
  • SimpleJdbcInsert and SimpleJdbcCall
  • RDBMS Objects including MappingSqlQuery, SqlUpdate and StoredProcedure

예시)

package ;

import java.sql.ResultSet;

@Component("offersDao")
public class OfferDAO{
	private JdbcTemplate jdbcTemplateObject;
    
    @Autowired
    public void setDataSource(DataSource dataSource){
    	this.jdbcTemplateObject = new JdbcTemplate(dataSource);
    }
    
    public List<Offer> getOffers() {
    
    	return jdbcTemplateObject.query("select * from offers", new RowMapper<Offer>(){
        	public Offer mapRow(ResultSet rs, int rowNum) throws SQLException{
            	Offer offer = new Offer();
              	
                offer.setId(rs.getInt("id"));
                offer.setName(rs.getString("name"));
                
                return offer;
            }
        });
    }
}

- jdbc Template은 DB에서 레코드를 받아 DAO에 객체를 넘겨준다. -> 객체로 mapping 해주는 부분 필요

But!! Spring에서 mapping을 해줄 수 없다 -> mapping 부분은 개발자가 해야 된다.

-> mapping을 위한 인터페이스 필요

-> Spring에서 RowMapper라는 인터페이스를 정의해두었다.

(Spring은 mapping을 할 순 없지만 자신이 알아볼 수 있도록 인터페이스를 정의해둠)

 

- 인터페이스는 객체를 만들 수 없지만 인터페이스와 같은 이름을 사용하기 위해 익명 클래스 개념을 사용

-> 위 코드에서 new RowMapper 부분에서 RowMapper는 인터페이스 이름이지만 코드를 축약시키기 위해서 익명 클래스를 사용 (바로 mapRow를 재정의하여 사용) -> 어차피 한번 쓰고 안 쓸 클래스이기 때문에 정의할 필요성 x

참고) sql문으로 record가 여러 개 넘어오면 그만큼 mapRow메서드가 호출된다.

 

@Component

//이전까지 DI를 위한 Bean 설정
<bean id="offersDao" class="패키지명/OfferDAO">
	<property name="dataSource" ref="dataSource"/>
</bean>

//@Component를 사용하는 경우
//기존 bean 설정 코드를 지우고
<context:component-scan base-package="패키지명">
</comtext:component-scan>

- component scan 설정을 해주면 해당 package에 들어있는 모든 @Component가 달린 클래스들을 빈으로 등록하고 

기존 XML 설정 코드처럼 @Autowired가 달린 변수에 의존성 주입을 시켜준다.

 

JdbcTemplate class Usage

  • Querying for an integer
    String SQL = "select count(*) from Student";
    int rowCount = jdbcTemplateObject.queryForObject( SQL, Integer.class);
  • Querying for an String
    String SQL = "select name from Student where id =?";
    String name = jdbcTemplateObject.queryForObject(SQL, new Object []{10}, String.class);

    String SQL = "select name from Student where id =?";
    String name = jdbcTemplateObject.queryForObject(SQL, 10, String.class);
  • Querying and returning an object
String SQL = "select * from Student where id = ?"; 
Student student = jdbcTemplateObject.queryForObject(SQL, 
			new Object[]{10}, new StudentMapper()); 

public class StudentMapper implements RowMapper<Student> { 

    public Student mapRow(ResultSet rs, int rowNum) 
			throws SQLException { 
        Student student = new Student(); 

        student.setID(rs.getInt("id")); 
        student.setName(rs.getString("name")); 
        student.setAge(rs.getInt("age")); 

        return student; 
  • Querying and returning multiple objects
String SQL = "select * from Student"; 
List<Student> students = jdbcTemplateObject.query(SQL, 
				new StudentMapper()); 

public class StudentMapper implements RowMapper<Student> {
 
    public Student mapRow(ResultSet rs, int rowNum) 
			throws SQLException { 
        Student student = new Student(); 

        student.setID(rs.getInt("id")); 
        student.setName(rs.getString("name")); 
        student.setAge(rs.getInt("age")); 

        return student; 

 

  • Inserting a row into the table
    String SQL = "insert into Student (name, age) values (?,?)";
    jdbcTemplateObject.update( SQL, new Object []{"Zara", 11} );
  • Updating a row into the table
    String SQL = "update Student set name =? where id =?";
    jdbcTemplateObject.update( SQL, new Object []{"Zara", 10} );
  • Deleting a row from the table
    String SQL = "delete  from Student where id =?";
    jdbcTemplateObject.update( SQL, new Object []{20} );

 

References

- https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/

 

Spring Framework Documentation

Overview history, design philosophy, feedback, getting started. Core IoC Container, Events, Resources, i18n, Validation, Data Binding, Type Conversion, SpEL, AOP. Testing Mock Objects, TestContext Framework, Spring MVC Test, WebTestClient. Data Access Tran

docs.spring.io

- https://d2.naver.com/helloworld/5102792

 

'Spring > 이론' 카테고리의 다른 글

Spring Web Form  (0) 2020.03.25
MVC(Model-View-Controller)  (0) 2020.03.17
관점 지향 프로그래밍(AOP, Aspect Oriented Programming)  (0) 2020.03.07
의존성 주입(Dependency Injection)  (0) 2020.03.05
JSP(Java Server Pages)  (0) 2020.03.05

댓글