TIL - 0715

java-ims

객체지향적으로 프로그래밍하기

  • 상태값을 꺼내어 외부에서 작업하지말고, 메세지로 요청을 날리자
    • path를 ControllerPool에 전달해서 ControllerPool이 원하는대로 작업을 하는게 아니라 path를 가진 Request에 요청할 수 있게 Request 객체만 전달해서 요청할 수 있게하기
  • 매번 의식하는데 이런 사소한 부분에서 계속해서 객체지향적인 코딩을 하지못하네…. 계속 의식하면서 하기
/* 수정 전 */
public class ControllerPool<T extends Controller> {
    
    .
    .
    .
    
    public T search(String requestPath) {
      .
      .
      .
    }
}



/* 수정 후 */
public class ControllerPool<T extends Controller> {
    
    .
    .
    .
    
    public T search(Request request) {
        String searchUri = request.getFirstUri();
        
        .
        .
        .
        
        String excludeParams = request.getUriExcludeParams();
    }
}
  • 또다른 예제코드
    • user가 Level을 가지고 있고, Level에 요청을 해서 다음 레벨을 가져오면 될 것을 Level.SILVER 처럼 하드코딩으로 다음 레벨을 지정해주고 있음 - 다음 레벨이 신설되어SILVER가 아닐 경우는 어떻게? 그렇다면 Level 코드 뿐만 아니라 upgradeLevel 코드도 변경해야함(전혀 객체지향적이지않음 - 요청하자)
private void upgradeLevel(User user) {
    // user.setLevel(Level.SILVER); 가 아니라
    user.upgradeLevel(); // 얘로
}

public enum Level {
    BASIC(SILVER),
    SILVER(GOLD),
    GOLD(null);
   
    private final Level next;
    
    public boolean hasNext() {
        return Objects.nonNull(next);
    }
    
    public Level nextLevel() {
        return next;
    }
}

spring

트랜잭션 서비스 추상화로 알아보는 서비스 추상화 - 트랜잭션 알기

  • 트랜잭션 : 여러개의 작업을 하나의 작업 단위(더이상 쪼갤 수 없음 - 원자성)로 묶음
    • 하나로 묶인 작업을 처리하는 도중 에러가 발생한다면 실패로 처리하여 실행하던 작업으로 인해 발생한 변경 상태를 다시 처음으로 돌려버림(모두 성공하거나 모두 실패하거나)
  • JDBC 트랜잭션 처리를 하기위해서는 트랜잭션 경계를 설정(커넥션을 가져오고 - 커넥션을 닫음까지)해야함 : 경계 내 코드는 하나의 작업으로 묶임(로컬 트랜잭션 : 커넥션 안에서 만들어짐)
    • 로컬 트랜잭션을 생성하지않으면 각각이 독립적인 트랜잭션으로 실행 : 묶어서 처리해서 결과를 내어야하는데 개별적으로 처리하다보니 하나가 에러 발생해도 앞서 처리한 것은 그대로 반영(commit)
    • 트랜잭션 처리 끝에서 commit()하면 데이터베이스에 반영 : 이후 다른 계층에서 에러(서버 다운 포함)가 발생해도 이미 데이터베이스에는 반영되어있는 상태
  • JDBC 트랜잭션 경계 설정 코드
Connection conn = dataSource.getConnection();

conn.setAutoCommit(false);


try {
    PreparedStatement ps1 = conn.prepareStatement("...");
    
    PreparedStatement ps2 = conn.prepareStatement("...");
    .
    .
    .
    
    conn.commit();
} catch (Exception e) {
    c.rollback();
}

conn.close();
  • [갑자기 생각난 트랜잭션 어노테이션 위치가 서비스 계층인 이유] : 서비스 계층에 트랜잭션 코드를 둬야하는 이유
    • 데이터베이스 액세스(DAO or Repository)계층에 두었을 때 단순히 액세스 코드만 트랜잭션 처리만 될 뿐 액세스 하기 전 비즈니스 로직에 포함된 코드들은 트랜잭션 처리가 되지않음, 데이터베이스 액세스 계층은 데이터베이스 액세스 작업만 할 뿐임(계층 나눴으면 계층 나눈대로 똑바로 시켜야함)
    • 액세스 작업을 포함한 비즈니스 로직 자체가 트랜잭션 처리되어야함 : 로직을 거쳐 상태값이 변경되고, 변경된 상태값을 데이터베이스에 반영하는 것임
/* UserService.class */
@Transactional
public User update() {
    userRepository.update(user);
}

java

enum 활용의 장점

  • 고정값을 정의해둘 수 있음 : 값 중복 걱정은 안해도됨
  • boundary가 생김 : 정의한 enum 타입의 인스턴스만 입력 받을 수 있음, int와 같이 기본 타입을 사용할 경우 컴파일러가 체크를 하지못하다보니 코드 상에서 처리를 해줘야함
public boolean exam(Level level) {
    .
    .
    .
}

sql(use mysql)

데이터 갱신(삽입, 수정, 삭제)

  • 삽입할 때 NOT NULL이 아니라면 생략가능함 : Default 값이 INSERT 됨
    • VALUES 뒤로 “()”로 구분해서 한꺼번에 여러개의 데이터를 삽입할 수도 있음 : 쿼리문을 여러번 실행하지않고, 쿼리 1개로 처리해서 더 빨라질 수 있음
  • 테이블을 복사하고, 원본 테이블로부터 복사본 테이블로 INSERT 할 수도 있음
> INSERT INTO MEMBER (MEMBER_ID, NAME) VALUES (1, 'colin');
> INSERT INTO MEMBER (MEMBER_ID, NAME) VALUES (1, 'colin'), (2, 'jinbro'), (3, 'imjinbro');

> CREATE TABLE MEMBER_COPY LIKE MEMBER;
> INSERT INTO MEMBER_COPY SELECT * FROM MEMBER;
  • 수정할 때 조건절을 줘서 특정 row(행)만 변경되도록 할 수 있음, 그렇지않으면 모든 row에 대해 SET으로 설정한 COLUMN의 값이 변경됨(DEFAULT로 설정)
    • 여러 column(열)의 값을 한꺼번에 변경하려면 SET 뒤의 column명을 여러개 지정하면 되는데 구분은 “,”로 구분함
    • column의 기본값이 있을 때는 지정할 값에 DEFAULT 를 입력하면 기본값으로 수정됨
> UPDATE MEMBER SET NAME = 'imjinbro' WHERE MEMBER_ID = 4;
> UPDATE MEMBER SET NAME = 'imjinbro', TEAM_ID = 1 WHERE MEMBER_ID = 4;
> UPDATE MEMBER SET TEAM_ID = DEFAULT WHERE MEMBER_ID = 5;
  • 삭제할 때 조건을 달지않으면 모든 row가 삭제됨
> DELETE FROM MEMBER;
> DELECT FROM MEMBER WHERE MEMBER_ID = 5;