TIL(Today I Learned)

TIL(22일차)(2021.11.12)

keepgoing 2021. 11. 12. 14:22

Spring Framework의 장점📌

-> 객체지향 설계가 가능
-> 인터페이스를 지정하고 구현체를 사용자 임의대로 바꿀 수 있음.(DI 덕분에)
-> 즉, memberService는 memberRepository를 의존하고 있고, 구현체로 memoryMemberReposotory와 , jdbcMemberRepository가 있다고 가정했을 때
-> 사용자 임의대로 memoryMemberRepository를 삭제하고 jdbcMemberRepository로 구현할 수 있다.
-> 이는, SOLID에서 O(OCP, Open-Closed Principle) 즉, 개방-폐쇄의 원칙을 만족시킨다.
-> 확장에는 열려있고, 수정에는 닫혀있다. 왜냐하면 기존 코드는 손대지 않고 새로운 jdbcMemberRepository를 새로 구현한뒤에 추가할 때
-> memberRepository 인터페이스 구현체를 살짝만 수정해도 되기 때문이다.

JDBC(Java DataBase Connectivity) 📌

-> java에서 데이터베이스와 연결하기 위한 드라이버

    //build.gradle
    implementation 'org.springframework.boot:spring-boot-starter-jdbc'
    runtimeOnly 'com.h2database:h2'

-> DB와 연결하기위해 dependencies에 추가

//HelloSpringApplicationTests
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa

-> 경로 지정
-> buildGradle에 가서 Load Gradle Changes 클릭.

preparedStatement 📌

-> 객체를 캐시에 담아 재사용한다.
-> 반복적으로 사용한다면 statement에 비해 성능이 좋다.
-> secure coding에 나와있듯이 statement보다 보안성이 좋다.
-> statement는 sql 쿼리문을 작성할 때, 변수값을 사용하지만
-> preparedStatement는 변수값 대신 ?를 사용한다.

statement

   String sql = "INSERT into member(name) VALUES('"name"')";

preparedStatement

  String sql = "INSERT into member(name) VALUES(?)";

e.printStackTrace(); 📌

-> 에러의 발생근원지를 찾아서 단계별로 에러 출력한다.
-> 상세한 에러 내역을 확인할 수 있어서 유용하다.
-> log4에서는 사용할 수 없다.
-> 때문에 log.error("error:",e);로 선언해줘야한다.

e.printStackTrace(); 예시

try{
    if(conn !== null){
        close(conn);
    }
    catch(SQLException e) {
        e.printStackTrace();
    }
}

log4에서 e.printStackTrace(); 예시

try {
    //..
}
catch(Exception e){
    StackTraceElement[] elem = e.getStackTrace();
    for(int i = 0; i<elem.length; i++)
        logger.error(elem[i]);
}

spring integration Test 📌

-> spring + database + test를 한변에 통합해서 테스트
-> 단점 : 속도가 느리다.
-> 더 좋은 테스트 방식은 더 잘게 쪼개서 단위테스트를 하는것이 바람직함.
-> Configuration에 선언된 spring bean으로 구현한 spring container에 있는 객체들을 가져다가 쓴다.

@SpringBootTest Annotation

-> 스프링 컨테이너와 테스트를 함께 실행한다.

@Transactional Annotation

-> 테스트 시작 전에 트랜잭션을 시작하고(각각의 메소드마다), 테스트 완료 후에 항상 롤백한다.
-> 이렇게하면 DB에 데이터가 남지 않으므로, 다음 테스트에 영향을 주지 않는다.
-> AfterEach Annotation 구현과 비슷한 기능이다.
-> 트랜잭션을 통해 회원 중복 테스트 , 반복 테스트, 회원 join 테스트 등을 수행할 수 있다.
-> 트랜잭션이 서비스에 선언되면 롤백하지 않고 정상적으로 돌고, 테스트에 붙을 때만 롤백한다.

JdbcTemplate와 Mybatis 라이브러리 📌

-> JDBC API에서 사용되는(아주 오래된 방식의 코드) 반복 코드를 대부분 제거해준다.
-> 하지만 SQL은 직접 작성해야한다.
-> injection을 받을 수 없다. 즉, DI가 불가능하다.
-> DataSource(DB와 연결할 때 선언해주는..)를 통해 injection 문제를 해결
-> 손쉽게 DB와 연동할 수 있도록 구현되어 있다.

JdbcTemplate 선언

    //JdbcTemplateMemberRepository
    private final JdbcTemplate jdbcTemplate;

    public JdbcTemplateMemberRepository(DataSource dataSource) {
        jdbcTemplate = new JdbcTemplate(dataSource);
    }

스프링 빈 수정

//SpringConfig
@Bean
    public MemberRepository memberRepository(){
//        return new MemoryMemberRepository();
    return new JdbcTemplateMemberRepository(dataSource);
    }

-> return new JdbcTemplateMemberRepository(dataSource); 추가

DataSource

private final DataSource dataSource;
 public SpringConfig(DataSource dataSource) {
 this.dataSource = dataSource;
 }

-> DataSource를 통해 DB connection을 획득한다.
-> 스프링부트는 데이터베이스 커넥션 정보를 바탕으로 DataSource를 생성하고
-> 스프링 빈으로 만들어 DI를 받을 수 있게된다.

stream() 📌

-> 람다에서 사용하는 메서드.
-> '데이터의 흐름'
-> 배열 또는 컬렉션 인스턴스를 함수형으로 처리 가능
-> 람다를 사용하여 코드의 양을 줄이고 간결하게 표현할 수 있다.
-> 병렬처리 가능(multi-threading)
-> List로 반환할 수 있다.

RowMapper 📌

-> 원하는 형태의 결과값을 반환할 수 있다.
-> SELECT로 나온 여러개의 값을 반환할 수 있을 뿐만 아니라
-> 사용자가 원하는 형태로도 바꿀 수 있다.
-> 람다식으로 코드를 간결하게 만들 수 있다.
-> RowMapper는 과거 순수 JDBC에서 ResultSet으로 값을 받고
-> 객체에 담아서 반환하는 과정을
-> 간결하고 짧게 구현할 수 있게 해준다.
-> 즉, 쿼리날려준걸 맵핑해준다.

    //JdbcTemplateMemberRepository
    private RowMapper<Member> memberRowMapper(){
        return (rs, rowNum) -> {
            Member member = new Member();
            member.setId(rs.getLong("id"));
            member.setName(rs.getString("name"));
            return member;
        };
    }

JPA(Java Persistence API)📌

-> JdbcTemplate도 코드가 짧고 간결해졌지만, SQL문 쿼리를 직접 작성해야한다는 단점이있다.
-> 이를 JPA가 해결해준다.
-> JPA는 기존의 반복 코드도 실행해준다.
-> JPA를 사용하면 SQL과 데이터 중심의 설계에서, 객체 중심의 설계로 패러다임을 전환할 수 있다.
-> JPA를 사용하면 개발 생산성을 크게 향상시킬 수 있다.
-> 객체랑, ORM을 이용하는 인터페이스이다.(ORM: Object Relational Mapping)

    //build.gradle
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

-> dependencies에 추가
-> 입력후 Load Gradle Changes 클릭(라이브러리 다운로드)

    //application.properties
    spring.jpa.show-sql=true //jpa에서 sql문 확인 가능하게 해줌
    spring.jpa.hibernate.ddl-auto=none //none이 아닌 create로 선언하면 DB column 객체를 보고 자기가 테이블을 자동으로 생성함.
    //Member.java
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)

-> IDENTITY는 컬럼의 ID값을 자동으로 생성하고 증가 시키는것을 의미한다.
-> 본 구현에서는 자동으로 ID가 상승하도록(++sequence) or (h2database 성질) 설정해주었기 때문에 IDENTITY를 선언해준다.
-> DB가 알아서 생성해주는것을 IDENTITY라고 한다.

    //JpaMemberRepository
 private final EntityManager em;

    public JpaMemberRepository(EntityManager em) {
        this.em = em;
    }

-> 위에서 build.gradle에 선언한
-> implementation 'org.springframework.boot:spring-boot-starter-data-jpa'가 자동으로
-> EntityManager를 선언할 수 있게 해줌.
-> EntityManager를 통해 JPA를 컨트롤함
-> application.properties와 DB Connection에 들어있는 정보랑 섞어서 자동으로 EntityManager를 만들어준다.
-> EntityManager를 통해 DB와의 통신을 내부적으로 처리한다.
-> 결론 JPA를 사용하려면 EntityManager를 주입받아야한다.

@Transaction 
@Commit

-> 트랜잭션 어노테이션을 사용할 때, 모든 값을 롤백해주는데,
-> 이때, 롤백하기 싫다면, Commit 어노테이션을 사용하면 값이 정상적으로 저장된다.

    @Override
    public Member save(Member member) {
        em.persist(member);
        return member;
    }

-> jpa em을 이용한 선언 방식
-> persistence : 영속적
-> 즉, 영속적으로 save한다.라는 의미

    //JpaMemberRepository
    @Override
    public List<Member> findAll() {
        return em.createQuery("select m from Member m", Member.class)
                .getResultList();
    }

-> JPQL
-> 객체를 대상으로 쿼리를 날림

@Transactional
public class MemberService {

-> JPA 사용할 때는 @Transaction을 추가해줘야한다(JPA는 트랜잭션 안에서 실행되어야함).

    //springConfig.java
    private EntityManager em;

    @Autowired
    public SpringConfig(EntityManager em) {
        this.em = em;
    }

    return new JpaMemberRepository(em);

-> spring환경을 JPA로 돌아가도록 설정.
-> EntityManager를 선언해서 DI