본문 바로가기
Blog/TIL

[240522] 드디어 JPA를 접하다

by 코젼 2024. 5. 22.
728x90
반응형

 

🔶분할정복을 이용한 거듭제곱

🔶데이터 접근 기술 (테스트, MyBatis, JPA)


 

목차

    / 오늘의 TIL /


    분할정복을 이용한 거듭제곱

    지수를 1/2씩 분할하면서 재귀함수를 통해 값을 구한다.

    모든 경우의 수를 확인하지 않아도 되는 장점이 있다. (메모리 초과 방지)

     

    https://www.acmicpc.net/problem/1629

    이 문제를 풀 경우 두 가지의 수학적 지식이 필요하다.

    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.io.IOException;
    import java.util.StringTokenizer;
    
    public class Main {
    
        static long C;
    
        public static void main(String[] args) throws IOException {
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            StringTokenizer st = new StringTokenizer(br.readLine());
    
            long A = Long.parseLong(st.nextToken());
            long B = Long.parseLong(st.nextToken());
            C = Long.parseLong(st.nextToken());
    
            System.out.println(pow(A, B));
        }
    
        private static long pow(long a, long exponent) {
            //지수가 1인 경우 a값 그대로 리턴
            if (exponent == 1) {
                return a % C;
            }
    
            //지수 분할정복(재귀 진행)
            long temp = pow(a, exponent/2);
    
            //지수가 홀수인 경우
            if (exponent % 2 == 1) {
                return ((temp * temp % C) * a) % C;
            }
    
            //지수가 짝수인 경우
            return temp * temp % C;
        }
    
    }

     

    지수법칙

    \(a^{n+m} = a^n \times a^m\)

     

    long temp = pow(a, exponent/2); //지수 반으로 나눔

    지수를 반으로 나눌 때, 홀수/짝수의 경우가 있다.

    • 홀수: \(a^9 = a^4 * a^4 * a\)
      • if (exponent % 2 == 1) return (temp * temp * a);
    • 짝수: \(a^8 = a^4 * a^4\)
      • return (temp * temp);

    모듈러 연산

    모듈러 성질

    \((a \times  b)\mod C = (a \mod C) \times (b \mod C) \mod C\)

     

    홀수/짝수 모듈러 연산

    • 홀수: if (exponent % 2 == 1) return ((temp * temp % C) * a) % C;
      (temp * temp * a) % C
      = (temp * temp % C) * (a % C) % C
      = (((temp * temp % C) % C) * (a % C % C)) % C
      			--> (a % C) < C 이므로, (a % C % C)는 (a % C)와 동일하다.
      = (((temp * temp % C) % C) * (a % C)) % C
      = ((temp * temp % C) * a) % C

     

    • 짝수: return (temp * temp % C);

    데이터 접근 기술

    테스트

    h2 데이터베이스인 경우, 테스트용 profile, datasource을 만들어서 따로 사용할 수 있다.

    - 테스트는 다른 테스트와 격리되어있어야 한다.
    - 테스트는 반복해서 실행 가능해야 한다.

    트랜잭션 - @Transactional

    테스트는 반복 테스트가 가능해야 하기 때문에 테스트 메서드 간에 의존성이 없도록 메서드 시작, 종료 시점에 각각 트랜잭션 시작과 롤백을 적용해야 한다.

    PlatformTranscationManager를 통해 transaction의 상태를 얻고(트랜잭션 시작), 롤백할 수 있다.

    스프링 부트는 자동으로 적절한 트랜잭션 매니저를 스프링 빈으로 등록해준다.

    PlatformTransactionManager transactionManager;
    TranscationStatus status;
    
    //트랜잭션 시작
    status = transactionManager.getTransaction(new DefaultTranscationDefinition());
    
    //트랜잭션 종료
    transcationManager.rollback(status);

     

    애플리케이션에서 @Transactional을 사용하면 트랜잭션을 시작하고 커밋하는 것을 제공한다.

    그러나, 테스트에서 @Transactional을 사용하면 트랜잭션을 시작하고 롤백한다.

    클래스, 메서드 별로 사용할 수 있다.

     

    테스트에서는 메서드가 종료된 후 롤백하기 때문에 커밋하고 싶은 경우 둘 중에 하나를 선택해서 사용하면 된다.

    @Commit
    @Rollback(false)

     

    임베디드 모드 DB

    h2 database는 JVM 내부에서 메모리 모드로 동작하는 임베디드 모드를 제공한다.

     

    데이터베이스에 접근하는 모든 설정 정보를 설정하지 않으면 기본적으로 임베디드 모드가 실행된다.

    (schema.sql 파일은 필요하다.)

    HikariDataSource에서 pool을 이용해서 connection을 꺼내는데, 기본적으로 "url=jdbc:h2:mem:testdb user=SA" 처럼 임베디드 모드가 설정되어 있는 것을 확인할 수 있다.

     

    애플리케이션에 내장해서 함께 실행되기 때문에, 애플리케이션 실행/종료 유무에 따라 메모리 db도 실행/종료된다.

     

    + h2 db가 연결되어있을 때 추가적으로 임베디드 모드 db를 설정하고 싶다면

    DataSource bean을 수동으로 등록해서 임베디드 모드로 동작할 수 있도록 설정하고, /test/resources 폴더 내에 schema.sql 파일을 만들어서 DDL(create table) SQL 구문을 작성한다.

    //임베디드 모드(메모리 모드)로 동작하는 h2 database
    dataSource.setUrl("jdbc:h2:mem:db;DB_CLOSE_DELAY=-1");
    dataSource.setUsername("sa");
    dataSource.setPassword("");

     


    MyBatis

    SQL Mapper 기술

    xml로 작성하고, 동적 쿼리를 편리하게 다룰 수 있는 장점이 있다.

    - xml이기 때문에, <. >, & 문자들은 xml 특수문자 처리를 해주어야 한다.

    - <where>태그 내부의 <if> 태그에 있는 test의 조건이 모두 실패하면, where를 만들지 않는다.

      - and가 먼저 시작되는 경우 and를 지운다. (trim을 사용해도 된다.)

     

    #{parameter} 문법은 ?를 넣고 파라미터를 바인딩하는 PreparedStatement를 사용한다.

     

    interface로 메서드를 선언해두고, @Mapper를 설정해둔다.

    xml의 위치는 @Mapper를 설정한 위치와 동일하게 resources 경로를 설정해야 한다. (properties를 통해 따로 설정 가능)

    @Mapper를 통해 exception을 모두 잡아주고, interface만으로도 구현할 수 있다. 

      -> 동적 Proxy 객체가 생성돼서, 생성된 동적 프록시 객체를 스프링 빈으로 등록한다.

    <mapper namespace=""> //Mapper 경로 위치
    
    //Identity Column있는 경우 작성
    <insert id="interface 메서드명" useGenerateKeys=true keyProperty="id">...</insert>
    
    //parameter 여러 개인 경우 객체에 접근해서 사용. 
    //@Mapper 설정한 인터페이스에서 @Param("")으로 데이터 받음.
    <update>
    	...
        price=#{updateParam.price}
    </update>
    
    //동적 쿼리 문법
    <where>
    	<if test="">...</if>
    </where>

     

    resutType으로 반환 타입을 명시한다.

    application.properties에 경로를 설정해두면 하위 클래스로 간단하게 작성할 수 있다.

    mybatis.type-aliases-package=hello.itemservice.domain

     

    중복되는 코드는 <sql>을 사용해서 코드를 재사용할 수 있다.

    <sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>
    <include refid="userColumns"><property name="alias" value="t1"/></include>

     

    <resultMap>을 사용해서 별칭(alias)를 사용하지 않고도 property, column을 사용해서 설정할 수 있다.

    <resultMap id="userResultMap" type="User">
    	<id property="id", column="user_id"/>
        <result property="username" column="username"/>
    </resutMap>
    
    <select id="selectUsers" resultMap="userResultMap">

    JPA

    Java Persistence API

    자바 진영의 ORM 기술 표준

    구현체로 대부분 Hibernate 사용

     

    - Java Collection과 유사한 사용(ex. 같은 인스턴스 객체 반환으로 == 검사 시 동일)

      -> 동일한 트랜잭션에서 조회한 엔티티는 같음을 보장해준다.

    - SQL 중심적인 개발에서 객체 중심으로 개발할 수 있다.

    - 자유롭게 객체 그래프를 탐색할 수 있다. (A <- B, C인 경우, A.getB(), A.getC() 가능)

     

    성능 최적화

    - 1차 캐시: 이미 실행한 SQL를 다시 호출할 경우, 1차 캐시를 통해 동일한 SQL을 다시 호출한다.

    - Buffer Writer 기능: transaction이 호출되고 JPA에 저장해두었다가 commit시 한 번에 전송한다.

    - 즉시 로딩: 기본적으로 지연 로딩을 사용하는데 지연 로딩은 객체 값을 getter, setter등 호출할 때마다 CRUD query를 보내는데, 즉시 로딩을 사용하면 같이 호출해야 하고 연관되어 있는 객체를 한꺼번에 로딩할 수 있다. (A <- B 인데 A를 호출하는 이유가 B를 사용하는 경우일 때  A, B 즉시 로딩)

     

    CRUD

    CUD를 진행할 때는 필수적으로 @Transactonal을 설정해주어야 한다.

    저장: jpa.persist(member)
    조회: Member member = jpa.find(memberId)
          Member member = jpa.find(MEmber.class, memberId) //class type, PK로 받기
    수정: member.setName("변경할 이름") //Collection처럼 값 변경을 할 수 있다. commit되는 시점에 update SQL을 실행한다.
    삭제: jpa.remove(member)

     

    EntityManager를 통해 db연결, entityManagerFacotry등 설정을 해주기 때문에 필수적으로 호출해서 사용한다.

    테이블 매핑할 때 @Entity를 사용해서 JPA가 관리하는 객체임을 인식시켜준다.

    JPA는 기본 생성자를 필수로 생성해주어야 한다.

    PK는 @Id, Identity Column을 위해 @GeneratedValue(strategy = GenerationType.IDENTITY)를 설정한다.

     

    entity를 대상으로 하는 jpql 문법: 동적 쿼리 문법 취약 (Querydsl로 문제 해결 가능)

    String jpql = "select i from Item i";

    ORM

    ORM(Object Relational Mapping) 기술 - 객체 관계 매핑

    객체는 객체대로 설계하고, 관계형 데이터베이스는 관계형 데이터베이스대로 설계한다.

    ORM 프레임워크가 중간에서 mapping 작업을 진행한다.

     

    자바에서는 객체를 주로 다루고, DB에서는 관계형 DB(RDBMS)를 주로 다룬다.

    RDBMS에서는 SQL 문법밖에 모르기 때문에, 자바에서 객체의 값을 CRUD를 통해 넘길 때, SQL 문법을 사용해야 한다.

    개발자가 SQL Mapper를 모두 진행해야 하는 번거로움이 발생한다.

     

    SQL 중심 개발의 문제점

    객체 CRUD 필드 의존성

    - 필드가 추가될 때마다 sql 구문에 필드를 모두 추가해주어야 한다.

    - A <- B 상속 받는 객체에 대해서 CRUD를 진행할 때도, A, B를 따로따로 분리해서 CRUD 진행해주어야 한다.

     

    패러다임의 불일치(객체 vs RDBMS)

    - 객체는 상속, 다형성, 추상화 등 다양한 기능을 제공하지만, db는 이러한 기능이 없다.

    - 객체 모델링을 할 수록 매핑 작업이 증가한다.

    FK가 있을 경우 객체 값을 getter로 하나씩 접근해서 CRUD를 진행해야 한다.

    member.getOrder().getDelivery();

    - 연관 관계 문제: 처음 실행하는 sql에 따라서 탐색 범위가 결정되기 때문에 모든 객체를 탐색할 수 없다.

        만약 A <- B, C가 있을 경우, sql이 A, B에 대해서 CRUD를 진행할 때 A, B에 대해서 join을 진행한다.

        A, B 테이블에 관한 정보를 알 수 있기 때문에 A, C가 연관되어있음에도 불구하고 탐색할 수 없다.

        C에 관해서 탐색하고 싶을 경우 A, C에 대해서 join을 추가적으로 진행해야 알 수 있다.

    - 엔티티 신뢰 문제: sql CRUD를 통해 받은 객체가 있을 경우 객체에 대한 어떤 연관 관계가 있는지, 어떤 sql이 실행되었는지 내부 로직을 모두 확인해야 한다. 물리으로는 가능하지만, 논리적인 계층 분할이 어렵다.


     

    728x90
    반응형

    'Blog > TIL' 카테고리의 다른 글

    [240526] API 개발하기  (0) 2024.05.26
    [240524] 수학 지식을 넓히자  (0) 2024.05.24
    [240523] 배워도 배워도 끝이 없네  (0) 2024.05.23
    [240521] JdbcTemplate 파헤쳐보기  (0) 2024.05.21
    [240520] 암기하지말고 이해하기  (0) 2024.05.20

    댓글