728x90

1. 영속성 컨텍스트

  • JPA에서 가장 중요한 것 두가지는
    1. 객체와 관계형 DB 매핑하기(정적인 느낌, 디비를 어케 설계하고 객체를 어케 설계해서 어케 매핑할거야?) 와
    2. 영속성 컨텍스트(실제 JPA가 나누어서 어떻게 동작하는지, 메커니즘) 이다.
  • 영속성 컨텍스트에 대해서 우선 알아보자.

1.1 JPA 주요 구성 요소

  • 엔티티 매니저(EntityManager):
    • JPA에서 데이터베이스 작업을 관리하는 주요 인터페이스
    • CRUD 연산을 수행하는 API를 제공
    • 엔티티의 생명 주기를 관리
    • EntityManagerFactory에 의해 생성
    • 각 EntityManager 인스턴스는 데이터베이스 세션과 직접 연결되어 독립적인 작업을 수행할 수 있다.
  • 엔티티:
    • 데이터베이스의 테이블에 해당하는 클래스
    • 각 엔티티 인스턴스는 테이블의 개별 레코드(행)에 해당
    • 이 클래스는 JPA 어노테이션을 사용하여 데이터베이스 테이블과 매핑
  • 영속성 컨텍스트(Persistence Context):
    • 엔티티를 영구 저장하는 환경
    • 엔티티의 생명주기와 상태를 관리
    • 데이터베이스와의 동기화를 담당
    • 트랜잭션 범위 내에서 엔티티를 저장하고 관
    1. 1차 캐시: 영속성 컨텍스트는 엔티티의 1차 캐시 역할을 수행하여, 한 트랜잭션 내에서 반복된 데이터베이스 호출을 최소화한다. -> 수정, 삭제 예제 커밋 부분
    2. 엔티티의 생명주기 관리: 엔티티의 상태(비영속, 영속, 삭제, 분리)를 관리한다.
    3. 변경 감지: 트랜잭션이 커밋될 때, 영속성 컨텍스트에 있는 엔티티들을 검사하여 변경된 엔티티를 자동으로 데이터베이스에 반영한다. -> 스냅샷과 비교해서 반영
    4. 지연 로딩: 엔티티와 그 연관된 객체들을 필요한 시점까지 로딩을 지연시켜 성능을 최적화한다.
  • 트랜잭션:
    • 데이터베이스 작업을 묶어주는 방법
    • 작업들이 모두 성공하거나 실패하게 보장한다.

1.2 Entity Manager Factory와 Entity Manager(엔티티 매니저)

  • 웹 어플리케이션 실행 -> 엔티티 매니저 팩토리 생성 -> 요청들에 따라 각각 엔티티 매니저 팩토리에서 엔티티 매니저를 생성 -> 각각의 매니저가 db 커넥션을 이용해 처리.

1.3 영속성 컨텍스트

  • 영속성 컨텍스트는 JPA를 이해하는데 가장 핵심이다.
  • 엔티티를 영구 저장하는 환경"이라는 의미
**EntityManager.persist("Entiy")**
  • persist 메서드를 호출하는 것은 객체를 db에 저장한다고 생각할 수 있지만 사실은 "엔티티를 영속화" 하는 것으로, db에 직접 저장하는 것이 아니라, "영속성 컨텍스트"에 저장하는 것이다.
  • 객체를 db에 저장하는구나? -> “엔티티를 영속화한다” -> 사실은 db에 저장하는게 아니라, 영속성 컨텍스트에 저장하는 거임.
  • 영속성 컨텍스트는 논리적인 개념. 눈에 보이지 않음. 엔티티 매니저를 통해서 영속성 컨텍스트에 접근.
  • 엔티티 매니저를 생성하면, 영속성 컨텍스트라는 공간이 생긴다고 생각하면 된다. (대충 1차 캐시를 영속성 컨텍스트라고 생각해도 될 것 같다. 물론 어느정도 차이가 있다).

1.3.1 엔티티 매니저와 영속성 컨텍스트의 상호작용 코드

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;

public class Example {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("ExamplePU");
        EntityManager em = emf.createEntityManager();

        em.getTransaction().begin();

        // 새 엔티티 생성 및 영속화
        User newUser = new User("carami", "carami@example.com");
        em.persist(newUser);

        // 엔티티 조회 -> 
        // 위 newUser가 영속화되었기 때문에 영속성 컨텍스트에 있는 newUser를 리턴
        User user = em.find(User.class, newUser.getId());
        em.getTransaction().commit();
        em.close();
    }
}

1.4 영속성 컨텍스트의 이점

  • 1차 캐시
    • 약간 중간에 있는 느낌. 버퍼링도 가능하고, 캐싱도 가능함.
    • 사실상 1차 캐시가 영속성 컨텍스트. 스프링에서는 조금 달라지긴 하지만 일단은….
    • .Persist() 하면 1차 캐시에 pk-엔티티 이렇게 저장
    • 조회를 하면, db를 바로 뒤지는게 아니라 1차캐시에서 조회함
    • 만약 1차 캐시에 없으면, db에서 조회하고, 이 값을 1차캐시에 다시 저장하고, 이걸 반환.
    • 어플리케이션에서 공유하는 캐시는 2차 캐시라고 함.
    • 디비에서 한번 가져오고, 계속해서 영속성 컨텍스트에다가 저장.
    • 성능적인 이점 보다는 컨셉이 주는 이점. 객체지향적인 느낌.

1.5 영속 엔티티의 동일성 보장.

  • ”== “ 비교하면 어떻게 되니? 같은 객체로 취급됨. 마치 자바 컬렉션에서 꺼낸 것 처럼. 1차 캐시가 있기 때문에 가능.
  • 1차 캐시로 “반복 가능한 읽기(repeatable read) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 어플리케이션 차원에서 제공한다.

 2. 엔티티의 생명주기

2.1 엔티티의 생명주기

  • 비영속 (new,transient)
    • 위와 같이 최초로 객체를 생성한 상태, 영속성 컨텍스트와는 아무런 관련 없는 상태
  • **Member member = new Member();**
  • 영속(managed)
    • 영속성 컨텍스트에 의해 관리되는 상태
    • 물론 위와 같이 persist 메서드를 호출한다고 해서 쿼리가 바로 날아가지는 않고, tx.commit() 시점에 실질적인 쿼리가 날아간다.
    • 즉, 이 시점에 바로 DB에 반영되는 것은 아니다.
  • em.persist(member);
  • 분리된(Detached) 상태: 엔티티가 영속성 컨텍스트에서 분리된 상태
  • 삭제된(Removed) 상태: 엔티티가 삭제되어 데이터베이스에서도 삭제될 상태

2.1.1 비영속 (new,transient)

  • 멤버객체를 생성을 하고, 엔티티 매니저에 안 넣은 상태
Member member = new Member(); 
member.setId(“memberID”) 

-> JPA와 아예 관계가 없음

2.1.2 영속 (managed)

  • em.persist(member)를 통해서, 영속성 컨텍스트라는 곳에 들어가 있는 상태.
  • 멤버가 이제 관리가 된다라는 의미.
  • 사실 이 때 db에 저장되는게 아님.
  • persist()메서드를 쓸 때 쿼리가 날아가는게 아님.
  • *tx.commit()을 하는 때에 쿼리가 날아가게 됨.

2.1.3 영속 -> 준영속

  • 준영속 상태
    • 만약, FIND를 했는데 영속성 컨텍스트에 없으면 DB에서 가져와서 1차 캐시에 올려놓음.
    • 즉, 영속 상태가 됨.
    • 근데 만약에 이거 영속성 컨텍스트에서 관리하고 싶지가 않아, 하면 detach()를 씀.
    • Em.detach() 특정 컨텍스트만 준영속으로 만들 떄.
    • Em.clear() em이라는 영속성 컨텍스트 안에 있는 걸 전부 날려버림. 즉 쿼리가 안날라감.
    • Em.close() 하면 em을 닫아버림. Jpa가 관리가 안되겠지?

2.2 생명주기 전환 메서드

  • persist(entity): 엔티티를 영속 상태로 만든다.
  • merge(entity): 분리된 상태의 엔티티를 다시 영속 상태로 복구한다.
  • remove(entity): 엔티티를 삭제 상태로 전환한다.
  • detach(entity): 엔티티를 분리 상태로 만들며, 이를 통해 성능 및 메모리 사용 최적화, 트랜잭션 관리, 테스트와 디버깅 등을 수행할 수 있다.

2.3 엔티티 클래스의 조건

  1. @Entity 어노테이션: 클래스는 @Entity 어노테이션으로 표시하여, 해당 클래스가 JPA 엔티티임을 나타낸다.
  2. 식별자: 각 엔티티는 유일하게 식별될 수 있는 식별자를 @Id 어노테이션을 사용하여 지정한다.
  3. 기본 생성자: 엔티티 클래스는 JPA 구현체가 엔티티 인스턴스를 프로그래밍적으로 생성할 수 있도록 public 또는 protected의 기본 생성자를 가져야 한다.
  4. 클래스 수준에서의 제한: 최종 클래스(final class)가 아니어야 하며, 변경 가능해야 하고, 일반 Java 객체처럼 메서드나 속성을 추가할 수 있다.

2.3.1 엔티티 예제 코드

package com.example.jpa;

import jakarta.persistence.*;

@Entity // 클래스는 @Entity 어노테이션으로 표시 해당 클래스가 JPA 엔티티 명시
@Table(name = "user")
public class User {
    @Id // 각 엔티티는 유일하게 식별될 수 있는 식별자
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = "username")
    private String username;
    @Column(name = "email")
    private String email;

    // 기본 생성자
    public User() { }

    // 추가적인 생성자와 setter, getter가 올 수 있다.
}

3. 지연 실행

3.1 지연 실행

  • 배칭: JPA는 여러 SQL 문을 함께 배치 처리할 수 있어 데이터베이스로의 왕복 횟수 를 줄일 수 있다.
  • 순서 보장: SQL 작업이 올바른 순서로 실행되도록 보장하여 참조 무결성을 유지한다.

3.1.1 변경 축적

  • 최적화: JPA는 실행해야 할 SQL 문의 수를 최적화할 수 있습니다. 예를 들어, 한 트랜 잭션 내에서 엔티티가 여러 번 수정된 경우, JPA는 최종 상태에 대해서만 하나의 UPDATE 문을 발행할 필요가 있습니다.
  • 불필요한 작업 감소: 데이터베이스에 불필요한 쓰기 작업을 방지합니다.

3.1.2 트랜잭션 무결성 보장

  • 원자성: 트랜잭션 경계 내에서 발생하는 모든 변경 사항은 완전히 커밋되거나 오류가 발생할 경우 전체적으로 롤백된다.
  • 격리성: 트랜잭션이 커밋될 때까지 변경사항이 다른 트랜잭션에 보이지 않는다.

3.1.3 자원 사용 최적화

  • 잠금 감소: 쓰기 작업을 지연함으로써 데이터베이스 행에 대한 잠금 시간을 줄일 수 있으며, 이는 잠금 경합을 줄이고 애플리케이션의 동시성을 개선해준다.
  • 연결 사용 효율성: 데이터베이스 연결이 실제 필요한 SQL 실행에만 사용되므로, 연결 사용이 더 효율적이다.
  • 예시 코드 - 트랜잭션 관리
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

// 엔티티가 저장되지만 데이터베이스에는 즉시 기록되지 않습니다.
User newUser = new User("carami", "carami@example.com"); em.persist(newUser);

// 추가 수정 사항이 있을 수 있음
newUser.setEmail("carami@example.com");

// 커밋: 모든 변경사항이 데이터베이스에 효율적으로 반영됩니다.
em.getTransaction().commit();

em.close();
  • 예시 코드 - 조회시 변경사항에 따라 자동 업데이트
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;
public class UpdateExample {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("ExamplePU");
        EntityManager em = emf.createEntityManager();
        try {
            em.getTransaction().begin();

            // ID로 특정 엔티티를 조회
            User user = em.find(User.class, 1L);

            // 엔티티의 필드 값을 변경
            if (user != null) {
                user.setEmail("updated.email@example.com");   // 이메일 필드 수정
            }

            // 트랜잭션을 커밋할 때 변경된 내용이 자동으로 데이터베이스에 반영
            em.getTransaction().commit();
        } finally {
            em.close();
            emf.close();
        }
    }  
}

4. 영속성 유닛(Persistence Unit)

4.1 영속성 유닛

  • 엔티티 클래스와 데이터베이스 연결 설정을 그룹화하는 JPA의 구성 단위
  • 애플리케이션에서 데이터를 관리하는 방법을 정의하고, 주로 persistence.xml 파일 내에 설정된다.

4.2 영속성 유닛의 주요 구성 요소

  1. 엔티티 클래스 목록: 영속성 유닛은 하나 이상의 엔티티 클래스를 포함할 수 있으며, 이들은 데이터베이스 테이블과 매핑된다.
  2. 데이터 소스 설정: 데이터베이스 연결을 위한 설정 정보를 포함하며, 이는 JPA 구현체가 데이터베이스와의 연결을 관리하는 데 사용된다.
  3. 트랜잭션 타입: JPA는 리소스 로컬(Resource-local)과 JTA(Java Transaction API) 트랜잭션 두 가지 타입을 지원하며, 사용 환경에 따라 적합한 트랜잭션 관리 방식을 선택할 수 있다.

5. 엔티티 매핑

앞서.. jpa에서 제일 중요하게 볼 것

    1. 메커니즘
    1. 매핑 ⇒ 객체와 테이블 매핑

5.1 엔티티 매핑 소개

  • 객체와 테이블 매핑 : @Entity, @Table
  • 피륻와 컬럼 매핑 : @Column
  • 기본 키 메핑 : @Id
  • 연관관계 매핑 : @ManyToOne, @JoinColumn

5.1.1 기본 엔티티 매핑

  • @Entity
    • @Entity 어노테이션은 클래스가 JPA 엔티티임을 나타내며,
    • 이 클래스의 인스턴스는 데이터베이스에 저장될 데이터를 나타낸다.
    • 주요 속성
      • name: 엔티티 이름 지정 —> 그냥 기본 값 쓰는게 정신건강에 좋음
      • catalog: 엔티티가 속할 데이터베이스 카탈로그 지정
      • schema: 엔티티가 속할 데이터베이스 스키마 지정
      • table: 엔티티에 매핑될 데이터베이스 테이블 이름 지정
      • readOnly: 엔티티의 읽기 전용 여부 설정
      • inheritance: 엔티티의 상속 전략 지정
  • @Id
    • @Id 어노테이션은 클래스의 필드를 테이블의 기본 키(primary key)로 지정하며,
    • 각 엔티티 인스턴스를 유일하게 식별하는 데 사용된다.
    • @GeneratedValue(…)
      • @GeneratedValue 어노테이션은 기본 키의 값을 자동으로 생성할 방법을 명시
      • 주요 기본 키 생성 전략
        • GenerationType.AUTO
          • JPA 구현체가 자동으로 가장 적합한 전략을 선택
        • GenerationType.IDENTITY
          • 데이터베이스의 ID 자동 생성 기능을 사용하여 기본 키를 생성한다.
          • 기본 키 생성을 데이터베이스에 위임.
          • (MYSQL의 경우 AUTO-INCREMENT가 이 경우) “나는 모르겠고 디비야 니가 알아서 해조..”
        • GenerationType.SEQUENCE
          • 데이터베이스의 시퀀스를 사용하여 기본 키를 생성한다.
          • 필드의 데이터 타입을 Long으로 하는 것을 추천.
        • GenerationType.TABLE
          • 키 생성을 위한 별도의 데이터베이스 테이블을 사용한다.
  • @Table
    • @Table 어노테이션은 엔티티 클래스가 데이터베이스의 어떤 테이블에 매핑될 것인지를 명시한다.
    • @Table(name=”MBR”) 로 하면 DB의 “MBR”테이블로 쿼리가 나감.
    • 주요 속성
      • name: 매핑될 테이블의 이름을 지정하며, 지정하지 않으면 클래스 이름을 사용한다.
      • catalog: 엔티티가 속할 데이터베이스 카탈로그를 지정한다.
      • schema: 엔티티가 속할 데이터베이스 스키마를 지정한다.
      • uniqueConstraints: 테이블 수준에서 유니크 제약 조건을 지정하며, 여러 열을 포함할 수 있는 제약 조건을 정의할 때 사용된다.
  • @Column
    • @Column 어노테이션은 엔티티의 필드가 데이터베이스 테이블의 어떤 열에 매핑될 것인지를 정의한다.
    • 주요 속성
      • name: 필드가 매핑될 테이블의 열 이름을 지정하며, 지정하지 않으면 필드 이름이 사용
      • nullable: 열이 널(Null) 값을 허용할지 여부를 지정하며, 기본값은 true
      • length: 문자열 필드의 경우 열의 최대 길이를 지정하며, 기본값은 255
      • unique: 이 열이 테이블 내에서 유니크한 값을 가져야 할지 지정 —> 잘 안씀
      • insertable 및 updatable: 이 필드가 데이터베이스에 삽입하거나 업데이트할 수 있는지 여부를 지정하며, 둘 다 기본값은 true이다.
      • Precision, scale : bigint나 소수점 쓸 때 쓰면 됨

5.1.2 관계 매핑 (@OneToMany, @ManyToOne)

  • @OneToMany와 @ManyToOne 관계는 엔티티 간의 일대다 관계를 매핑할 때 사용하는 어노테이션이다.
    • @OneToMany 관계
      • 한 엔티티가 다른 엔티티 여러 개와 관계를 가질 수 있다.
    • @ManyToOne 관계
      • 엔티티 여러 개가 다른 엔티티 하나와 관계를 가질 수 있다.

5.2 엔티티 등록

  • 영속성 컨텍스트 안에는 1차캐시 말고도, “쓰기 지연 sql 저장소”가 있음
  • .persist()하면, jpa가 엔티티를 분석을 해서 “쓰기 지연 sql 저장소”에다가 쿼리문을 보관해 놓음. 차곡 차곡 여기다가 쿼리문을 저장해 놓음
  • 이 쿼리가 언제 날라가냐? Transaction.commit()할 때 날라가는 거임. 플러쉬라고 함. 이 때 실제 db에 커밋됨
  • entity 객체는 기본 생성자가 있어야 함
  • 이렇게 하면 버퍼링이라는게 가능. 하이버네이트 같은 경우에는 옵션이 있음<”hibernate.jdbc.batch_size, value = " ... "> 이 속성
  • 쿼리를 모았다가 디비에 한방에 보내는 것 —> 실전에서는 딱히 크게 얻을 이점이 많지는 않지만, 이러한 최적화의 여지가 생김.

5.3 엔티티 수정 : 변경감지(더티 체킹)

  • jpa의 목적은 마치 자바 컬렉션에 넣은 것처럼 다루는 거임
  • 만약에 컬렉션 값을 수정 할 때 그 값을 꺼내서 수정하고 다시 그걸 저장함? 안함. 딱 찾아온 다음에 변경만 함
  • em.find() 해서 가져온 값을 member.set() 해서 수정 하고, 다시 em.persist 하면 안됨!!!!xxxx —> 마치 자바 컬렉션에서 값 바꾸듯이 바꼈는데 sql이 날라감
  • commit()을 하면, flush()가 호출됨. 무슨 일이 벌어지냐, 엔티티와 스냅샷을 비교함
  • db에서 가져오든 해서 최초로 영속성 컨텍스트에 들어온 상태를 “스냅샷”을 띄워 둠
  • 만약 memberA를 변경하고 flush를 하면, 그 순간 엔티티와 스냅샷을 쫙 비교를 함
  • 비교를 해보고 어 memberA가 바꼈네? 하면 쓰기 지연 SQL 저장소에다가 UPDATE 쿼리를 저장해 두고, 이걸 DB에 날려줌
  • 즉, em.find() → em.set() → em.getTransaction.commit() 으로 진행

5.4 엔티티 삭제

  • Em.remove() 하면 delete() 쿼리 생성. 커밋 시점에 날라감
  • 결론 : jpa는 값을 바꾸면 트랜잭션 커밋 시점에 알아서 업데이트 쿼리가 날아가는 구나. 라고 생각하고, 따로 persist()를 안하는게 정답임

5. 5 생명주기 변환 메서드 사용 관련 예

  • 테이블 생성
CREATE TABLE schools (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL
);

CREATE TABLE students (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    school_id BIGINT,
    FOREIGN KEY (school_id) REFERENCES schools(id)
);

--  테이블 INSERT문
-- Schools 추가
INSERT INTO schools (name) VALUES ('Greenwood High School');
INSERT INTO schools (name) VALUES ('Riverside Academy');

-- Students 추가
INSERT INTO students (name, school_id) VALUES ('Alice', 1);
INSERT INTO students (name, school_id) VALUES ('Bob', 1);
INSERT INTO students (name, school_id) VALUES ('Charlie', 1);
INSERT INTO students (name, school_id) VALUES ('David', 2);
INSERT INTO students (name, school_id) VALUES ('Eva', 2);
  • Entity 클래스 - School
package com.example.jpa;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name="schools")
@Getter@Setter
@NoArgsConstructor
public class School {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    // cascade: 학교가 수정되거나 삭제되면 연관되어있던 학생도 같이 수정되거나 삭제
    // orphanRemoval: 부모(학교) 엔티티와 연관관계가 끊어진 자식(학생) 엔티티를 삭제
    @OneToMany(mappedBy = "school", cascade = CascadeType.ALL, orphanRemoval = true)    // 학교 1 : 학생 n
    private List<Student> students = new ArrayList<>();                                 // 일대다 관계

    public School(String name) {
        this.name = name;
    }
}
  • Entity 클래스 - Student
package com.example.jpa;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Entity
@Table(name="students")
@Getter@Setter
@NoArgsConstructor
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @ManyToOne                          // 본인 기준 관계(학생 n : 학교 1)
    @JoinColumn(name = "school_id")     // 조인 컬럼명
    private School school;  // school_id를 사용하기 위한 방법

    public Student(String name, School school) {
        this.name = name;
        this.school = school;
    }
}
  • 조회, 삽입, 수정, 삭제 예제
package com.example.jpa;

import jakarta.persistence.*;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class SchoolExamMain {

    private static void find(){
        EntityManager em = JPAUtil.getEntityManagerFactory().createEntityManager();
        em.getTransaction().begin();
        try{
            // 1번 학교 인스턴스 생성 후 이름 출력
            School school = em.find(School.class, 1L);
            log.info("schhol name : {}", school.getName());

            // 1번 학교의 학생들 이름 출력
            for (Student student : school.getStudents()) {
                log.info("student name : {}", student.getName());
            }

            // 5번 학생의 이름과 학교이름 출력
            Student student = em.find(Student.class, 5L);
            log.info("student name: {}", student.getName());
            log.info("school name: {}", student.getSchool().getName());
            em.getTransaction().commit();

        } finally {
            em.close();
        }
    }

    private static void create(){
        EntityManager em = JPAUtil.getEntityManagerFactory().createEntityManager();
        em.getTransaction().begin();
        try{
            School school = new School("LionSchool");

            Student student1 = new Student("홍길동", school);
            Student student2 = new Student("아무개", school);
            Student student3 = new Student("삽살개", school);

            school.getStudents().add(student1);
            school.getStudents().add(student2);
            school.getStudents().add(student3);

            // 영속화
            em.persist(school);

            // 실제 db와 비교해 반영
            em.getTransaction().commit();
        }finally {
            em.close();
        }
    }

    private static void update(){
        EntityManager em = JPAUtil.getEntityManagerFactory().createEntityManager();
        em.getTransaction().begin();
        try{
	          // find: 이미 영속성 컨텍스트에 존재
            School school = em.find(School.class, 2L);      

            school.setName("update School Name");

            em.getTransaction().commit();
        }finally {
            em.close();
        }
    }

    private static void delete(){
        EntityManager em = JPAUtil.getEntityManagerFactory().createEntityManager();
        em.getTransaction().begin();
        try{
            School school = em.find(School.class, 5L);

            // cascade 속성->학교가 삭제될 때 해당 학교에 소속되어 있는 학생들도 삭제
            em.remove(school);

            em.getTransaction().commit();
        }finally {
            em.close();
        }
    }

    public static void main(String[] args) {
//        find();
//        create();
//        update();
        delete();
    }
}

6. 플러시

6.1 플러시

  • 영속성 컨텍스트의 변경내용을 데이터베이스에 반영.
  • 보통 db 트랜잭션이 커밋 될 때 플러시가 일어남.
  • 쌓아놓은 sql문이 날라가는 것. 영속성 컨텍스트의 쿼리들을 db에 쭉 날려주는 것.

6.1.1 플러시 발생

  • .commit() 되면 자동으로 발생.
  • 변경 감지(더티 체킹), 수정된 엔티티를 쓰기 지연 sql 저장소에 등록함.
  • 쓰기 지연 sql 저장소의 쿼리를 db에 전송 (등록,수정,삭제 쿼리)를 쫙 보내는거임.
  • 플러시가 발생한다고 해서 커밋 이 발생하는건 아니고…

6.2 영속성 컨텍스트를 플러시 하는 방법

  • em.flush()
    • 직접 호출 —> 진짜로 이렇게 쓰는 경우는 없음
  • 트랜잭션 커밋
    • 플러시 자동 호출
  • Jpql 쿼리 실행
    • 플러시 자동 호출

Q. 플러시를 하면 1차 캐시가 지워지나요?

  • 아니요. 오직 쓰기 지연 sql 저장소에 있는 쿼리들만 db로 날려버리는 거임. 뭔가 바뀐거 이런것만 데이터베이스에 반영이 되는 것.

Q. jpql에서 플러시가 자동으로 호출되는 경우?

  • 만약 persist만 한 상태에서 중간에 jpql로 조회하는 쿼리를 날린다면, 아무것도 안날라올 수도 있음. 따라서, jpql은 무조건 플러시를 한번 날리고 시작함. 아 그래서 날라가는 구나 라고 생각하면 됨.

Q. 플러시 모드 옵션?

  • 딱히 쓸 일은 없음.
  • FlushModeType.COMMIT – 커밋 할 때만 플러시, 쿼리에는 플러시를 안한다. 위 같은 경우에 만약 JPQL을 하는데 뭐 아예 다른 테이블을 가져오고 그런 경우에는 굳이 플러시를 할 필요가 없겠지? 근데 굳이 플러시를 하고 싶지 않을 수도 있음. 정 원하면 플러시 모드를 커밋으로 바꾸라 -> 큰 도움이 되지는 않습니다. 걍 AUTO로 쓰세요. 손대지 말고.
  • 플러시는 영속성 컨텍스트를 비우는게 아니라, 영속성 컨텍스트를 DB에 반영 하는 것.
    • 트랜잭션이라는 작업 단위가 중요함.
    • 커밋 직전에만 동기화 하면 됨.
    • 어쨌든 트랜잭션 커밋 직전에만 날려주면 되니까!!
    • JPA는 결국 이런 동시성 관련 이슈는 다 DB 트랜잭션에 위임하기 때문에.
728x90
728x90

JOIN

  • JOIN 이란 - 하나 이상의 테이블로부터 연관된 데이터를 검색해 오는 방법

Cartesian Join

  • Join에 대한 조건이 생략되거나 잘못 기술되어 한 테이블에 있는 모든 행들이 다른 테이블에 있는 모든 행들과 Join이 되어서 얻어진 경우를 Cartesian Product라 한다.
  • 두 테이블의 합집합이라 생각하면 된다.
  • select * from employees, departments;
  • Cartesian Product를 얻지 않기 위해서 반드시 WHERE 절을 써 준다.
  • (JOIN 하는 테이블의 수 - 1) 개의 JOIN 조건이 필요하다.

Simple Join

SELECT t1.col1, t1.col2, t2.col1 ... FROM Table1 t1, Table2 t2
WHERE t1.col3 = t2.col3
  • FROM 절에 필요로 하는 테이블을 모두 적는다.
    • 컬럼 이름의 모호성을 피하기 위해 테이블 이름으로 직접 지칭 가능 (어느 테이블에 속하는지 알 수 없는 경우가 있을 수 있으므로 Table 이름에 Alias 사용)
    • 적절한 Join 조건을 Where 절에 부여 (일반적으로 테이블 개수 -1 개의 조인 조건이 필요)
    • 일반적으로 PK와 FK간의 = 조건 이 붙는 경우가 많다.

Join 종류

종류 설명

Cross Join (Cartesian Product) 모든 가능한 쌍이 나타남
Inner Join 조건을 만족하는 튜플만 나타남
Outer Join Join 조건을 만족하지 않는 튜플 (짝이 없는 튜플)도 null과 함께 나타남
Theta Join 조건(theta) 에 의한 조인 / (FK가 아닌 다른걸 이용 ?)
Equi-Join Theta Join & 조건이 Equal (=)
Natural Join Equi-Join & 동일한 Column 명 합쳐짐
Self Join 자기 자신과 조인

EQUI-JOIN

  • 컬럼에 있는 값이 정확하게 일치하는 경우에 = 연산자를 사용하여 JOIN
SELECT 테이블명.컬럼명, 테이블명.컬럼명. …
FROM 테이블1, 테이블2
WHERE 테이블1.컬럼1 = 테이블2.컬럼2
  • 테이블명.컬럼명 - 검색해올 데이터가 어디에서 오는지 테이블과 컬럼을 밝혀둔다.
  • 테이블1.칼럼1 = 테이블2.컬럼2 - 두 테이블 간에 논리적으로 값을 연결시키는 칼럼 간의 조건을 기술한다.

EQUI-JOIN의 문형

  • 컬럼에 있는 값들이 정확히 일치하는 경우에 = 연산자를 사용해서 조인
  • 일반적으로 PK-FK 관계에 의하여 JOIN이 성립
  • WHERE절 or ON절을 이용
  • 액세스 효율을 향상시키고 좀 더 명확히 하기 위해 칼럼이름 앞에 테이블 이름을 밝힌다.
  • 같은 이름의 칼럼이 조인대상 테이블에 존재하면 반드시 테이블 이름을 밝혀줄 것 !
  • JOIN을 위한 테이블이 N개라 하면, JOIN을 위한 최소한의 = 조건은 N-1 이다.
# 사원 이름과 부서명을 출력하라.
select emp.ename, dept.dname from emp, dept;
select e.ename, d.dname from emp e, dept d;
select ename, dname from emp e, dept d;

# 중복되는 컬럼명 앞에는 반드시 테이블을 명시해야함
select ename, dname, e.deptno from emp e, dept d;

select e.ename, d.dname from emp e, dept d
where e.deptno = d.deptno;

# 모든 사원의 사번, first_name, email, department_name을 조회하라
select e.employee_id, e.first_name, e.email, d.department_name
from employees e, departments d
where e.department_id = d.department_id;

추가적인 조건 기술

  • WHERE 절에 JOIN 조건 이외의 추가적인 조건을 가질 수 있다.
  • 조인을 만족하는 데이터 중 특정 행만 선택하여 결과를 얻고 싶을 때 추가조건을 AND로 연결한다.
  • 사원의 이름과 부서명을 출력하라. 단, location_id 가 1800인 경우만 출력하라.
  • select concat(e.first_name, ' ', e.last_name) as 이름, d.department_name, l.city from employees e, departments d, locations l where e.department_id = d.department_id and d.location_id = l.location_id and l.location_id = 1800;

조건(theta) Join

  • 임의의 조건을 Join 조건으로 사용가능
  • Non-Equi Join 이라고도 한다.
  • # 이름, 급여, 등급을 조회하라 select e.ename, e.sal, s.grade from emp e, SALGRADE s where e.sal BETWEEN s.LOSAL AND s.HISAL;

Natural Join

  • 두 테이블에 공통 칼럼이 있는 경우 별다른 조인 조건없이 공통 칼럼처럼 묵시적으로 조인이 되는 유형
  • ANSI / ISO SQL1999를 따르는 ANSI JOIN 문법
select e.ename, d.dname
from emp e natural join dept d;

문제점

  • 조인하고자 하는 두 테이블에 같은 이름의 칼럼이 많을 때, 위와 같을 시 특정한 칼럼으로만 조인하고 싶다면 USING 절을 사용해서 기술한다.
  • select e.ename, d.dname from emp e join dept d using(deptno);

INNER JOIN - JOIN ~ ON

  • 공통된 이름의 칼럼이 없는 경우 가장 보편적으로 사용할 수 있는 유형이다.
  • WHERE 절에 일반조건만 쓸 수 있게하고, 조인 조건은 ON에 두어 보다 의미를 명확히 하고 알아보기도 쉽다.
  • ON 부분을 WHERE 절에서 작성 가능하다.
  • select e.ename, d.dname from emp e join dept d on (e.deptno = d.deptno) where d.deptno = 20 ;

Outer Join

  • Join 조건을 만족하지 않는(짝이 없는) 튜플의 경우 Null을 포함하여 결과를 생성
  • 모든 행이 결과 테이블에 참여

종류

  • Left Outer Join - 왼쪽의 모든 튜플은 결과 테이블에 나타남
  • Right Outer Join - 오른쪽의 모든 튜플은 “”
  • Full Outer Join - 양쪽 모두 결과 테이블에 참여

→ 왼쪽 테이블의 Null 값을 포함하는 튜플도 모두 보고 싶어요 : Left Outer Join

  • 오라클의 경우, NULL이 올 수 있는 쪽 조건에 + 를 붙여준다.
select e.ename, d.dname
from emp e left outer join dept d using(deptno); # 왼쪽의 모든 튜플

select e.ename, d.dname
from emp e right outer join dept d using(deptno); # 오른쪽의 모든 튜플

select e.ename, d.dname
from emp e left outer join dept d using(deptno)
union
select e.ename, d.dname
from emp e right outer join dept d using(deptno); # 양쪽 모든 튜플
  • MySQL의 경우, FULL OUTER JOIN이 아닌 UNION 연산을 통해 가능하다.

JOIN 문법 정리

SELECT table1.column, table2.column
FROM table1

[CROSS JOIN table2] |

[NATURAL JOIN table2] |

[JOIN table2 USING (column_name)] |

[JOIN table2
ON(table1.column_name = table2.column_name)] | [LEFT|RIGHT|FULL OUTER JOIN table2
ON (table1.column_name = table2.column_name)];
  • left outer → left / right outer → right 로 축약해서 사용가능

SELF JOIN

  • 하나의 테이블을 서로 다른 테이블로 생각하여 JOIN 하는 것
# 사원이름과 매니저 이름을 출력하라.
select e.ename 사원명, m.ename 매니저이름
from emp e, emp m
where e.mgr = m.empno;

# 사원이름, 매니저 이름을 출력하세요. 단, 매니저가 없는 사원도 출력하시오.
select e.ename 사원명, m.ename 매니저이름
from emp e
left outer join emp m
on e.mgr = m.empno;

🔎 SubQuery

  • 하나의 SQL 질의문 속에 다른 SQL 질의문이 포함되어 있는 형태
  • 쿼리 하나로는 원하는 결과를 얻어내지 못하는 경우에 사용
# SMITH가 속한 부서의 급여 평균을 알고 싶다.
# 1. 스미스의 부서 번호는?
# 2. 해당 부서 번호의 급여 평균

select avg(sal)
from emp
where deptno = (select deptno from emp where ename = 'SMITH');
  • ✔ 서브쿼리 짜는 것을 익히기 위해선 각 조건들을 작은 단위로 (쿼리 하나씩) 뽑아내는 연습을 하는 것이 좋겠다.

Single-Row Subquery

  • Subquery의 결과가 한 개의 ROW인 경우
  • Single-Row Operator 를 사용해야 함 - = , > , >= , < , <= , <>
# 사원의 평균 급여보다 작은 급여를 받는 사원의 이름과 급여를 출력
select ename, sal
from emp
where sal < (select avg(sal)
             from emp);

# 부서이름이 SALES인 부서의 사원 이름과 부서 번호를 출력
select ename, deptno
from emp
where deptno = (select deptno
                from dept
                where dname = 'SALES');

Multi-Row SubQuery (Feat. IN, ANY, ALL)

  • Subquery의 결과가 둘 이상의 ROW
  • Multi-Row에 대한 연산을 사용해야 함 - ANY , ALL , IN , EXIST …
# 아래의 경우는 오류가 발생한다.
SELECT *
FROM emp
WHERE ename = (SELECT MIN(ename)
               FROM emp GROUP BY deptno);

# 아래와 같이 in 연산을 사용하여 그룹 내에 있는지
# 확인하는 식으로 사용해야 한다.
SELECT *
FROM emp
WHERE ename in ('KANG', 'CLARK', 'ADAMS', 'ALLEN');

# 따라서 다음과 같이 해결할 수 있다.
SELECT *
FROM emp
WHERE ename in (SELECT MIN(ename)
               FROM emp GROUP BY deptno);

ANY

  • 다수의 비교값 중 한개라도 만족하면 true
  • IN 과 다른 점은 비교 연산자를 사용한다는 점
  • SELECT ename, sal, deptno FROM emp WHERE ename = ANY (SELECT MIN(ename) FROM emp GROUP BY deptno); # 아래 쿼리는 sal > 950 과 같은 결과이다. SELECT * FROM emp WHERE sal > ANY(950, 3000, 1250)

ALL

  • 전체 값을 비교해서 모두 만족해야만 true
  • Oracle은 오류가 발생하지 않지만, MySQL은 Subquery에서만 사용 가능하다.
  • # 아래의 쿼리는 결과가 없다. 모두를 만족할 수는 없기 때문 # MySQL에서는 값에 대해서는 ALL을 사용할 수 없다. 여기선 단지 예제일뿐 ! SELECT * FROM emp WHERE sal = ALL(950, 3000, 1250) # 결국 아래와 같이 사용된다. select * from emp where sal < all (select sal from emp where deptno in (30, 10));

Correlated Query

  • Outer Query와 Inner Query가 서로 연관되어 있다.
  • 해석방법
    • Outer query의 한 Row를 얻는다.
    • 해당 Row를 가지고 Inner Query를 계산한다.
    • 계산 결과를 이용, Outer query의 WHERE 절을 evaluate
    • 결과가 참이면 해당 Row를 결과에 포함시킨다.
# 사원의 이름, 급여, 부서 번호를 출력하시오. 단 사원의 급여가
# 그 사원이 속한 부서의 평균 급여보다 큰 경우만 출력하시오.
SELECT o.ename, o.sal, o.deptno
FROM emp o
WHERE o.sal > (SELECT AVG(i.sal)
               FROM emp i
               WHERE i.deptno = o.deptno);

# 각 부서별로 최고 급여를 받는 사원을 출력하라.
# 1
select deptno, empno, ename, sal
from emp
where (deptno, sal) in (select deptno, max(sal)
                        from emp
                        group by deptno);

# 2
select a.deptno, a.empno, a.ename, a.sal
from emp a,
     (select b.deptno, max(b.sal) msal
      from emp b
      group by deptno) c
where a.deptno = c.deptno
  and a.sal = c.msal;

# 3
SELECT deptno, empno, ename, sal
FROM emp e
WHERE e.sal = (SELECT max(sal)
               FROM emp WHERE deptno = e.deptno);

Set Operator

  • 두 질의의 결과를 가지고 집합 연산
  • UNION , UNION ALL , INTERSECT , MINUS
  • 우선 임의로 테이블 생성 a : {1, 2, 3} / b : {2, 3, 4}
  • create table a ( name int ); create table b ( name int ); insert into a value ('1'); insert into a value ('2'); insert into a value ('3'); insert into b value ('2'); insert into b value ('3'); insert into b value ('4');
# UNION : 합집합 (중복 제거)
select * from a union select * from b;
# UNION ALL : 합집합 (중복 포함)
select * from a union all select * from b;
# INTERSECT : 교집합
# - MySQL에서는 지원하지 않는다.
select * from a intersect select * from b; # 불가
select a.name from a, b where a.name = b.name;
# minus : 차집합
# - MySql 에서 지원하지 않는다.
select * from a minus select * from b;
select a.name from a where a.name not in (select b.name from b);

RANK() 함수

  • 요소에 대해 순서를 매기는 함수
  • MySQL 8 이상에서 사용가능
SELECT sal, ename,
			rank() over(order by sal desc) AS ranking
FROM emp;

 

728x90
728x90

String 클래스

== 와 equals()

  • == : 비교하는 두 대상의 주소값을 비교
  • equals() : 비교하는 두 대상의 값을 비교
public class StringExam {
    public static void main(String[] args) {
        String str1 = "hello";
        String str2 = "hello";
        String str3 = new String("hello");
        String str4 = new String("hello");

        System.out.println(str1 == str2);      // true
        System.out.println(str1.equals(str2)); // true

        System.out.println(str2 == str3);      // false
        System.out.println(str2.equals(str3)); // true

        System.out.println(str3 == str4);      // false
        System.out.println(str3.equals(str4)); // true
    }
}

추상 클래스

  • abstract 키워드를 사용하여 클래스를 정의한다.
  • 확장 만을 위한 용도로 정의되는 클래스
  • 추상 클래스는 인스턴스가 될 수 없다.
  • 추상 클래스를 상속받는 자손이 인스턴스가 된다.
  • 추상 클래스는 보통 1개 이상의 추상 메소드를 가진다. (없어도 오류 발생은 X)
public abstract class 클래스명 { ... }

추상 메소드

  • 추상 메소드는 메소드에 대한 구현을 가지지 않는다.
  • 추상 클래스의 자식 클래스가 해당 메소드를 구현 하도록 강요하기 위해 존재
  • 추상 메소드는 추상 클래스에만 조재할 수 있다.

✔ 추상 클래스를 사용하는 이유

  1. 공통된 필드와 메서드를 통일할 목적
    • 필드와 메서드 이름을 통일하여 유지보수성을 높이고 통일성을 유지할 수 있다.
  2. 실체 클래스 구현시, 시간절약
    • 강제로 주어지는 필드와 메서드를 가지고 실체 클래스의 성격대로 구현만 하면 된다.
    • 설계 시간이 절약되며, 구현하는데만 집중할 수 있다.
  3. 규격에 맞는 실체 클래스 구현
    • 공통된 속성은 규격에 맞게 구현할 수 있다는 의미
    • → 추상 클래스를 상속받은 실체 클래스들은 반드시 추상 메서드를 재정의해서 실행 내용을 작성해야 하기 때문
    • 그 외의 것은 자유롭게 구현이 가능하다.

중요한 것은, 소스 수정시 다른 소스의 영향도를 적게 가져가면서 변화에는 유연하게 만들기 위해 추상클래스를 사용한다는 점 !

추상클래스의 상속

  • 추상 클래스의 상속에도 extends 키워드 사용
  • 추상 클래스를 상속하는 클래스는 반드시 추상 클래스의 추상 메소드를 구현해야 함
  • 추상 클래스간의 상속에서는 추상클래스를 구현하지 않아도 됨
  • 추상 클래스의 추상 메소드를 상속받아 정의하는 예시
abstract class Shape {
    public abstract double calculateArea();
}

class Triangle extends Shape {
    private double x;
    private double y;

    public Triangle(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public double calculateArea() {
        return this.x * this.y / 2;
    }
}

class Rectangle extends Shape {
    private double x;
    private double y;

    public Rectangle(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public double calculateArea() {
        return this.x * this.y;
    }
}

public class ShapeDemo {
    public static void main(String[] args) {
        Shape triangle = new Triangle(10, 5);
        System.out.println("삼각형의 너비: " + triangle.calculateArea());

        Shape rectangle = new Rectangle(10, 20);
        System.out.println("사각형의 너비: " + rectangle.calculateArea());
    }
}
  • 추상 클래스의 연속 상속
abstract class Aclass {
	public abstract void Amethod();  // 추상 메소드
}
abstract class Bclass extends Aclass { // 추상 클래스를 상속받은 추상 클래스
	public abstract void Bmethod();
}

public class Cclass extends Bclass {
	public void Amethod() { ... } // 반드시 구현해야 함
	public void Amethod() { ... } // 반드시 구현해야 함
**}**

템플릿 메소드 패턴(Template Method Pattern)

  • 디자인 패턴 중 하나로, 알고리즘의 구조를 메소드에 정의하고,
  • 알고리즘의 일부 단계를 서브클래스에서 구현하도록 하여 알고리즘의 일부 변경을 허용하는 패턴이다.
  • → 공통의 알고리즘 과정이 있지만, 그 과정 중 일부가 클래스마다 다를 때 유용하다.
  • 추상 클래스를 사용하여 템플릿 메소드를 정의하고, 구체적인 작업은 서브클래스에서 오버라이드하여 구현

구조

  • 추상 클래스(Abstract Class)
    • 알고리즘의 단계를 정의하는 템플릿 메소드와 알고리즘의 일부를 구현하는 하나 이상의 추상 메소드로 구성
  • 구체 클래스(Concrete Class)
    • 추상 클래스를 상속받아 추상 메소드를 구현한다. (알고리즘의 일부 단계를 구체화)
abstract class BeverageRecipe {
    public **final** void prepareRecipe() {  // 템플릿 메소드
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    abstract void brew(); // 추상 메소드

    abstract void addCondiments(); // 추상 메소드

    public void boilWater() {
        System.out.println("물을 끓입니다.");
    }

    public void pourInCup() {
        System.out.println("컵에 따릅니다.");
    }
}

class Coffee extends BeverageRecipe {
    @Override
    void brew() {
        System.out.println("필터를 통해 커피를 우려냅니다.");
    }

    @Override
    void addCondiments() {
        System.out.println("설탕과 우유를 추가합니다.");
    }
}

class Tea extends BeverageRecipe {
    @Override
    void brew() {
        System.out.println("차를 우려냅니다.");
    }

    @Override
    void addCondiments() {
        System.out.println("레몬을 추가합니다.");
    }
}

public class TemplateMethodPatternDemo {
    public static void main(String[] args) {
        BeverageRecipe tea = new Tea();
        tea.prepareRecipe();

        BeverageRecipe coffee = new Coffee();
        coffee.prepareRecipe();
    }
}

Java Interface

개념

  • 서로 관계가 없는 물체들이 상호 작용을 하기 위해서 사용하는 장치나 시스템
  • 클래스 구조상의 관계와 상관 없이 클래스들에 의해 구현되어질 수 있는 규약

목적

  • 클래스들 사이의 유사한 특성을 부자연스러운 상속 관계를 설정하지 않고 얻어냄

활용

  • 하나 또는 그 이상의 클래스들에서 똑같이 구현되어질 법한 메소드를 선언하는 경우
  • 클래스 자체를 드러내지 않고 객체의 프로그래밍 인터페이스를 제공하는 경우

클래스 vs 인터페이스

  • 완결한 메소드(Concrete method)
    • 메소드의 구현을 포함한 일반적인 메소드를 concrete method라 함
    • 추상 메소드(Abstract Method)의 반대 개념

✔ 인터페이스의 상속

  • 인터페이스는 다중 상속을 지원하지 않는 자바에서 다중 상속의 장점을 활용하기 위해 도입
  • 한 클래스는 하나의 부모 클래스를 가지며 하나 이상의 인터페이스를 구현할 수 있다.
  • 인터페이스 사이에서는 다중 상속이 가능하다.
  • interface 키워드로 선언하고 implements 키워드로 사용
  • 인터페이스를 구현한 클래스에서 추상 메소드를 반드시 정의해야 함
interface Ainter {
    public void aMethod();
    public void same();
}
interface Binter extends Ainter{
    public void bMethod();
}
interface Cinter{
    public void cMethod();
    public void same();
}
interface Dinter extends Cinter, Binter{
    // 인터페이스들 간에는 다중 상속이 가능하다.
    public void dMethod();
}
class DImpl implements Dinter{
    @Override
    public void aMethod() {
        System.out.println("aMethod 구현");
    }
    @Override
    public void bMethod() {

    }
    @Override
    public void same() {

    }
    @Override
    public void cMethod() {
        System.out.println("aMethod 구현");
    }
    @Override
    public void dMethod() {
        System.out.println("aMethod 구현");
    }
}

public class InterfaceDemo{
    public static void main(String[] args) {
        // 인터페이스도 타입이다.
        Ainter ainter = new DImpl();
        Cinter cInter = new DImpl();

        // 자기가 정의한 것만 사용 가능
        ainter.aMethod();
        cInter.cMethod();

        Binter binter = new DImpl();
        Dinter dinter = new DImpl();

        binter.aMethod();
        binter.bMethod();
        binter.same();

        /* 타입이 아는 것 까지만 쓸 수 있고,
         * 외의 것을 쓰고 싶다면 형변환이 필요 */
        ((DImpl)ainter).say();
    }
}

default 메소드

  • 인터페이스가 default 키워드로 선언되면 메소드가 구현될 수 있다.
  • 또한 이를 구현하는 클래스는 default 메소드를 오버라이딩 할 수 있다.
  • 인터페이스가 변경이 되면, 인터페이스를 구현하는 모든 클래스들이 해당 메소드를 구현해야 하는 문제가 있다.
  • → 이런 문제를 해결하기 위하여 인터페이스에 메소드를 구현해 놓을 수 있도록 함
interface Calculator {
    public int plus(int i, int j);

    public int multiple(int i, int j);

    default int exec(int i, int j) {  // default로 선언함으로 메소드를 구현할 수 있다.
        return i + j;
    }
}

class MyCalculator implements Calculator {
    @Override
    public int plus(int i, int j) {
        return i + j;
    }

    @Override
    public int multiple(int i, int j) {
        return i * j;
    }
}

public class CalculatorDemo {
    public static void main(String[] args) {
        Calculator cal = new MyCalculator();

        System.out.println(cal.plus(5, 10));
        System.out.println(cal.multiple(5, 10));
        
        int value = cal.exec(5, 10);
        System.out.println(value);
    }
}

인터페이스의 static

변수

  • 인터페이스에서 int a = 10; 과 같이 멤버 변수를 선언하게 되면 static final 로서 선언된다. (생략 가능, 생략 하더라도 static final 로서 선언됨)
  • 즉, 인터페이스에서 변수를 선언하게 되면, 그 변수는 상수가 된다.
  • → 상수로서 변수와 구분을 위해 대문자로 선언하는 것이 관례이다.

static 메소드

  • 인터페이스에 static 메소드를 정의하는 것은, 클래스에 static 을 정의하는 것과 동일하다.
  • 인터페이스에서 메소드 구현이 가능하다.
  • 반드시 클래스 명으로 메소드를 호출해야 한다.
728x90
728x90

3월 25일 월요일 학습 내용


객체지향

  • 객체들의 모임으로 파악하고자 하는 것
  • 각각의 객체는 메시지를 주고받고, 데이터를 처리할 수 있음
  • 객체지향 관점에서의 서점
    • 책을 관리하는 것은? <책장>
    • 손님을 관리하는 것은? <방명록>
    • 돈을 관리하는 것은? <금고>


객체 지향 프로그래밍

  • 객체 지향 프로그래밍은 컴퓨터 프로그래밍의 패러다임 중 하나이다.  
  • “객체”들의 모임으로 파악하고자 하는 것
    → 각각의 객체는 메시지를 주고받고, 데이터를 처리할 수 있다.
  • 클래스 class
  • 오브젝트 object
  • 인스턴스 instance
  • 참조형 변수 reference variable
  • 핵심 : 메시징 => 객체가 어떻게 해야 하는가가 아니라 무엇을 해야하는가를 설명한다는 것 (메소드)


final 키워드

  • 변수(variable), 메서드(method), 또는 클래스(class)에 사용 가능
  • 변수에 final을 붙이면 이 변수는 수정할 수 없다는 의미
  • 메서드에 final을 붙이면 override를 제한
  • final 키워드를 클래스에 붙이면 상속 불가능 클래스


접근제한자

  • public, protected, package, private가 있다.
    • public : 외부 클래스가 자유롭게 사용할 수 있도록 한다.
    • protected : 같은 패키지 또는 자식 클래스에서 사용할 수 있도록 한다.
    • default**(**package) : 같은 패키지에서 사용할 수 있도록 한다.
    • private : 외부 클래스에서 사용할 수 없도록 한다.접근제한자

  • package안의 하위 package가 있더라고 다른 package로 보기 때문에 사용하고자하는 package를 import 해줘야한다.

 

<예시 코드>

  • com.example.util 패키지 안에 있는 Calculator 클래스
package com.example.util;

public class Calculator {
    public int plus(int a, int b){
        return a+b;
    }
    public int minus(int a, int b){
        return a-b;
    }
}
  • com.example.main 패키지 안에 있는 CalculatorTest 클래스
package com.example.main;

// util 패키지에 있는 클래스를 사용하기 위해 import
import com.example.util.Calculator; 

public class CalculatorTest {
    public static void main(String[] args) {
        Calculator cal = new Calculator();
        System.out.println(cal.plus(1,5));
        System.out.println(cal.minus(5,2));
    }
}

클래스

    • 자바에서 클래스는 설계도라고 본다.
    • 클래스는 필드(속성, Field)와 메소드(행위, 기능, Method)를 가진다.
    • 클래스에는 객체를 생성하기 위한 필드와 메소드가 정의되어 있어야한다.
    • 클래스로부터 객체를 만드는 과정을 인스턴스화라고 한다.
    • 하나의 클래스로부터 여러 객체를 만들 수 있음
    • 클래스로부터 만들어진 객체를 해당 클래스의 인스턴스라고 함
    • main 메소드가 없는 클래스는 실행되지 않음

 

필드(field)

  • 클래스가 가지는 속성
  • 다른 언어의 경우 멤버변수라고 하는 경우도 있다.
  • static 키워드가 함께 사용되는 필드를 클래스 필드, 함께 사용되지 않는 필드를 인스턴스 필드라고 한다.

필드 선언 방법

[접근제한자] [static] [final] 타입 필드명 [=초기값];

String name;
String address = "경기도 수원시";
public int age = 50;
protected boolean flag;​

 

  • 대괄호 안에 있는 내용은 생략가능하다는 뜻
  • 필드의 첫번째 글자는 소문자로 시작하는 것이 관례이며, 자바의 경우 흔히 카멜 케이스로 작성
  • 타입(type)은 기본형(boolean, byte, …)과 참조타입(class, 인터페이스, 배열) 등 사용가능
  • 초기값이 없는 경우
    • 참조형일 경우 null / boolean형일 경우 false / 나머지 기본형은 모두 0 으로 초기화된다.

 

클래스 선언

클래스 선언 방법

public class 클래스명 {

}
  • Dice 클래스와 클래스의 필드 및 메소드 선언
package com.example.util;

public class Dice {

    private int face;
    private int eye;

    public void roll(){
        eye = (int)(Math.random()*face) + 1;
    }
    public int getEye(){
        return eye;
    }
    public void setFace(int face) {
        this.face = face;
    }
}

 

객체 선언

객체 선언 방법

   클래스명 객체명 = new 클래스명();
// 참조타입 참조변수 new연산자 생성자
  • 메모리
    • new 연산자를 사용할 때마다 메모리에 인스턴스가 생성.
    • 인스턴스는 더 이상 참조되는 것이 없을 때, 보통 메모리 부족시 가비지 컬렉션(Garbage Collection)에 저장
    • static한 필드는 클래스가 로딩될 때 딱 한번 메모리에 올라가고 초기화
    • 인스턴스 메소드(static이 안붙은 메소드)는 인스턴스를 생성하고나서 레퍼런스 변 수를 이용해 사용
    • 클래스 메소드는 클래스명.메소드명() 으로 사용가능
    • 메소드 안에 선언된 변수들은 메소드가 실행될 때 메모리에 생성되었다가, 메소드가 종료될 때 사라짐
  • DiceTest 클래스에서 Dice 클래스 객체 생성
package com.example.main;

import com.example.util.Dice;

public class DiceTest {
    public static void main(String[] args) {
        Dice dice = new Dice();
        dice.setFace(6);
        dice.roll();
        int eye = dice.getEye();
        System.out.println(eye);
    }
}

 

메소드 선언

  • 객체지향의 핵심은 “메시징”이다.
  • 자율적인 객체는 스스로 정한 원칙에 따라 판단하고 스스로의 의지를 기반으로 행동하는 객체이다.
  • 객체가 어떤 행동을 하는 이유는 다른 객체로부터 요청을 수신했기 때문이다.

메소드 선언 방법

[접근제한자][static]리턴type 메소드이름([매개변수,...]) {
실행문..
}
    • 메소드 이름은 소문자로 시작하는 것이 관례이다.
    • 클래스의 메소드를 호출하려면
      • 클래스에 대한 인스턴스를 생성하거나
      • 래퍼런스 변수를 이용하여 메시지를 전송한다. (메소드 호출)
      • static 메소드는 인스턴스를 생성하지 않아도 호출할 수 있다.
  • 전달인자 : 메소드를 호출할 때 전달하는 실제 값
  • 매개변수 : 메소드 정의 부분에 나열되어있는 변수
728x90

+ Recent posts