나를 기록하다
article thumbnail
반응형

클래스패스(classpath) 설정

java -cp {~/Desktop/DEV.PRAO/eclipse-workspace/out/production/eclipse-workspace} fullPathName

클래스패스(classpath) 지정

import문

  • 사용할 클래스가 속한 패키지를 지정하는데 사용
  • import문을 사용하면 클래스를 사용할 때 패키지명을 생략할 수 있다.

import문을 생략 가능한 경우

  1. java.lang 패키지는 자동으로 import 된다 → java.lang패키지의 클래스는 Import하지 않고도 사용할 수 있다.
  2. 나랑 같은 패키지에 속한 클래스는 import문 생략 가능
  • 부모 패키지만 import하면 자식 패키지도 사용 가능할까?
    → 아니다. 자식 패키지는 따로 import 하여야 한다.

import 패키지명.클래스명; vs import 패키지명.*; 성능상의 차이는?

→ C에서는 엄청나게 큰 차이지만 자바에서는 큰 차이가 나지 않는다.

C는 컴파일을 수행하게 되면 내가 사용하는 라이브러리들의 바이너리 코드가 실행파일에 다 들어감. 그래서 C는 컴파일 완료된 파일의 크기가 자바보다 훨씬 큼.

그런데 자바는 실행파일의 크기가 굉장히 작음. 실행파일에 라이브러리가 포함되지 않음. 실행파일 자체가 소스코드를 바이트 코드로 변환하는 작업만 포함하면 됨.

자바 라이브러리는 jvm에 이미 가지고 있는 것을 가져와서 사용. → 자바에서는 큰 차이가 없음.

 

 

제어자(modifiers)

접근 제한자

  1. public
  2. protected
    같은 패키지 내에서, 그리고 다른 패키지의 자손클래스에서 접근이 가능
  3. (default) - 다른 말로 패키지 접근 제한이라고도 함.
    같은 패키지에 있는 다른 객체는 default 패키지를 자유롭게 호출 가능
    외부 패키지에 있는 다른 객체는 default 패키지를 호출 불가능
  4. private
    같은 클래스 내에서만 접근이 가능 >> 자기 자신 내에서만 사용 가능
    클래스에 속해 있는 것들에는 사용 가능 but 클래스 자체에는 붙일 수 없음
  5. native - JIN(Java Native Interface): C나 C++과 같은 시스템 종속적인 외부의 프로그램을 자바에서 사용할 때 사용
    자바 언어의 가장 큰 단점: 자바 언어는 JVM 소프트웨어에서 동작 → 필연적으로 자바 프로그램은 느릴 수 밖에 없음. C와 비교했을 때 자바 프로그램은 수행속도가 현저히 느림.
    컴퓨팅 파워가 발전하면서 인지하지 못할 수준으로 좁혀졌으나 여전히 하드웨어를 제어하는 분야는 시스템 상황이 열악 → 임베디드와 같은 분야에서는 무조건 C를 사용해야 함.
  6. transient - 객체 직렬화. 현재 사용중인 객체를 그 상태 그대로 파일로 저장
  7. abstract - 추상 메서드
  8. synchronized - 동기화. 멀티 쓰레드 프로그래밍 시 사용. 똑같은 프로그램을 서로 다른 여러개의 쓰레드로 실행 가능.
    ex) 표 예매 - 가장 먼저 선점한 사용자(쓰레드)가 좌석을 잠그는 것. 예약 취소 시 다른 쓰레드들에게 기회 제공

 

private나 default로 선언된 변수를 다른 패키지의 자손 클래스에서 받아오는 방법

package chap07.packageA;

public class Parent {
    private int num1;
    int num2;
    protected int num3;
    public int num4;

    public Parent(int num1, int num2, int num3, int num4) {
        this.num1 = num1;
        this.num2 = num2;
        this.num3 = num3;
        this.num4 = num4;
    } // default 생성자 -> 동일한 패키지 내부에서만 사용 가능.
    // 다른 패키지에서도 사용하려면 public 생성자 사용
    
    protected int getNum1() {
        return this.num1;
    }
    public int getNum2() {
        return this.num2;
    }
}
package chap07.packageB;

import chap07.packageA.Parent;

public class Child extends Parent {
    int result;

    Child(int num1, int num2, int num3, int num4) {
        super(num1, num2, num3, num4);
    }

    int getResult1() {
//        this.result = this.num1;  // private 변수는 자식에게 상속 불가
//        this.result = super.num1; // private 변수는 자식에게 상속 불가
//        this.result = this.num2;  // default 변수는 같은 패키지 내에서만 가능
        this.result = this.num3 + this.num4;
        this.result += this.getNum1();
        this.result += this.getNum2();
        // protected 변수는 다른 패키지의 자손 클래스에서 접근 가능
        return this.result;
    }

}

getter를 사용하여 받아올 수 있다.

 

 

접근 제어자를 이용한 캡슐화

  • 접근 제어자를 사용하는 이유
    1. 외부로부터 데이터를 보호
    2. 외부에는 불필요한, 내부적으로만 사용되는 부분 감추기
  • 기존의 Circle 클래스
package chap07;

public class Circle {
    static final double PI = 3.141592; // 가능
//    static final double PI;          // 불가능
//    static final 변수는 반드시 선언과 동시에 초기화 해주어야 한다.
    final int radius;                     // 가능

    static {
//        Circle.PI = 3.141592;
    }

    Circle(int radius) {
        this.radius = radius;
    }

    double getArea() {
        final double result; // 로컬 변수에 final 붙일 수 있다.
        result = this.radius * this.radius * Circle.PI;

        return result;
    }
}

 

  • getter and setter를 이용하여 정보 은닉을 한 Circle 클래스
package chap07;

public class Circle3 {
    static final double PI = 3.141592;
    private int radius;

    Circle3(int radius) {
        this.setRadius(radius);
    }

    void setRadius(int radius) {
        if(radius >= 0) {
            this.radius = radius;
        } else {
            this.radius = 1;
        }
    }

    int getRadius() {   // getter는 매개변수가 없다
        return this.radius;
    }

    double getCircumference() {
        return this.radius * 2 * Circle2.PI;
    }

    double getArea() {
        return this.radius * this.radius * Circle2.PI;
    }
}

 

[번외] 싱글톤 패턴

package chap07;

public class Circle5 {

    static Circle5 circle;
    static final double PI = 3.141592;
    private      int    radius;

    private Circle5(int radius) {
        this.setRadius(radius);
    }

    static Circle5 getCircle5(int radius) {
        if(Circle5.circle == null) {
            Circle5.circle = new Circle5(radius);
        } else {
            Circle5.circle.setRadius(radius);
        }
        return Circle5.circle;
    }

    void setRadius(int radius) {
        if (radius > 0) {
            this.radius = radius;
        } else {
            this.radius = 1;
        }
    }

    int getRadius() {   // getter는 매개변수가 없다
        return this.radius;
    }

    double getCircumference() {
        return this.radius * 2 * Circle5.PI;
    }

    double getArea() {
        return this.radius * this.radius * Circle5.PI;
    }
}

main 메서드

package chap07;

public class Ex10 {
    public static void main(String[] args) {
        Circle5 c1, c2, c3, c4;

        c1 = Circle5.getCircle5(3);
        System.out.println("area:\t" + c1.getArea());
        System.out.println("circumference:\t" + c1.getCircumference());

        c2 = Circle5.getCircle5(4);
        System.out.println("area:\t" + c2.getArea());
        System.out.println("circumference:\t" + c2.getCircumference());

        c3 = Circle5.getCircle5(5);
        System.out.println("area:\t" + c3.getArea());
        System.out.println("circumference:\t" + c3.getCircumference());

        c4 = Circle5.getCircle5(6);
        System.out.println("area:\t" + c4.getArea());
        System.out.println("circumference:\t" + c4.getCircumference());

        System.out.println("==================================");
        System.out.println("c1 == c2:\t" + (c1 == c2));
        System.out.println("c2 == c3:\t" + (c2 == c3));
        System.out.println("c3 == c4:\t" + (c3 == c4));
    }
}

결과

area:	28.274328
circumference:	18.849552000000003
area:	50.265472
circumference:	25.132736
area:	78.5398
circumference:	31.41592
area:	113.097312
circumference:	37.699104000000005
==================================
c1 == c2:	true
c2 == c3:	true
c3 == c1:	true

이 코드는 싱글톤 디자인 패턴의 한 예이다.

싱글톤 패턴이란, 해당 클래스의 인스턴스가 하나만 만들어지고, 어디서든지 이 인스턴스에 접근할 수 있도록 하는 디자인 패턴

이러한 패턴은 전역 변수를 사용하는 것과 비슷하지만, 좀 더 관리하기 쉽고 안정적인 방법을 제공함

클래스 변수 'circle'를 사용하여 Circle5의 유일한 인스턴스를 저장

이 클래스는 프라이빗 생성자를 가지므로, 외부에서 새로운 Circle5 인스턴스를 만들 수 없다.

대신에 getCircle5()라는 정적 메서드를 통해 인스턴스를 가져올 수 있다. 이 메서드는 첫 호출 시에만 새 Circle5 인스턴스를 생성하고, 그 이후 호출에서는 첫 번째로 생성된 인스턴스를 반환한다.

이렇게 하면, 어떤 경우에도 Circle5 클래스의 인스턴스는 단 하나만 존재하게 된다.

setRadius()는 반지름을 설정하는 메서드로, 입력된 값이 0보다 큰 경우에만 반지름을 변경하며, 그렇지 않은 경우에는 기본값인 1로 설정

getRadius(), getCircumference(), getArea()는 각각 반지름, 원의 둘레, 원의 면적을 반환하는 메서드

 


 

1. 객체와 테이블 매핑

@Entity

특징

@Entity가 붙은 클래스는 JPA가 관리, 엔티티라 부른다.

JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 필수

주의사항

  1. 기본 생성자 필수(파라미터가 없는 public 또는 protected 생성자)
  2. final 클래스, enum, interface, inner 클래스 사용 X
  3. 저장할 필드에 final 사용 X

속성: name

  1. JPA에서 사용할 엔티티 이름을 지정
  2. 기본값: 클래스 이름을 그대로 사용(예: Member)
  3. 같은 클래스 이름이 없으면 가급적 기본값을 사용

main 클래스

package hellojpa;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import java.util.List;

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");

        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try {
            Member member = em.find(Member.class, 150L);
            member.setName("AAAAA");

            em.clear();

            Member member2 = em.find(Member.class, 150L);

            System.out.println("==========================");
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
        emf.close();
    }
}

멤버

package hellojpa;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "MBR")
public class Member {

    @Id
    private Long id;
    private String name;

    public Member() {}

    public Member(Long id, String name) {
        this.id   = id;
        this.name = name;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

출력 SQL 구문

Hibernate: 
    select
        member0_.id as id1_0_0_,
        member0_.name as name2_0_0_ 
    from
        MBR member0_ 
    where
        member0_.id=?

@Table(name = “MBR”)을 추가하면 엔티티 이름을 MBR로 사용한다.

 

2. 데이터베이스 스키마 자동 생성

특징

  1. DDL을 애플리케이션 실행 시점에 자동 생성
  2. 테이블 중심 → 객체 중심
  3. 데이터베이스 방언을 활용해서 데이터베이스에 맞는 적절한 DDL 생성
  4. 이렇게 생성된 DDL은 개발 장비에서만 사용
  5. 생성된 DDL은 운영서버에서는 사용하지 않거나, 적절히 다듬은 후 사용

속성

  1. create: 기존테이블 삭제 후 다시 생성(DROP + CREATE)
  2. create-drop: create와 같으나 종료시점에 테이블 DROP
  3. update: 변경분만 반영(운영DB에는 사용하면 안됨)
  4. validate: 엔티티와 테이블이 정상 매핑되었는지만 확인
  5. none: 사용하지 않음

1. create

persistence.xml에서 create 설정

 

 SQL 구문

Hibernate: 
    
    drop table Member if exists
Hibernate: 
    
    create table Member (
       id bigint not null,
        age integer not null,
        name varchar(255),
        primary key (id)
    )
Hibernate: 
    select
        member0_.id as id1_0_0_,
        member0_.age as age2_0_0_,
        member0_.name as name3_0_0_ 
    from
        Member member0_ 
    where
        member0_.id=?

애플리케이션 로딩 시점에 기존의 테이블은 지우고 엔티티가 매핑된 것들로 테이블을 만들어냄.

 

create-drop

Hibernate: 
    
    drop table Member if exists
Hibernate: 
    
    create table Member (
       id bigint not null,
        age integer not null,
        name varchar(255),
        primary key (id)
    )
Hibernate: 
    select
        member0_.id as id1_0_0_,
        member0_.age as age2_0_0_,
        member0_.name as name3_0_0_ 
    from
        Member member0_ 
    where
        member0_.id=?
Hibernate: 
    
    drop table Member if exists

→ 마지막에 drop table 구문 나감. 테스트 케이스 실행하고 마지막에 깔끔하게 날려버리고 싶을 때 실행

 

update

update문을 통해 먼저 id, name 컬럼 생성

Hibernate: 
    
    create table Member (
       id bigint not null,
        name varchar(255),
        primary key (id)
    )
Hibernate: 
    select
        member0_.id as id1_0_0_,
        member0_.name as name2_0_0_ 
    from
        Member member0_ 
    where
        member0_.id=?

update문 실행을 통해 drop table을 하지 않은 상태로 age 컬럼 추가, 삭제는 불가능

Hibernate: 
    
    alter table Member 
       add column age integer not null
Hibernate: 
    select
        member0_.id as id1_0_0_,
        member0_.age as age2_0_0_,
        member0_.name as name3_0_0_ 
    from
        Member member0_ 
    where
        member0_.id=?

 

validate

엔티티와 테이블이 정상 매핑되었는지만 확인

Schema-validation: missing column [gogo] in table [Member]
	at org.hibernate.tool.schema.internal.AbstractSchemaValidator.validateTable(AbstractSchemaValidator.java:136)
	at org.hibernate.tool.schema.internal.GroupedSchemaValidatorImpl.validateTables(GroupedSchemaValidatorImpl.java:42)
	at org.hibernate.tool.schema.internal.AbstractSchemaValidator.performValidation(AbstractSchemaValidator.java:89)
	at org.hibernate.tool.schema.internal.AbstractSchemaValidator.doValidation(AbstractSchemaValidator.java:68)
	at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:191)
	at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:72)
	at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:310)
	at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:467)
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:939)
	... 4 more

→ gogo 컬럼이 member 테이블에 없는 것을 확인하고 오류 출력

 

주의사항

  1. 운영장비에는 절대 create, create-drop, update를 사용하면 안됨.
  2. 개발 초기 단계 - create or update
  3. 테스트 서버 - update or validate
  4. 스테이징과 운영서버 - validate or none

 

DDL 생성 기능

  • 제약조건 추가: 회원 이름 필수, 10자 초과 x
@Column(nullable = false, length = 10)
  • 유니크 제약조건 추가
@Column(unique = true, length = 10)
Hibernate: 
    
    alter table Member 
       add constraint UK_ektea7vp6e3low620iewuxhlq unique (name)

DDL 생성 기능은 DDL을 자동 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 주지 않는다.

 

 

3. 필드와 컬럼 매핑

요구사항 추가

package hellojpa;

import javax.persistence.*;
import java.util.Date;

@Entity
public class Member {

    @Id // pk 키 매핑
    private Long     id; 

    @Column(name = "name") // 객체는 username이라고 쓰고 싶지만 db에는 name이라고 작성해야할 때 - db 컬럼명은 name
    private String   username;

    private Integer  age; // Integer와 가장 적절한 숫자 타입이 db에도 설정

    @Enumerated(EnumType.STRING) // JPA에서 enum 타입 사용하고 싶을 때, db에는 enum 타입 없음 -> @Enumerated 어노테이션 사용
    private RoleType roleType;

    @Temporal(TemporalType.TIMESTAMP) // 날짜 타입 - DATE / TIME / TIMESTAMP 3가지 타입 존재 - DB는 3가지를 구분해서 사용하기에 매핑 전보 전달해야 함.
    private Date     createdDate;

    @Temporal(TemporalType.TIMESTAMP)
    private Date     lastModifiedDate;

    @Lob // DB에 큰 varchar을 넘어서는 큰 컨텐츠를 넣고 싶을 때 Lob 사용
    private String   description;

		@Transient // DB에 넣지 않고 싶을 때 - 특정 필드를 컬럼에 매핑하지 않음(매핑 무시)
    private int temp;

    public Member() {
    }
}
// RoleType 생성
package hellojpa;

public enum RoleType {
    USER, ADMIN
}

JpaMain

package hellojpa;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import java.util.List;

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");

        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try {
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }
        emf.close();
    }
}

출력 SQL 구문

Hibernate: 
    
    drop table Member if exists
Hibernate: 
    
    create table Member (
       id bigint not null,
        age integer,           // integer
        createdDate timestamp,
        description clob,      // clob(@lob에 문자타입일 때)
        lastModifiedDate timestamp,
        roleType varchar(255), // roleType varchar와 매핑
        name varchar(255),
        primary key (id)
    )

 

매핑 어노테이션 정리

  1. @Column: 컬럼 매핑
  2. @Temporal: 날짜 타입 매핑
  3. @Enumerated: enum 타입 매핑
  4. @Lob: BLOB, CLOB 매핑
  5. @Transient: 특정 필드를 컬럼에 매핑하지 않음(매핑 무시)

 

@Column(설명 / 기본값)

  1. name: 필드와 매핑할 테이블의 컬럼 이름 / 객체의 필드 이름
  2. insertable, updatable: 등록, 변경 가능 여부 / TRUE
  3. nullable(DDL): null 값의 허용 여부. false로 설정하면 DDL 생성 시에 not null 제약조건이 붙음.
  4. unique(DDL): @Table의 uniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약조건을 걸 때 사용(자주 사용하지 않음)
    이유
    Hibernate: 
        
        alter table Member 
           add constraint UK_ektea7vp6e3low620iewuxhlq unique (name)
    
    이름이 랜덤처럼 알아보지 못하는 이름 나옴 → 이름 반영 어려움 → @Table에서 uniqueConstraints를 사용하면 이름까지 사용할 수 있기에 이 방법을 더 선호
  5. length(DDL): 문자 길이 제약조건, String 타입에만 사용 / 255
  6. columnDefinition(DDL): 데이터베이스 컬럼 정보를 직접 줄 수 있다. ex) varchar(100) default ‘EMPTY’ / 필드의 자바 타입과 방언 정보를 사용해
  7. precision, scale(DDL): BigDecimal 타입에서 사용(BigInteger도 사용 가능) / precision = 19, scale = 2
    precision은 소숫점을 포함한 전체 자릿수
    scale은 소수의 자리수
    double, float 타입에는 적용 불가. 아주 큰 숫자나 정밀한 소수를 다루어야 할 때만 사용

 

@Enumerated

⚠️ 주의! ORDINAL 사용 X

이유: RoleType GUEST를 0번지에 추가하였으나, 기존의 USER로 0번을 부여받은 A와 C가 같은 0번지로 겹치는 현상 발생
→ 버그 발생 → 필수로 String으로 사용할 것

ORIDINAL 사용시 발생하는 문제점

  • 속성: value
  • 설명
    1. EnumType.ORDINAL: enum 순서를 데이터베이스에 저장
    2. EnumType.STRING: enum 이름을 데이터베이스에 저장
  • 기본값: EnumType.ORDINAL → 숫자로 저장(integer)

@Temporal

최신 하이버네이트에서는 LocalDate, LocalDateTime을 사용할 때 생략 가능

private LocalDate     testLocalDate;
private LocalDateTime testLocalDateTime;

→ 결과

testLocalDate date,
testLocalDateTime timestamp

@Lob

  1. 데이터베이스 BLOB, CLOB 타입과 매핑
  2. @Lob에는 지정할 수 있는 속성이 없다.
  3. 매핑하는 필드 타입이 문자면 CLOB 매핑, 나머지는 BLOB 매핑
  4. CLOB: String, char[], java.sql.CLOB BLOB: byte[], java.sql. BLOB

@Transient

  1. 필드 매핑 X
  2. 데이터베이스에 저장 X, 조회 X
  3. 주로 메모리상에서만 임시로 어떤 값을 보관하고 싶을 때 사용
반응형
profile

나를 기록하다

@prao

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

profile on loading

Loading...