DTO
- Data Transfer Object의 약자
- 계층간 데이터 교환을 위한 자바빈즈
- 여기서의 계층이란 컨트롤러 뷰, 비지니스 계층, 퍼시스턴스 계층을 의미
- 일반적으로 DTO는 로직을 가지고 있지 않고, 순수한 데이터 객체
- 데이터를 한꺼번에 들고 다닐 수 있도록 만들어짐
- 필드와 getter, setter를 가진다. 추가적으로 toString(), equals(), hashCode()등의 Object 메소드를 오버라이딩 할 수 있습니다.
-
public class ActorDTO { private Long id; private String firstName; private String lastName; public String getFirstName() { return this.firstName; } public String getLastName() { return this.lastName; } public Long getId() { return this.id; } // ...... }
-
DAO
- DAO란 Data Access Object의 약자
- 데이터를 조회하거나 조작하는 기능을 전담하도록 만든 객체
- 딱 그 일만 수행하도록 만든 객체
- 보통 데이터베이스를 조작하는 기능을 전담하는 목적으로 이용
ConnectionPool
- DB연결은 비용이 많이 듭니다.
- 프로그램이 DBMS에 접속하는 시간이 조금 오래 걸리거나, 자원소모가 심한 경우
- 커넥션 풀은 미리 커넥션을 여러 개 맺어 둡니다.
- 커넥션이 필요하면 커넥션 풀에게 빌려서 사용한 후 반납합니다.
- Client 1, Client 2가 ConnectionPool로부터 Connection 객체를 얻어서 사용
- 그 다음엔 Client 1, Client 3이 다시 Connection을 얻어서 사용
- Client 1이 Connection을 종료하면 사용한 Connection을 반납
- 마지막으로 Client 3이 Connection 종료하여 반납됨
- 이렇게 Connection을 되도록 빨리 사용하고 반납해야 함
- 커넥션을 반납하지 않으면 어떻게 될까요?
- ConnectionPool에서 사용가능한 Connection이 없어서
- 프로그램이 늦어지거나, 심할 경우 장애를 발생시킴
DataSource
- DataSource는 커넥션 풀을 관리하는 목적으로 사용되는 객체입니다.
- DataSource를 이용해 Connection을 얻어오고 반납하는 등의 작업을 수행합니다.
Spring JDBC로 DAO 작성해보기
DB 접속
- ApplicationConfig 클래스
- @Import
- DBConfig 클래스
- @EnableTransactionManagement
- DataSource
- DataSourceTest 실행 클래스
- ApplicationContext - AnnotationConfigApplicationContext
- DataSource(프로그램 속 Component들), Connection
- DTO 클래스
- row 한 건에 대한 정보, column들을 필드와 getter, setter로 표현
- DaoSqls 클래스
- 필요한 SQL문을 상수 형태로 담는 곳
- DAO 클래스
- @Repository
- NamedParameterJdbcTemplate(dataSource) 객체
- DaoSqls 클래스 속 SQL문을 각각 하나의 메소드로 생성
- ApplicationConfig.java 수정
- @ComponentScan(basePackages = {"패키지명"}) 추가
- SelectAllTest.java로 테스트
- INSERT
- RoleSqls에 insert 문 추가
- new SimpleJdbcInsert(dataSource).withTableName("");
- insert용 메소드를 DAO 클래스에 작성
- UPDATE, (DELETE도 비슷)
- RoleSqls에 update 문 추가
- SqlParameterSource params = new BeanPropertySqlParameterSource(role);
- return jdbc.update(UPDATE, params);
- DELETE
- RoleSqls에 delete 문 추가
- Map<String, Integer> params = Collections.singletonMap("roleId", id); return jdbc.update(DELETE_BY_ROLE_ID, params);
- SELECT 한 건
- RoleSqls에 delete 문 추가
- Map<String, ?> params = Collections.singletonMap("roleId", id); return jdbc.queryForObject(SELECT_BY_ROLE_ID, params, rowMapper);
1. Maven Project 생성
archtype = maven - quickstart
artifact id = daoexam
2. 라이브러리 가져오기
pom.xml에 추가할 내용
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<!-- Spring Version -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.3.5.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Spring JDBC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- basic data source -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.1.1</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- 1.8버전으로 지정 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
3. ApplicationConfig 클래스 파일 생성
- 먼저 config 파일만 담을 패키지 생성
경로 : daoexam/src/main/java
패키지 이름 : kr.or.connect.daoexam.config
해당패키지 안에
ApplicationConfig라는 이름으로 클래스파일 생성
@Import
- 설정 파일을 여러 개로 나눠서 작성할 수 있음
- 이 설정 파일 하나에 모든 설정을 넣지 않고
- DB 연결과 관련된 설정만 따로 저장 => 그래야 유지보수하기 좋음
package kr.or.connect.daoexam.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration // 여기에 작성한 설정들을 읽어들일 수 있도록 선언
@Import({DBConfig.class})
public class ApplicationConfig {
}
4. DBConfig 클래스 생성
위의 파일과 같은 경로에, 이름은 DBConfig
package kr.or.connect.daoexam.config;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement // 트랜잭션을 위해 선언
public class DBConfig {
private String driverClassName = "com.mysql.jdbc.Driver";
private String url = "jdbc:mysql://localhost:3306/connectdb?useUnicode=true&characterEncoding=utf8";
private String username = "connectuser";
private String password = "connect123!@#";
//pom.xml에서 지정한 DataSource 객체 라이브러리를 이용하여
//DataSource를 생성하지 않고 가져오기 위한 설정하기 위해 Bean 등록
//등록할 때 메소드의 이름은 id로 지정한 이름과 일치하게 해줌
@Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
// dataSource 객체는 커넥션을 관리하기 때문에
// JDBC 드라이버, url, username, password 설정
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
4. DataSourceTest 실행 파일로 DB 접속 테스트
- main 클래스를 담을 패키지 생성
경로 : daoexam/src/main/java
패키지 이름 : kr.or.connect.daoexam.main
해당패키지 안에
DataSourceTest라는 이름으로 클래스 파일 생성
!! DataSource는 DBConfig 안에 있지만, DBConfig를 ApplicationConfig가 import하기 때문에
AnnotationConfigApplicationContext으로 ApplicationConfig 클래스를 넣어줌
package kr.or.connect.daoexam.main;
import java.sql.Connection;
import javax.sql.DataSource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import kr.or.connect.daoexam.config.ApplicationConfig;
public class DataSourceTest {
public static void main(String[] args) {
// Spring 컨테이너가 Bean들을 생성하고 관리
// 그 일을 담당해줄 공장을 불러옴
ApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationConfig.class);
// 어노테이션을 정보를 읽어서 만들어줄 공장 생성
// Application Context는 IoC/DI 컨테이너
// DataSource라는 클래스 요청
DataSource ds = ac.getBean(DataSource.class);
Connection conn = null;
try {
conn = (Connection) ds.getConnection();
if(conn != null)
System.out.println("접속 성공");
}catch (Exception e) {
e.printStackTrace();
}finally {
if (conn!=null) {
try {
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
Spring JDBC로 DAO/DTO 객체 생성하기
SELECT_ALL
1. DTO 클래스 생성
- dto 클래스를 담을 패키지 생성
경로 : daoexam/src/main/java/kr.or.connect.daoexam
패키지 이름 : kr.or.connect.daoexam.dto
해당패키지 안에
Role이라는 이름으로 클래스 파일 생성
package kr.or.connect.daoexam.dto;
public class Role {
private int roleId;
private String description;
public int getRoleId() {
return roleId;
}
public void setRoleId(int roleId) {
this.roleId = roleId;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return "Role [roleId=" + roleId + ", description=" + description + "]";
}
}
2. 쿼리문을 담을 객체를 생성할 클래스 만들기
- 쿼리문을 담을 객체를 생성하는 클래스를 담을 패키지 생성
경로 : daoexam/src/main/java/kr.or.connect.daoexam
패키지 이름 : kr.or.connect.daoexam.dao
해당패키지 안에
RoleDaoSqls이라는 이름으로 클래스 파일 생성
여기에 쿼리문을 상수 형태로 넣어줌 -> final
package kr.or.connect.daoexam.dao;
public class RoleDaoSqls {
public static final String SELECT_ALL = "SELECT role_id, description FROM role order by role_id";
}
3. Role 객체(데이터)에 접근할 객체를 생성하는 DAO 클래스 만들기
방금 전 클래스와 같은 경로에 RoleDao 클래스 생성
@Repository
Spring 컨테이너가 이 클래스를 읽어서 사용하기 위해 Bean 등록을 해야함.
DAO 객체는 저장소의 역할을 한다는 것을 밝히기 위해 @Repository를 붙임
package kr.or.connect.daoexam.dao;
import javax.sql.DataSource;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;
import kr.or.connect.daoexam.dto.Role;
// SQL문을 가져오기 위한 import
// static으로 가져오면 그 객체 속 변수를 클래스 이름 없이 바로 사용 가능
import static kr.or.connect.daoexam.dao.RoleDaoSqls.*;
import java.util.Collections;
import java.util.List;
@Repository
public class RoleDao {
private NamedParameterJdbcTemplate jdbc;
private RowMapper<Role> rowMapeer = BeanPropertyRowMapper.newInstance(Role.class);
// BeanPropertyRowMapper 객체 : column 값을 자동으로 DTO에 넘겨줌
// 또한 java와 DBMS의 변수명 규칙인 camleCase와 under_bar를 자동으로 맞춰줌
public RoleDao(DataSource dataSource) {
this.jdbc = new NamedParameterJdbcTemplate(dataSource);
}
// 직접 SQL 관련 메소드를 생성
public List<Role> selectAll(){
// 1. RoleDaoSqls에서 적은 SQL인 SELECT_ALL
// 2. 비어있는 맵 객체
// 3. 이전에 만들어둔 rowMapper => select한 결과를 DTO에 저장하는 목적으로 사용
// query()의 결과가 여러 건이라면 내부적으로 반복하며 DTO를 생성하여 List에 담아줌
return jdbc.query(SELECT_ALL, Collections.emptyMap(), rowMapeer);
// sql문에 바인딩할 값이 있다면 바인딩하기 위한 내용
}
}
- select해올 때 NamedParameterJdbcTemplate의 메소드를 이용
- 이를 이용해 ?에 값을 바인딩하거나, 결과값을 가져옴
- 생성자에서 DataSource를 받아옴
- Spring 4.3부터 ComponentScan으로 객체를 찾았을 때 기본 생성자가 없다면 자동으로 객체를 주입해줌
- DBConfig에서 Bean으로 등록된 DataSource가 파라미터로 전달됨
- 이를 이용해 NamedParameterJdbcTemplate 객체 생성
- Spring 4.3부터 ComponentScan으로 객체를 찾았을 때 기본 생성자가 없다면 자동으로 객체를 주입해줌
4. ApplicationConfig에 컴포넌트를 읽어들일 방법 설정하기 -> @ComponentScan
자동으로 RoleDao에 Repository가 붙어있는 클래스를,
Bean으로 등록해준 것처럼 대함
ApplicationConfig.java 파일에 아래 코드 추가
// basePackages를 이용해 살펴 볼 패키지를 정확히 지정해주기
// basePackages를 쓰면 패키지를 여러 개 명시할 수 있음
@ComponentScan(basePackages = {"kr.or.connect.daoexam.dao"})
5. 동작 확인하기
kr.or.connect.daoexam.main 패키지에
SelectAllTest.java 클래스 만들어서 실행
package kr.or.connect.daoexam.main;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import kr.or.connect.daoexam.config.ApplicationConfig;
import kr.or.connect.daoexam.dao.RoleDao;
import kr.or.connect.daoexam.dto.Role;
public class SelectAllTest {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationConfig.class);
RoleDao roleDao = ac.getBean(RoleDao.class);
List<Role> list = roleDao.selectAll();
for(Role role: list) {
System.out.println(role);
}
}
}
INSERT
1. INSERT문을 실행하기 위해 SimpleJdbcInsert 추가하기
private SimpleJdbcInsert insertAction;
//~~
public RoleDao(DataSource dataSource) {
//~~
this.insertAction = new SimpleJdbcInsert(dataSource).withTableName("role");
// DB 속 접근할 테이블 이름 명시
}
RoleDao.java
@Repository
public class RoleDao {
private NamedParameterJdbcTemplate jdbc;
// 추가
private SimpleJdbcInsert insertAction;
private RowMapper<Role> rowMapeer = BeanPropertyRowMapper.newInstance(Role.class);
public RoleDao(DataSource dataSource) {
this.jdbc = new NamedParameterJdbcTemplate(dataSource);
//추가
this.insertAction = new SimpleJdbcInsert(dataSource).withTableName("role");
// DB 속 접근할 테이블 이름 명시
}
2. 추가할 값 만들기
Way 1) 개발자가 직접 primary key를 세세히 지정하는 방법
아래 코드를 RoleDao.java에 추가
// primary key 값도 함께 직접 넣어줌
public int insert(Role role) {
SqlParameterSource params = new BeanPropertySqlParameterSource(role);
//role 객체를 받아서 맵으로 바꿔줌
return insertAction.execute(params); // execute 메소드만 쓰면 값이 알아서 저장됨
}
main 패키지에 JdbcTest.java 파일을 생성하여 아래 코드를 넣은 후 실행
package kr.or.connect.daoexam.main;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import kr.or.connect.daoexam.config.ApplicationConfig;
import kr.or.connect.daoexam.dao.RoleDao;
import kr.or.connect.daoexam.dto.Role;
public class JdbcTest {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(ApplicationConfig.class);
RoleDao roleDao = ac.getBean(RoleDao.class);
Role role = new Role();
role.setRoleId(500);
role.setDescription("CEO");
int count = roleDao.insert(role);
System.out.println(count+"건 입력하였습니다.");
}
}
그 다음에 SelectAllTest.java를 실행해보면
방금 넣은 값도 함께 출력됨
UPDATE
Way 2) 자동으로 primary key 생성하도록 만들기
RoleDaoSqls.java에 추가
:description과 :roleId가 값으로 바인딩될 부분
public static final String UPDATE
= "UPDATE role SET description = :description WHERE ROLE_ID = :roleId";
RoleDao.java에 추가
public int update(Role role) {
SqlParameterSource params = new BeanPropertySqlParameterSource(role);
// 1. sql문 - static으로 import했기에 클래스 이름 명시 안해도 됨
// 2. 인자로 받아온 DTO 속 값을 맵으로 변환시킬 사용할 객체
return jdbc.update(UPDATE, params);
}
JdbcTest.java에 수정 후 실행
Role role = new Role();
role.setRoleId(500);
role.setDescription("PROGRAMMER");
//int count = roleDao.insert(role);
//System.out.println(count+"건 입력하였습니다.");
int count = roleDao.update(role);
System.out.println(count + "건 수정하였습니다.");
그 다음에 SelectAllTest.java를 실행해보면
방금 수정한 값도 함께 출력됨
DELETE
RoleDaoSqls.java에 추가
public static final String DELETE_BY_ROLE_ID = "DELETE FROM role WHERE role_id = :roleId";
RoleDao.java에 추가
public int deleteById(Integer id) {
// Delete 경우 값이 딱 하나만 들어오기 때문에 간단하게 객체 하나만 받아줄 Map을 사용
Map<String, Integer> params = Collections.singletonMap("roleId", id);
return jdbc.update(DELETE_BY_ROLE_ID, params);
}
SELECT - 한 개
RoleDaoSqls.java에 추가
public static final String SELECT_BY_ROLE_ID
= "SELECT role_id, description FROM role where role_id = :roleId";
RoleDao.java에 추가
public Role selectById(Integer id) {
try {
Map<String, ?> params = Collections.singletonMap("roleId", id);
return jdbc.queryForObject(SELECT_BY_ROLE_ID, params, rowMapper);
}
catch (EmptyResultDataAccessException e) {
return null;
}
}
JdbcTest.java에 추가 후 실행
101을 조회
500을 삭제하고 조회하면 null 값이 출력
Role resultRole = roleDao.selectById(101);
System.out.println(resultRole);
int deleteCount = roleDao.deleteById(500);
System.out.println(deleteCount + "건 삭제했습니다.");
Role resultRole500 = roleDao.selectById(500);
System.out.println(resultRole500);
'Web > Java+Spring' 카테고리의 다른 글
Spring MVC - Controller 작성 실습 1 (0) | 2021.06.07 |
---|---|
Spring MVC (0) | 2021.06.04 |
Spring JDBC (0) | 2021.06.02 |
JAVA Config (0) | 2021.06.02 |
Spring IoC/DI 컨테이너 (0) | 2021.06.01 |