본문 바로가기

신세계 국비과정/신세계 - Spring 공부

14주차 배운점 - DTO, DAO, JDBC, Mapper

728x90
반응형

 

HttpServletRequest

 

HttpServletRequest는 HTTP 메시지의 형태로 들어오는 요청(Request)에 대한 정보를 파악하기 위해서 제공

 

 

HttpServletResponse


데이터에대한 응답에애한 페이지를 만드는 기능들이 셋업돼있는 객체
전달 기능은 없음!
브라우저로 전송하기 위해서 데이터를 만들어내는데 필요한 기능들을 제공

 

 

모델과 서비스 계층

 

컨트롤러에서는 화면에 필요한 데이터를 화면쪽으로 전달해주는데 이런 데이터들을 담당하는 객체를 모델(Model)이라고 함


JSP로 전달된 모델은 EL등을 이용해서 처리
JSP에서는 EL과 JSTL을 이용

 

 

3-tier 구조

 

표현 계층: 실제 화면 처리를 담당하는 객체들로 이루어진 계층으로 앱의 화면이나 GUI화면의 화면들, 웹 MVC등이 이에 해당


서비스(비즈니스) 계층: 고객의 요구사항을 반영하고 각 기능들에 대한 API를 제공하는 계층. 모든 기능에 대한 입력과 출력을 정의한 객체들로 구성


영속 계층: 오직 데이터의 관리만을 목적으로 하며 데이터베이스나 네트워크, 파일 처리등의 연결을 담당하는 객체들로 구성

 

 

계층의 데이터 전달을 위한 DTO

 

Data Transfer Object의 약어
여러 개의 데이터를 묶어서 하나의 단위 객체로 구성하기 위해서 주로 사용
서비스나 컨트롤러의 경우 오랜 시간 유지되는데 비해 DTO는 아주 짧은 생명주기
(쓰고버리고 쓰고버리고..)
DTO의 구성 규칙 – Java Beans 규칙 준수:
최소한의 규칙
생성자가 없거나 반드시 파라미터가 없는 생성자 함수를 가지는 형태
속성(멤버 변수)은 private으로 작성
getter/setter를 제공할 것

 

 

서비스 객체

 

 

실제 필요한 기능의 구현은 서비스 객체를 구성해서 처리
서비스 객체는 POJO(Plain Old Java Object)로 구성
특별한 API에 종속적이지 않도록 구성
Web MVC 구조에서는 여러 개의 컨트롤러들이 하나의 서비스 객체를 사용하는 방식

 

 

todo 서비스에 요청함 . 등록, 수정, 삭제,리스트 ... 기능을 가지고 있다

 

컨트롤러에서 모델 처리하기

 

 

목록 화면의 흐름

 

 

JSP


${} EL(Expressing Language)의 표현식


EL을 이용하여 getter/setter 를 호출할 수 있다.

 

<ul>
    <c:forEach var="num" begin="1" end="10">
        <li>${num}</li>
    </c:forEach>

    <c:if test="${list.size()%2 == 0}">
        짝수
    </c:if>
    <c:if test="${list.size()%2 != 0}">
        홀수
    </c:if>
    <c:choose>
        <c:when test="${list.size() %2 ==0}">
            짝수
        </c:when>
        <c:otherwise>
            홀수
        </c:otherwise>
    </c:choose>
</ul>

 

 

JDBC

 

Java Database Connectivity 자바에서 데이터베이스에 접속할 수 있도록 하는 자바 API

 

마리아디비 설치

https://mariadb.com/kb/en/mariadb-10515-release-notes/

 

 

신규 디비만들기

 

 

 

 

디비연결, Test connection

 

 

 

 

console에서 확인

 

 

 

 

Test

 

 

@Test
    public void test1(){
        int v1 = 10;
        int v2 = 20;

        Assertions.assertEquals(v1,v2);
    }

 

 

 

 

db 연결 테스트

 

@Test
    public void testConnection() throws Exception{
        Class.forName("org.mariadb.jdbc.Driver");

        Connection conn = DriverManager.getConnection("jdbc:mariadb://localhost:3307/webdb","webuser","webuser");
        Assertions.assertNotNull(conn);
        conn.close();
    }

 

 

Class.forName("org.mariadb.jdbc.Driver");
코드를 사용하지 않고도 드라이버가 자동으로 로드되고, 데이터베이스에 연결할 수 있다.
Assertions.assertNotNull() 메서드를 사용하여 연결 객체가 null이 아닌지 확인
Class.forName( ): JDBC드라이버 클래스를 메모리 상으로 로딩 하는 역할을 합니다. 이 때 문자열은 패키지명과
클래스명의 대소문자까지 정확히 일치해야 합니다. 만일 JDBC 드라이버 파일이 없는 경우에는 이 부분에서 예외가
발생하게 됩니다.
Connection connection: java.sql의 Connection 인터페이스 타입의 변수입니다. Connection은 데이터베이스와 연결이
정상적으로 이루어졌을때만 생성됩니다.
DriverManager.getConnection( ): 데이터베이스내에 있는 여러 정보들을 통해서 특정한 데이터베이스(예제에서는
webdb)에 연결을 시도합니다.
‘jdbc:mariadb://localhost:3306/webdb’ 는 jdbc프로토콜이라는 것을 이용한다는 의미이고, localhost:3306은
네트워크 연결 정보를, webdb는 연결하려는 데이터베이스 정보를 의미합니다.
webuser: 연결을 위해서는 사용자의 계정과 패스워드가 필요합니다.
Assertions.assertNotNull( ): 데이터베이스와 정상적으로 연결이 된다면 Connection 타입의 객체는 null이 아니라는
것을 확신한다는 의미입니다.
connection.close( ): 데이터베이스와의 연결을 종료합니다. JDBC 프로그램은 데이터베이스와 연결을 잠깐 씩 맺고
종료하는 방식으로 처리됩니다. 따라서 반드시 작업이 완료되면 데이터베이스와의 연결을 종료 해주어야만 합니다.

 

 

 

의존성 추가


마리아디비 사용

 

dependencies {
    compileOnly('javax.servlet:javax.servlet-api:4.0.1')

    testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
    testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
    // https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client
    implementation 'org.mariadb.jdbc:mariadb-java-client:3.0.4'
    implementation group: 'jstl', name: 'jstl', version: '1.2'


}

 

 

테이블 생성

 

 

Connection Pool과 DataSource

 

네트워크를 통한 Connection의 연결은 많은 시간과 리소스의가 필요함
미리 커넥션을 연결해두고 보관하면서 필요할 때마다 사용 후 반반하는
개념의 Connection Pooling
• javax.sql.DataSource는 Connection Pool을 인터페이스로 정리
• Connection Pool 라이브러리:
DBCP
C3PO
HikariCP

 

DAO(Data Access Object)

 

• 데이터베이스와의 연동을 전문적으로 처리하는 객체
• 비지니스 로직과 무관하도록 구성하고 데이터베이스에 원하는 작업을 처리하는
메소드들로 구성
• 파라미터나 리턴타입은 DTO혹은 VO
• VO(Value Object) – 순수한 데이터 자체를 의미하는 객체로 주로 getter로만 구성하는 읽기 전용 객체

 

Lombok 라이브러리

 

코드 작성시 자주 사용하는 코드들을 컴파일 시점에 생성해 주는 라이브러리
• getter/setter/생성자/toString( )/equals( )...
• 어노테이션을 이용
• 프로젝트의 라이브러리로 추가되어야 하고, 개발 도구에서 지원 필요

 

VO

 

@Getter
@Builder  //TodoVO.builder().build()로 객체 생성
@ToString
public class TodoVO {
    private Long tno;
    private String title;
    private LocalDate dueDate;
    private boolean finished;
}

 

 

커넥션 풀 테스트

 

    @Test
    public void testHikariCP() throws Exception{
        HikariConfig config = new HikariConfig();
        config.setDriverClassName("org.mariadb.jdbc.Driver");
        config.setJdbcUrl("jdbc:mariadb://localhost:3307/webdb");
        config.setUsername("webuser");
        config.setPassword("webuser");
        config.addDataSourceProperty("cachePrepStmts","true");
        config.addDataSourceProperty("prepStmtCasheSize","250");
        config.addDataSourceProperty("prepStmtCacheSqlLimit","2048");

        HikariDataSource ds = new HikariDataSource(config);
        Connection connection = ds.getConnection();
        connection.close();
    }

 

 

ConnectionUtil Enum

 

커넥션 만듦 , 싱글톤임

 

public enum ConnectionUtil {
    INSTANCE;

    private HikariDataSource ds;

    ConnectionUtil(){
        HikariConfig config = new HikariConfig();
        config.setDriverClassName("org.mariadb.jdbc.Driver");
        config.setJdbcUrl("jdbc:mariadb://localhost:3307/webdb");
        config.setUsername("webuser");
        config.setPassword("webuser");
        config.addDataSourceProperty("cachePrepStmts","true");
        config.addDataSourceProperty("prepStmtCacheSize","250");
        config.addDataSourceProperty("prepStmtCacheSqlLimit","2048");

        ds = new HikariDataSource(config);
    }
    public Connection getConnection() throws Exception{
        return ds.getConnection(); // Connection.INSTANCE.getConnection();
    }
}

 

 

setDriverClassName: JDBC 드라이버 클래스 이름을 설정. 여기서는 MariaDB JDBC 드라이버를 사용하고 있으므로 org.mariadb.jdbc.Driver를 설정.
setJdbcUrl: 데이터베이스에 연결하기 위한 JDBC URL을 설정. 여기서는 MariaDB의 URL을 설정.
setUsername 및 setPassword: 데이터베이스에 연결하기 위한 사용자 이름과 암호를 설정.
addDataSourceProperty: 데이터베이스 연결에 필요한 추가 속성을 설정. 여기서는 PreparedStatement 캐시 관련 속성을 설정. 이는 성능 향상을 위한 설정.
PreparedStatement 캐시를 활성화하고, 캐시의 크기를 250으로, 캐시할 SQL 문의 제한을 2048바이트로 설정

 

TodoDAO

 

ConnectionUtil 사용

 

public class TodoDAO {
    //데이터베이스 커넥션 풀에서 Connection을 받아서 시간 'select now()' 쿼리 결과를 담아서 리턴하는 getTime() 메소드 작성

    String now = null;

    public String getTime() {

        try ( // 변수 자동으로 close // connection, prepardStatement, ResultSet 클로저블 인터페이스가 구현된 애들만 들어감
            Connection conn = ConnectionUtil.INSTANCE.getConnection();

            PreparedStatement pstmt = conn.prepareStatement("select now()");
            ResultSet rs = pstmt.executeQuery();
        )  {
            rs.next();
            now = rs.getString(1);

        }catch (Exception e){
            e.printStackTrace();
        }
        return now;

    }
}

 

@Cleanup 롬복 사용

 

close( )처리 코드를 자동으로 컴파일 시점에 완성
• 별도의 try~ 등의 코드가 필요하지 않으므로 간결한 코드 완성
• Lombok 라이브러리에 종속적이라는 단점

 

public String getTime2() throws Exception{
        @Cleanup Connection connection = ConnectionUtil.INSTANCE.getConnection();
        @Cleanup PreparedStatement pstmt = connection.prepareStatement("select now()");
        @Cleanup ResultSet rs = pstmt.executeQuery();
        //finally 블록을 사용하지 않고도 자원을 자동으로 닫을 수 있다.
        rs.next();

        String now = rs.getString(1); // 첫번째꺼
        return now;
    }

 

자원 관리를 위해 finally 블록을 사용하지 않고도 자원을 자동으로 닫을 수 있다.

 

 

insert 메서드

 

public void insert(TodoVO todoVO) throws Exception {
        String sql = "insert into tbl_todo (title,dueDate,finished) values (?,?,?)";

        @Cleanup Connection connection = ConnectionUtil.INSTANCE.getConnection();
        @Cleanup PreparedStatement psmt = connection.prepareStatement(sql);

        psmt.setString(1, todoVO.getTitle());
        psmt.setDate(2, Date.valueOf(todoVO.getDueDate()));
        psmt.setBoolean(3, todoVO.isFinished());

        psmt.executeUpdate();
    }

 

 

test

 

@Test
    public void testInsert() throws Exception {
        TodoVO vo = TodoVO.builder().title("Sample_Todo_").dueDate(LocalDate.of(2022, 03, 06)).build();
        todoDAO.insert(vo);
    }

 

 

전체 리스트 출력

 

public List<TodoVO> selectAll() throws Exception{
        String sql = "select * from tbl_todo";
        
        @Cleanup Connection connection = ConnectionUtil.INSTANCE.getConnection();
        @Cleanup PreparedStatement psmt = connection.prepareStatement(sql);
        @Cleanup ResultSet rs = psmt.executeQuery();
        
        List<TodoVO> list = new ArrayList<>();
        
        while(rs.next()){
            TodoVO vo = TodoVO.builder().tno(rs.getLong("tno"))
                    .title(rs.getString("title"))
                    .dueDate(rs.getDate("dueDate").toLocalDate())
                    .finished(rs.getBoolean("finished")).build();
            list.add(vo);
        }
        return list;
    }

 

test

다출력해서 확인

 

@Test
    public void testList() throws Exception{
        List<TodoVO> list = todoDAO.selectAll();
        list.forEach(vo -> System.out.println(vo));
    }

 

 

select, delete, update

 

// SelectOne() tno 을 받아서 VO에 담아서 리턴하는 메소드
    public TodoVO selectOne(Long tno) throws Exception {

        TodoVO vo = null;

        @Cleanup Connection connection = ConnectionUtil.INSTANCE.getConnection();
        @Cleanup PreparedStatement psmt = connection.prepareStatement("select * from tbl_todo where tno = ?");
        psmt.setLong(1, tno);

        @Cleanup ResultSet rs = psmt.executeQuery();

        if (rs.next()) {
            vo = TodoVO.builder().tno(rs.getLong("tno"))
                    .title(rs.getString("title"))
                    .dueDate(rs.getDate("dueDate").toLocalDate())
                    .finished(rs.getBoolean("finished")).build();
        }


        return vo;
    }

    public void deleteOne(Long tno) throws Exception{
        @Cleanup Connection connection = ConnectionUtil.INSTANCE.getConnection();
        @Cleanup PreparedStatement psmt = connection.prepareStatement("delete from tbl_todo where tno = ?");
        psmt.setLong(1,tno);
        psmt.executeUpdate();

    }

    public void updateOne(TodoVO todoVO) throws Exception{

        String sql = "update tbl_todo set title= ?, duedate =?, finished =? where tno = ?";
        @Cleanup Connection connection = ConnectionUtil.INSTANCE.getConnection();
        @Cleanup PreparedStatement psmt = connection.prepareStatement(sql);

        psmt.setString(1,todoVO.getTitle());
        psmt.setDate(2, Date.valueOf(todoVO.getDueDate()));
        psmt.setBoolean(3, todoVO.isFinished());
        psmt.setLong(4,todoVO.getTno());

        psmt.executeUpdate();


    }

 

 

 

Mapper

 

 

비슷한 구조의 객체를 복제하는 작업에 유용
• DTO -> VO 혹은 VO -> DTO처리시 유용

database -> @entity 로 vo 연결 -> modelmapper로 dto 연결 -> dto를 서비스에서 활용 -> 서블릿에서 view로 http 보냄 -> vuejs 에서 해당 데이터를 활용해서 화면 출력

 

 

의존성 추가

 

implementation group: 'org.modelmapper', name: 'modelmapper', version: '3.0.0'

 

vo -> dto , dto -> vo 변환

 

 

TodoDTO

 

@Builder
@Data // 오버라이딩할 수있는 getter setter toString hashocode ~~  몽땅 세팅
@NoArgsConstructor
@AllArgsConstructor
public class TodoDTO {
    private Long tno;
    private String title;
    private LocalDate dueDate;
    private boolean finished;
}

 

 

MapperUtil enum

 

public enum MapperUtil { // 싱글톤
    INSTANCE;

    private ModelMapper modelMapper;
    //object mapping 서로 다른 클래스의 값을 한번에 복사하게 도와주는 라이브러리1

    MapperUtil(){
        this.modelMapper = new ModelMapper();
        this.modelMapper.getConfiguration()
                .setFieldMatchingEnabled(true)
                .setFieldAccessLevel(Configuration.AccessLevel.PRIVATE)
                .setMatchingStrategy(MatchingStrategies.STRICT);

    }

    public ModelMapper get(){
        return modelMapper;
    }
}

 

 

setFieldMatchingEnabled(true): 필드 매칭을 활성화. 이 설정을 사용하면 ModelMapper가 기본적으로 필드 이름을 사용하여 매핑을 시도.


setFieldAccessLevel(Configuration.AccessLevel.PRIVATE): 필드 접근 수준을 설정. 여기서는 PRIVATE로 설정되어 있으므로, private 필드에 대한 접근이 허용.


setMatchingStrategy(MatchingStrategies.STRICT): 매칭 전략 서정. STRICT 매칭 전략을 사용하면 ModelMapper가 매핑할 때 정확한 필드 및 타입 매칭을 요구한다.

 

반응형