데브코스 27일차 TIL

Heesu Song·2025년 4월 9일

데브코스 - 백엔드

목록 보기
29/32
post-thumbnail

하…JDBC 진짜 어렵네
이거를 직접 사용하신 백엔드 조상님들은 참 대단하신거 같다 ..ㅎ
connection이 자동문이었으면 얼마나 좋을까
알아서 문 좀 닫고 나가지ㅜ
그래도 MyBatis는 처음 해봤는데 생각보다 재밌는것 같다!
단짠단짠 수업 🤯 😊

그리고 오늘 또!! 인터넷 연결이 말썽이어서 몇번 들어왔다 나갔다.
다시 들어가면 강사님과 FT님만 계시는 대강당을 지나가야 되는데 그럴때마다 굉장히 머쓱,,,ㅎㅎ


JDBC

CREATE

@Override
    public Member save(Member member) throws SQLException {

        String sql = "insert into member (username, password) values (?, ?)";

        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {

            conn = getConnection();
            pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);

            pstmt.setString(1, member.getUsername());
            pstmt.setString(2, member.getPassword());

            pstmt.executeUpdate();

            rs = pstmt.getGeneratedKeys();

            if ( rs.next() ) {
                int idx = rs.getInt(1);
                member.setMemberId(idx);
            }

            return member;

        } catch ( SQLException e ) {
            throw e;
        } finally {
            closeConnection(conn, pstmt, rs);
        }

    }
  • Statement.RETURN_GENERATED_KEYS → 생성된 primary키를 받아올 수 있음

READ

@Override
    public Optional<Member> findById(Integer id) throws SQLException {

        String sql = "select * from member where member_id = ?";

        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {

            connection = getConnection();

            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setInt(1, id);

            resultSet = preparedStatement.executeQuery();

            if ( resultSet.next() ) {
                Member findMember = new Member(
                        resultSet.getInt("member_id"),
                        resultSet.getString("username"),
                        resultSet.getString("password")
                );
                return Optional.of(findMember);
            } else {
                return Optional.empty();
            }

        } catch ( SQLException e ) {
            throw e;
        } finally {
            closeConnection(connection, preparedStatement, resultSet);
        }

    }

UPDATE

    @Override
    public void update(Member member) {

        String sql = "update member set password = ? where member_id = ?";
        Connection conn = null;
        PreparedStatement pstmt = null;

        try {

            conn = getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, member.getPassword());
            pstmt.setInt(2, member.getMemberId());
            pstmt.executeUpdate();

        }catch (SQLException e){

        }finally {
            closeConnection(conn, pstmt, null);
        }

    }

트랜잭션 적용


Service에서 Rollback이 안되는 문제

Service에 @Transactional을 붙이는 이유

public void logic() throws Exception {
Member saveReq = new Member;
Member saved = repository.save(saveReq);
saved.setPassword("asdf");
// 만약 에러가 발생한다면?
repository.update(saved);
}
  • 트랜잭션은 커넥션 단위로 이루어진다.

  • save 와 update가 repository에서 커넥션 연결이 다르기 때문에, update가 실행되기 전에 에러가 발생된다면 한 로직 안에서 처리되어야 하는 update가 반영되지 않는 문제가 생김

  • 이럴 땐 service에서 트랜잭션을 관리할 수 있도록 설정해준다.

    public void logic1(Member saveReq, boolean isRollback) throws Exception {
        TransactionStatus transaction = transactionManager.getTransaction(
                new DefaultTransactionDefinition()
                //기본적인 설정의 트랜잭션을 만들어줌

        );
        //트랜잭션을 서비스에서 관리할 수 있도록

        try{

            Member saved = repository.save(saveReq);
            Optional<Member> memberOptional = repository.findById(saved.getMemberId());

            Member findMember = memberOptional.orElseThrow();

            log.info(findMember.getUsername());

            if(isRollback){
                transactionManager.rollback(transaction);
                return;
            }
            transactionManager.commit(transaction);

        }catch (Exception e){
            transactionManager.rollback(transaction);
        }

    }
  • transactionManager → 스프링이 제공하는 트랜잭션 관리 객체이고, DB와의 커넥션을 열고 닫고, 커밋하거나 롤백하는 일을 담당
  • getTransaction(new DefaultTransactionDefinition())new DefaultTransactionDefinition() 을 통해 트랜잭션의 동작방식을 정의하고 트랜잭션을 시작함

MyBatis

SQL Mapper

  • 데이터베이스를 쉽게 다룰 수 있도록 도와주는오픈 소스 ORM(Object-Relational Mapping) 프레임워크
  • MyBatis는 SQL을 xml에 편리하게 작성할 수 있고, xml 내에서 동적 쿼리를 매우 편리하게 작성할 수 있다.

사용 방법 순서

의존성 설정 -> DB 설정 -> MyBatis 설정 -> Mapper 인터페이스 작성 -> XML 작성 -> MyBatis 사용

Mapper Interface 작성


@Mapper
public interface ItemMapper {

    void save(Items items);
    void update(@Param("price") Integer price, @Param("id") Long id);
    Items findByItemCode(@Param("itemCode") String itemCode);
    void remove(Items items);

}

Mapper XML 설정


mapper → 연결할 Mapper파일의 경로

<insert>
<select>
<update>
<delete>
  • 태그안에 쿼리문을 작성해주면 됨

INSERT

<insert>

→ id안에 mapper의 메서드를 넣어줘야함

→ 해당 메서드가 실행될 때 태그안에 있는 쿼리문이 실행

void save(String name, String itemCode, Integer price);
<insert id="save" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO items (name, code, price)
        values (#{name}, #{itemCode}, #{price})
</insert>

UPDATE

<update id="update">
        UPDATE items
        SET price = #{price}
        WHERE item_id = #{id}
</update>

SELECT에서 생기는 문제

<select>

  • 객체의 이름과 테이블 칼럼명이 다를때

첫번째 커서가 가리키고 있는 이름을 객체의 이름과 맞춰주면 된다

<select id="findByItem">
        SELECT i.item_id as id,
               i.name,
               i.code as itemCode,
               i.price,
               i.created_at as createdAt
        FROM items i
        WHERE i.code = #{itemCode}
</select>
  • 반환타입을 알 수 없음

type을 찾을 수 있는 파일의 경로를 지정

<select id="findByItem" resultType="io.shs0160.dao.global.entity.Items">
        SELECT i.item_id as id,
               i.name,
               i.code as itemCode,
               i.price,
               i.created_at as createdAt
        FROM items i
        WHERE i.code = #{itemCode}
</select>

DELETE

<delete id="remove">
        DELETE FROM items i
        WHERE
            i.item_id = #{id}
</delete>

application.yml에 MyBatis 설정

mybatis:

  • mapper-locations: → mapper.xml의 위치
  • type-aliases-package → xml안에 있는 타입들을 찾을 수 있는 경로 지정

Builder 패턴


복잡한 객체의 생성 과정과 표현 방법을 분리하여 다양한 구성의 인스턴스를 만드는 생성 패턴이다. 생성자에 들어갈 매개 변수를 메서드로 하나하나 받아들이고 마지막에 통합 빌드해서 객체를 생성하는 방식


Builder Class 구현

  1. 먼저 Builder 클래스를 만들고 필드 멤버 구성을 만들고자 하는 Student 클래스 멤버 구성과 똑같이 구성한다.
  2. 그리고 각 맴버에 대한 Setter 메서드를 구현해준다.
  • this 는 Builder 객체 자신을 말함
  • 빌더 객체 자신을 리턴함으로써 메서드 호출 후 연속적으로 빌더 메서드들을 체이닝(Chaining) 하여 호출할 수 있게 된다. ex)new Builder().id(값).name(값)
  1. 마지막으로 빌더의 목표였던 최종 Student 객체를 만들어주는 build 메서드를 구성
    public static class Builder {
        private String name;
        private Integer age;
        private String email;
        private String address;
        public Builder name(String name) {
            this.name = name;
            return this;
        }
        public Builder age(Integer age) {
            this.age = age;
            return this;
        }
        public Builder email(String email) {
            this.email = email;
            return this;
        }
        public Builder address(String address) {
            this.address = address;
            return this;
        }
    }
profile
Abong_log

0개의 댓글