Java Collection

Java Collection

Java Collection

Collections는 복수개의 엘리먼트를 그룹화 하고 저장하고, 조회하고, 오퍼레이션을 하는 객체이다. 

Collection 인터페이스는 컬렉션 하위 인터페이스들의 최상위 루트이다. 

하위 인터페이스들은 다음과 같다. 

Collection Interface : 

List :
- Ordered : Yes
- Dups : Yes
- 특징 : 특정 위치에 엘리먼트를 입력할 수 있다. 

Map : 
- Ordered : 가능
- Dups : No
- 특징 : 유니크 키, 키에 매핑된 값이 저장된다. 

Queue : 
- Ordered : Yes
- Dups : Yes
- 특징 : FIFO에 의한 오퍼레이션

Set : 
- Ordered : 가능 
- Dups : No
- 특징 : 유니크한 엘리먼트만 받는다. (유니크 성을 위한 구분 필요, 동일성, 동등성)

Collection 구현체 : 

ArrayList : 
- 인터페이스 : List
- Ordered : Index
- Sorted : No
- Dups : Yes
- 특징 : 빠르게 크기를 조정할 수 있는 배열
- BigO :
-- O(1) : get, set
-- O(n) : add, remove, contains, indexOf

LinkedList : 
- 인터페이스 : List
- Ordered : Index
- Sorted : No
- Dups : Yes
- 특징 : 이중 연결 리스트로 구성됨
- BigO :
-- O(1) : get, set, add, remove(from either end)
-- O(n) : get, set, add, remove(from index), contains, indexOf

Vector : 
- 인터페이스 : List
- Ordered : Index
- Sorted : No
- Dups : Yes
- 특징 : 기존 컬렉션, 동기화된 컬렉션임

HashMap : 
- 인터페이스 : Map
- Ordered : No
- sorted : No
- Dups : No
- 특징 : 키/값의 쌍으로 저장
- BigO :
-- O(1) : get, put, remove, containsKey

Hashtable : 
- 인터페이스 : Map
- Ordered : No
- sorted : No
- Dups : No
- 특징 : 기존 컬렉션, 동기화된 컬렉션

LinkedHashMap : 
- 인터페이스 : Map
- Ordered : Insertion, last access
- sorted : No
- Dups : No
- 특징 : 링크드 리스트 and Hash table

TreeMap :
- 인터페이스 : Map
- Ordered : Balanced
- sorted : Yes
- Dups : No
- 특징 : Red-black tree map
- BigO :
-- O(log n) : remove, get, put, containKey

PriorityQueue :
- 인터페이스 : Queue
- Ordered : Priority
- sorted : Yes
- Dups : Yes
- 특징 : 힙 구현체
- BigO :
-- O(1) : peek
-- O(log n) : add, remove

HashSet : 
- 인터페이스 : Set
- Ordered : No
- sorted : No
- Dups : No
- 특징 : 셋에 빠른 접근 가능
- BigO :
-- O(1) : add, remove, contains

LinkedHashSet : 
- 인터페이스 : Set
- Ordered : Insertion
- sorted : No
- Dups : No
- 특징 : Linked list and Hash set
- BigO :
-- O(1) : add, remove, contains

TreeSet :
- 인터페이스 : Set
- Ordered : Sorted
- sorted : Yes
- Dups : No
- 특징 : Red-black tree set
- BigO :
-- O(log n) : add, remove, contains


# 참고 BigO notations : 
---------------------------------------------------------------
O(1) : 엘리먼트의 개수에 상관없이, 변화되지 않는 상수 시간
O(n) : 엘리먼트의 개수에 따라, 선형적으로 증가되는 시간
O(log n) : 엘리먼트 개수에 대해서 로그 로 증가되는 시간
O(n log n) : 엘리먼트 개수에 대해서 선형 로그로 정가되는 시간.
---------------------------------------------------------------


from : Java8 Cookbook 


Java Concurrency

Java Concurrency

Java Concurrency 

Thread 생성
- 자바에서 스레드를 생성하는 방법은 2가지 이다. java.lang.Thread와 java.lang.Runnable 2가지가 있다.

Thread를 상속받는 방법. 
class Comet extends Thread {
    public void run() {
        System.out.println("Orbiting");
        orbit();
    }
}
Comet halley = new Comet();
hally.run();

Runnable를 구현하는 방법.
class Asteroid implements Runnable {
    plublic void run() {
        System.out.println("Orbiting");
        orbit();
    }
}
Asteroid majaAsteroid = new Asteroid();
Thread majaThread = new Thread(majaAsteroid);
majaThread.run();
Lambda 표현식을 이용하는 방법. 
Runnable asteroid = () -> {
    System.out.println("Orbiting");
    orbit();
}
Thread asteroidThread1 = new Thread(asteroid);
Thread asteroidThread2 = new Thread(asteroid);
asteroidThread1.run();
asteroidThread2.run();

Thread 상태 

NEW : 스레드는 생성되었으나 시작되지 않은 상태.
RUNNABLE : 스레드가 실행 가능한 상태가 되었다.
BLOCKED : "alive" 스레드로 모니터 락을 기다리는 블록된 상태.
WAITING : "alive" 스레드로 다른 스레드를 기다리는 동안 wait()혹은 join()을 호출한다.
TIMED_WAITING : "alive"스레드로 특정 시간동안 다른 스레드를 기다린다. sleeping
TERMINATED : 스레드가 완료된 경우

스레드 우선순위

스레드 우선순위는 1 ~ 10단계로 둘수 있다. 기본은 5이다.
MIN_PRIORITY, NORM_PRIORITY,  MAX_PRIORITY 를 이용하여 우선순위를 탐색 할 수 있다.

System.out.println(Thread.MAX_PRIORITY);
낮은 우선순위는 높은 우선순위의 스레드에 양보하게 된다.

공통 메소드 

getPriority() : 스레드의 우선순위를 반환한다.
getState() : 스레드의 상태를 반환한다.
interrupt() : 스레드에 인터럽트를 건다.
isAlive() : 스레드가 얼라이브 상태인지 검사한다.
isInterrupted() : 스레드가 인터럽트 되었는지 검사한다.
join() : 이 메소드를 호출하여 스레드를 기다린다. 이는 객체가 종료 되었음을 알려준다.
setPriority(int) : 스레드의 우선순위를 설정한다.
start() : 스레드를 실행 가능 상태의 위치로 옮긴다.


notify() : 스레드에게 깨어나고, 실행하라고 해준다.
notifyAll() : 모든 기다리는 스레드나 리소스에게 깨어나게 한다. 그리고 스레드 중의 하나를 선택해서 실행한다.
wait() : 다른 스레드가 notify를 호출하거나 notifyAll()을 호출하기 전까지 대기 하게 한다.

# 중요 :
만약 wait() 와 notify()를 호출할때 스레드의 상태가 interrupted flag가 true로 설정된 경우 InterruptedException이 발생된다.

정적 스레드 메소드 : 

activeCount() : 현재 스레드 그룹에서 스레드의 수를 반환한다.
currentThread() : 현재 실행중인 스레드의 레퍼런스를 반환한다.
interrupted() : 현재 실행중인 스레드의 인터럽트 여부를 체크한다.
sleep(long) : 지정된 밀리 세컨동안 실행되는 스레드를 블록한다.
yield() : 현재 스레에게 다른 스레드가 수행할 수 있도록 알려준다.

Synchronization

synchronized 키워드는 특정 블록이나 메소드에게 락을 수행하도록 해준다.
Locks는 블록이나 메소드에 걸리며 이는 매우 중요하게 공유되어야할 리소스에 접근할때 이용하게 된다. 이 모니터 락은 브레이스를 열고 닫는 것으로 시작과 종료가 된다.

객체 인스턴스 t에 동기화 락을 걸어준다.
synchronized (t) {
    // Block body
}
객체 인스턴스 this 에 동기화 락을 걸어준다.
synchronized (this) {
    // Block body
}
raise() 메소드에 동기화 락을 걸어준다.
synchronized void raise() {
    // Method body
}
void raise() {
    synchronized (this) {
        // Method body
    }
}
상기 두 처리는 동일하다.

다음은 정적 메소드에 동기화 락을 걸어 준 예이다.
class Telescope {
    synchronized static void calivrate() {
        // Method body
    }
}

동시성 유틸리티

java에서는 java.util.concurrent 패키지를 제공하여 동시성을 지원하고 있다. 이는 executors와 concurrent collections, synchronizers, timing 유틸리티를 포함하고 있다.

Executor 

ThreadPoolExecutor과 같이 ScheduledThreadPoolExecutor 은 Executor을 구현한 인터페이스이다. 이는 설정 가능하고, 유동적인 스레드 풀을 제공한다.
스레드 풀은 스레드의 재사용 가능하도록 해준다.

Executors는 팩토리를 제공한다.  다음은 스레드 풀을 생성하기 위한 내용을 보여준다.

newCachedThreadPool()
    - unbounded 스레드 풀을 생성하며, 자동적으로 재 사용 가능한 스레드이다.

newFixedThreadPool(int nThreads)
    - 고정 크기의 스레드 풀을 생성한다. 이것은 공유된 unbounded 큐를 끈 스레드를 재사용 하도록 한다.

newScheduledThreadPool(int corePoolSize)
    - 스레드 풀을 생성하며, 스케줄 된 커맨드를 가진 주기적인 실행 혹은 지정된 딜레이를 가진 스레드 풀을 지원한다.

newSingleThreadExecutor()
    - 단일 스레드 executor을 생성하고, unbounded 큐를 끈 처리를 수행한다.

newSingleThreadScheduledExecutor()
    - 단일 스레드 executor을 생성하고 지정된 기간 혹은 특정 딜레이에 의한 스케줄된 커맨드를 가질 수 있다.

다음은 newFixedThreadPool 팩토리 메소드를 사용한 예제이다.

import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
public class ThreadPoolExecutor {
    public static void main() {
        // 태스크를 생성한다.
        RTask t1 = new RTask("thread1");
        RTask t2 = new RTask("thread2");
        // 스레드 매니저를 생성한다.
        ExecutorService threadExecutor = Executors.newFixedThreadPool(2);
        // 스레드를 실행 가능 상태로 만든다.
        threadExecutor.execute(t1);
        threadExecutor.execute(t2);
        // 스레드를 셧다운 한다.
        threadExecutor.shutdown();
    }
}

Concurrent Collections

컬렉션 타입을 동기화된 방식으로 제공한다. 이는 thread-safe 한 처리를 지원한다.

HashMap <--> ConcurrentHashMap
TreeMap <--> ConcurrentSkipListMap
TreeSet   <--> ConcurrentSkipListSet
Map subtypes <--> ConcurrentMap
List subtypes <--> CopyOnWriteArrayList
Set subtypes  <--> CopyOnWriteArraySet
PriorityQueue <--> PriorityBlockingQueue
Deque            <--> BlockingDequeue
Queue            <--> BlockingQueue


Synchronizers

Synchronizers는 특별한 목적을 위해 만들어진 동기화 툴이다.
Semaphore : 허가들의 집합을 관리한다.
CountDownLatch : 막 수행된 오퍼레이션의 집합에 대한 waits를 구현한다.
CyclicBarrer : 공통 장벽 지점에 대한 waits를 구현한다.
Exchanger : 동기화 포인트를 구현하며, 스레드들이 변화시킬 수 있는 엘리먼트에 구현한다.


Timing Utility

TimeUnit은 enumeration으로 시간 기반의 메소드에 공통으로 사용된다. 이것은 시간 파라미터가 어떤 단위로 진행될지 지정하며, 다음과 같다.

if (lock.tryLock(15L, TimeUnit.DAYS)) {...} // 15일 락을 시도한다.

NANOSECONDS : 1/1000us 나노초
MICROSECONDS : 1/1000ms 마이크로초
MILLISECONDS : 1/1000sec 밀리초
SECONDS : sec : 초
MINUTES : 60sec : 60초
HOURS : 60min : 3600초
DAYS : 24hr : 86400초


from : java 8 pocket book







Simple JPA Test

Simple JPA Test

Simple JPA Test

단순한 JPA의 OneToMany, ManyToOne 관계에 대한 테스트를 해보자.

1. JPA테스트 :

- JPA에서 OneToMany와 ManyToOne을 양방향 연결 관계로 설정하고, 이를 운용하는 테스트를 진행해보았다.
- 여기서는 CascadeType.ALL의 설정과 orphanRemove = true에 대해서 알아보고자 한다. 
- 또한 Unique 설정에서 해당 처리가 어떻게 수행되는지 알아 볼 것이다. 

2. Teacher Entity 객체 생성하기.

package kr.co.unclebae.jpa.domain;

import com.google.common.collect.Lists;
import org.springframework.util.CollectionUtils;

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

/** * Created by UncleBae on 15. 9. 14.. */@Entity@Table(name = "TEACHER")
public class Teacher {
    @Id    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "TEACHER_ID")
    private Long id;

    private String name;

    private String subject;

    @OneToMany(mappedBy = "teacher" ,fetch = FetchType.LAZY
cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Student> students = Lists.newArrayList();

    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public List<Student> getStudents() {
        return students;
    }

    public void setStudents(List<Student> students) {
        if (students == null) {
            return;
        }

        for(Student student : students) {
            addStudent(student);
        }
    }

    public void addStudent(Student student) {
        if (students.contains(student)) {
            return;
        }
        this.students.add(student);
        student.setTeacher(this);
    }

    public void removeStudent(Student student) {
        if (student == null) {
            return;
        }

        students.remove(student);
        student.setTeacher(null);
    }

    public void removeAllStudents() {
        if (CollectionUtils.isEmpty(students)) {
            return;
        }

        for(Student student : students) {
            removeStudent(student);
        }
    }

    @Override    public String toString() {
        return String.format("Teacher ID %s, name %s, 
subject %s, students %s", id, name, subject
createStudentsString());
    }

    private String createStudentsString() {
        StringBuffer sb = new StringBuffer();
        sb.append("[");
        if (!CollectionUtils.isEmpty(students)) {
            for (Student student : students) {
                sb.append("{");
                sb.append(student.toString());
                sb.append("},");
            }
        }
        sb.append("]");
        return sb.toString();
    }

    @Override    public boolean equals(Object obj) {
        return super.equals(obj);
    }
}


- Teacher : Student = 1 : N관계 즉 OneToMany관계이다.

- 여기서 mappedBy를 이용하여 연관관계의 주인이 아님을 설정한다. (연관관계의 주인은 보통 Many쪽에 지정한다. )

- cascade = CascadeType.ALL 을 지정하였다.
    이로 인해 Teacher의 영속성 변화에 대해서 자식인 Student에게 전이를 시켜준다. (즉, 부모가 영속화 되면 자식도 영속화 되며, 부모에서 제거되면 자식도 제거되는 처리를 수행한다.)

- orphanRemove = true 를 지정하였다.
    이렇게 지정하면 Teacher객체를 제거하면, 자식인 Student의 객체들도 함께 제거된다.
    또한 Student에 대해서 student.setTeacher(null); 이라고 지정하면 해당 자식은 제거 된다.

- addStudent, removeStudent, removeAllStudents와 같은 편의 메소드를 부모쪽에 지정하여 자식 객체에 대한 연관관계를 함께 추가 및 제거해주도록 처리한다.

3. Student Entity 객체 생성하기.

package kr.co.unclebae.jpa.domain;

import javax.persistence.*;

/** * Created by UncleBae on 15. 9. 14..
 */@Entity@Table(name = "STUDENT")
public class Student {

    @Id    
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true)
    private String name;

    private String grade;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "TEACHER_ID")
    private Teacher teacher;

    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public Teacher getTeacher() {
        return teacher;
    }

    public String getGrade() {
        return grade;
    }

    public void setGrade(String grade) {
        this.grade = grade;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;

        if (teacher == null) {
            return;
        }

        if ( !teacher.getStudents().contains(this)) {
            teacher.getStudents().add(this);
        }
    }

    @Override    public String toString() {
        return String.format("Student id %s, name %s, 
grade %s", id, name, grade );
    }
}

- ManyToOne 를 지정하여 자식 객체에서 부모와의 연관을 맺어준다.

- ManyToOne에서는 cascade를 사용하지 말것. - 만약 cascade = CascadeType.ALL을 지정했을때 테스트를 해보면 자식객체에서 부모 객체를 제거하면, Teacher객체 역시 함께 제거 되어 버린다. (주의)

4. 리포지토리 생성하기 

TeacherRepository.java

package kr.co.unclebae.jpa.repository;

import kr.co.unclebae.jpa.domain.Teacher;
import org.springframework.data.jpa.repository.JpaRepository;

/** * Created by Uncle Bae on 15. 9. 14.. */
public interface TeacherRepository 
    extends JpaRepository<Teacher, Long> {
}
StudentRepository.java
package kr.co.unclebae.jpa.repository;

import kr.co.unclebae.jpa.domain.Student;
import org.springframework.data.jpa.repository.JpaRepository;

/** * Created by naver on 15. 9. 14.. */
public interface StudentRepository 
    extends JpaRepository<Student, Long> {
}


- SpringData JPA를 이용하였다. 

- JpaRepository<T, ID>를 상속받은 인터페이스를 생성하면, 실행시점에 해당 구현체를 만들어준다. 

5. 테스트 케이스 작성하기.

package kr.co.unclebae.jpa.service;

import com.google.common.collect.Lists;
import kr.co.unclebae.jpa.domain.Student;
import kr.co.unclebae.jpa.domain.Teacher;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;

import java.util.List;

/** * Created by Uncle Bae on 15. 9. 14.. */
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration@ContextConfiguration
{"classpath:hibernateConfig.xml"})
public class TeacherTest {

    @Autowired    
    private TeacherService teacherService;

    @Test    
    public void TeacherInsertTest() {
        Teacher teacher = createTeacherFixture();

        Teacher savedTeacher = teacherService.save(teacher);

        System.out.println(String.format("Saved Val : %s", savedTeacher));
    }

    @Test    public void TeacherInsertAndSutdentUpdate() {

        // 일반적인 CascadeType.ALL 테스트        
        Teacher teacherFixture = createTeacherFixture();

        Student student1 = createStudentFixture("STUDENT1", "B-");
        Student student2 = createStudentFixture("STUDENT2", "A+");

        teacherFixture.addStudent(student1);
        teacherFixture.addStudent(student2);

        Teacher teacherSaved = teacherService.save(teacherFixture);
        System.out.println( String.format("[GENERAL CascadeType.ALL] 
SAVED TEACHER %s", teacherSaved.toString() ) );

        // Delete And Insert And Update        
        List<Student> students = Lists.newArrayList();
        Student student3 = createStudentFixture("STUDENT3", "A-");
        students.add(student3);

        Student student4 = createStudentFixture("STUDENT1", "F");
        student4.setId(student1.getId());  
        // Update가 되려면 ID를 동일하게 세팅해야한다.        
        students.add(student4);

        teacherService.deleteAndMerge(teacherSaved.getId(), students);
        Teacher teacherSelected = teacherService.findById(teacherSaved.getId());

        System.out.println( String.format("[CascadeType.REMOVE 
or CascadeType.ALL] Delete And Merge Result %s"
teacherSelected ) );

        teacherService.removeFirstStudent(teacherSelected.getId());
        Teacher teacherSelected2 = teacherService.findById(teacherSaved.getId());

        System.out.println(String.format("[orphanRemove = true] 
RemoveFirstStudent Result %s", teacherSelected2));

        teacherService.deleteById(teacherSelected.getId());
        System.out.println( String.format("[orphanRemove = true] 
delete parent") );
    }

    private Student createStudentFixture(String name, String grade) {
        Student student = new Student();
        student.setName(name);
        student.setGrade(grade);
        return student;
    }

    private Teacher createTeacherFixture() {
        Teacher teacher = new Teacher();
        teacher.setName("Undle Bae");
        teacher.setSubject("Computer Science");
        return teacher;
    }
}

- 위 테스트는 아래 4가지를 테스트한다.
A. 일반적인 CascadeType.ALL 동작

B. Delete And Insert Update 처리 확인

C. 리스트에서 첫번째 자식 객체를 제거한경우 처리 확인

4. 부모 제거시 동작 확인

6. 생성된 스키마 확인. 

    create table STUDENT (
        id bigint generated by default as identity,
        grade varchar(255),
        name varchar(255),
        TEACHER_ID bigint,
        primary key (id)
    )

    create table TEACHER (
        TEACHER_ID bigint generated by default as identity,
        name varchar(255),
        subject varchar(255),
        primary key (TEACHER_ID)
    )

    alter table STUDENT 
        add constraint UK_3leirxifnbbkn35r06ggygpj0  unique (name)

    alter table STUDENT 
        add constraint FK_kqtywag05gv4ixllub1d0ayp1 
        foreign key (TEACHER_ID) 
        references TEACHER


- 상기 내역을 보면 STUDENT에서 foreign key로 부모의 TEACHER_ID가 설정되었다. 

7. Cascade.ALL에 의한 부모 및 자식객체 저장 

    /* insert kr.co.unclebae.jpa.domain.Teacher
        */ insert
        into
            TEACHER
            (TEACHER_ID, name, subject)
        values
            (null, ?, ?)

    /* insert kr.co.unclebae.jpa.domain.Student
        */ insert
        into
            STUDENT
            (id, grade, name, TEACHER_ID)
        values
            (null, ?, ?, ?)

    /* insert kr.co.unclebae.jpa.domain.Student
        */ insert
        into
            STUDENT
            (id, grade, name, TEACHER_ID)
        values
            (null, ?, ?, ?)

- TEACHER 테이블에 에 인서트가 되고나서
- SUTDENT 테이블에 2건 인서트가 되었다.

    select
        teacher0_.TEACHER_ID as TEACHER_1_1_0_,
        teacher0_.name as name2_1_0_,
        teacher0_.subject as subject3_1_0_
    from
        TEACHER teacher0_
    where
        teacher0_.TEACHER_ID=?

    select
        students0_.TEACHER_ID as TEACHER_4_1_0_,
        students0_.id as id1_0_0_,
        students0_.id as id1_0_1_,
        students0_.grade as grade2_0_1_,
        students0_.name as name3_0_1_,
        students0_.TEACHER_ID as TEACHER_4_0_1_
    from
        STUDENT students0_
    where
        students0_.TEACHER_ID=?

- 해당 내역을 출력하기 위해서 TEACHER를 먼저 호출하고, STUDENT 를 TEACHER_ID로 호출하여 2건을 가져오고 있다.

- 출력 결과 : 
[GENERAL CascadeType.ALL]
SAVED TEACHER Teacher ID 1, name Undle Bae, subject Computer Science,
students [
{Student id 1, name STUDENT1, grade B-},
{Student id 2, name STUDENT2, grade A+},
]

8. Cascade.ALL, orphanRemove = true 처리 (삭제, 인서트, 업데이트)

    /* insert kr.co.unclebae.jpa.domain.Student
        */ insert
        into
            STUDENT
            (id, grade, name, TEACHER_ID)
        values
            (null, ?, ?, ?)

    /* update
        kr.co.unclebae.jpa.domain.Student */ update
            STUDENT
        set
            grade=?,
            name=?,
            TEACHER_ID=?
        where
            id=?

    /* delete kr.co.unclebae.jpa.domain.Student */ delete
        from
            STUDENT
        where
            id=?

- 우선 신규로 추가되는 STUDENT3이 인서트 된다.

- 다음으로 STUDENT1이 업데이트 된다. (ID가 같으므로 업데이트함)

- 마지막으로 STUDENT2가 삭제 된다.

# 중요한 것은 JPA는 ID값이 존재하면 업데이트를, ID가 없으면 인서트를 수행한다.
# orphanRemove를 통해서 부모와 연결이 끊어진 객체 STUDENT2는 트랜잭션이 종료 될때 제거된다.

- 처리 결과 : 
[CascadeType.REMOVE or CascadeType.ALL]
Delete And Merge Result Teacher ID 1, name Undle Bae, subject Computer Science,
students [
{Student id 1, name STUDENT1, grade F},
{Student id 3, name STUDENT3, grade A-},
]

9. orphanRemove = true설정후 부모 객체 삭제 

    /* delete kr.co.unclebae.jpa.domain.Student */ delete
        from
            STUDENT
        where
            id=?

- students의 첫번째 엘리먼트만 제거한경우에는 트랜잭션이 종료 되면서 해당 내역을 DB에서 제거한다.

- 처리결과 : 
[orphanRemove = true] RemoveFirstStudent Result Teacher ID 1, name Undle Bae, subject Computer Science, students [{Student id 3, name STUDENT3},]

    /* delete kr.co.unclebae.jpa.domain.Student */ delete 
        from
            STUDENT 
        where
            id=?

    /* delete kr.co.unclebae.jpa.domain.Teacher */ delete 
        from
            TEACHER 
        where
            TEACHER_ID=?

- 부모인 TEACHER객체를 제거하게 되면 부모를 잃은 자식 객체도 자동적으로 제거 된다. 
- 로그 : 
[orphanRemove = true] delete parent

10. 설정하기. 

10.1 hibernateConfig.xml설정하기. 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xmlns:jpa="http://www.springframework.org/schema/data/jpa"

       xmlns:context="http://www.springframework.org/schema/context" 

       xmlns:tx="http://www.springframework.org/schema/tx"

       xsi:schemaLocation="
          http://www.springframework.org/schema/beans 
          http://www.springframework.org/schema/beans/spring-beans.xsd 
          http://www.springframework.org/schema/context 
          http://www.springframework.org/schema/context/spring-context.xsd 
          http://www.springframework.org/schema/tx 
          http://www.springframework.org/schema/tx/spring-tx.xsd 
          http://www.springframework.org/schema/data/jpa 
          http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

   <jpa:repositories base-package="kr.co.unclebae.jpa.repository" />

    <tx:annotation-driven/>

    <context:component-scan base-package="kr.co.unclebae.jpa.service, 
kr.co.unclebae.jpa.repository"/>

    <bean id="dataSource" class="org.apache.tomcat.jdbc
.pool.DataSource">
        <property name="driverClassName" value="org.h2.Driver"/>
        <property name="url" value="jdbc:h2:mem:jpashop"/>
        <property name="username" value="sa"/>
        <property name="password" value=""/>
    </bean>

    <bean id="transactionManager" class="org.springframework
.orm.jpa.JpaTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean class="org.springframework.dao.annotation.
 PersistenceExceptionTranslationPostProcessor"/>

    <bean id="entityManagerFactory" class="org.springframework.
orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="packagesToScan" value="kr.co.unclebae.
jpa.domain"/> 
        <property name="jpaVendorAdapter">

            <bean class="org.springframework.orm.jpa.vendor.
HibernateJpaVendorAdapter"/>
        </property>
        <property name="jpaProperties"> 
            <props>
               <prop key="hibernate.dialect">org.hibernate.dialect.H2Dialect</prop>
               <prop key="hibernate.show_sql">true</prop> 
               <prop key="hibernate.format_sql">true</prop>
               <prop key="hibernate.use_sql_comments">true</prop>
               <prop key="hibernate.id.new_generator_mappings">true</prop>
               <prop key="hibernate.hbm2ddl.auto">create</prop>       
            </props>
        </property>
    </bean>

</beans>


10.2 POM 설정하기 

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>JPATest</groupId>
    <artifactId>JPATest</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>JPATest</name>
    <packaging>war</packaging>

    <properties>

        <java.version>1.8</java.version>

        <spring-data-jpa.version>1.8.0.RELEASE</spring-data-jpa.version>
        <spring-framework.version>4.1.6.RELEASE</spring-framework.version>

        <querydsl.version>3.6.3</querydsl.version>
        <hibernate.version>4.3.10.Final</hibernate.version>
        <tomcat-jdbc.version>7.0.52</tomcat-jdbc.version>
        <h2db.version>1.4.187</h2db.version>

        <jsp.version>2.2</jsp.version>
        <jstl.version>1.2</jstl.version>
        <servlet.version>3.0.1</servlet.version>

        <logback.version>1.1.1</logback.version>
        <slf4j.version>1.7.6</slf4j.version>

        <junit.version>4.12</junit.version>

    </properties>

    <dependencies>

        <!-- 스프링 데이터 JPA -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>${spring-data-jpa.version}</version>
        </dependency>

        <!-- QueryDSL -->
        <dependency>
            <groupId>com.mysema.querydsl</groupId>
            <artifactId>querydsl-apt</artifactId>
            <version>${querydsl.version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>com.mysema.querydsl</groupId>
            <artifactId>querydsl-jpa</artifactId>
            <version>${querydsl.version}</version>
        </dependency>

        <!-- 스프링 MVC -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring-framework.version}</version>
        </dependency>

        <!-- JPA, 하이버네이트 -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>${hibernate.version}</version>
        </dependency>

        <!-- H2 데이터베이스 -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>${h2db.version}</version>
            <scope>runtime</scope>
        </dependency>

        <!-- 커넥션 풀 -->
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-jdbc</artifactId>
            <version>${tomcat-jdbc.version}</version>
            <scope>compile</scope>
        </dependency>

        <!-- WEB -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>${jstl.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>${servlet.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.1</version>
            <scope>provided</scope>
        </dependency>

        <!-- 로깅 SLF4J & LogBack -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
            <scope>runtime</scope>
        </dependency>

        <!-- 테스트 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring-framework.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <!-- Spring Data JPA 1.8이 스프링 4.0.9에 의존관계를 가지므로 스프링 버전을 직접 관리한다. -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <version>${spring-framework.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-beans</artifactId>
                <version>${spring-framework.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>${spring-framework.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-orm</artifactId>
                <version>${spring-framework.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aop</artifactId>
                <version>${spring-framework.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-tx</artifactId>
                <version>${spring-framework.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>


    <build>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <path>/</path>
                    <uriEncoding>UTF-8</uriEncoding>
                </configuration>
            </plugin>

            <plugin>
                <groupId>com.mysema.maven</groupId>
                <artifactId>apt-maven-plugin</artifactId>
                <version>1.1.3</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>process</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>target/generated-sources/java</outputDirectory>
                            <processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

Java Memory Management

Java Memory Management

Java memory Management : 


Garbage Collection (GC) 
- Java 내부의 자동화된 메모리 관리 기능
- 메모리의 할당, 메모리 내의 객체 레퍼런스 관리, 객체로 부터 메모리 환원 등의 역할을 수행한다. 


Garbage Collectors

GC의 목표 요구사항 : 

1. Maximum pause time goal : 
- Maxium pause time goal은 메모리를 복구하는데 어플리케이션이 GC로 인해서 멈추는 시간에 대한 기대치이다. 

2. Throughtput goal :
- Throughtput goal은 어플리케이션 타임에 대한 기대값이나 혹은 GC 이외 부분에서 소비되는 시간을 말한다. 

Garbage Collector의 타입 : 

1. Serial Collector : 
- 하나의 CPU에서 단일 쓰레드로 실행되는 컬렉터이다. 
- GC가 실행되면 어플리케이션은 GC가 완료될때까지 멈춰 있는다. 
- 이 컬렉션은 작은 데이터 셋을 가진 어플리케이션 100Mb내외, 그리고 low pause time을 필요하지 않는데 이용된다. 
- 현재 사용할 이유가 없는 GC

2. Parallel Collector : 
- Parallel Collector은 응답속도가 빠른 컬렉터로 알려져 있다. 이는 복수의 CPU에서 복수의 스레드로 실행이 된다. 
- 이는 GC속도를 매우 빠르게 향상시키고자 할때 이용된다. 
- 이는 pause time 제약이 없고, 어플리케이션 성능이 매우 중요할때 사용된다. 

3. Parallel Compacting Collector
- Parallel Compacting Collector는 parallel collector과 유사하다. 다만 컬렉션 pause time를 줄이는 알고리즘이 다르다. 
- 이 컬렉터는 pause time 제약이 있는 경우 사용하면 좋다. 
- J2SE 5.0 update 6 이상부터 사용이 가능하다. 

4. Concurrent Mark-Sweep Collector
- Concurrent Mark-Sweep Collector(CMS)는 낮은 지연을 가진 collector로 알려져 있다. 이것은 GC pause가 오래 걸리는 큰 컬렉션을 수행하는데 적합한 옵션이다.
- 이 컬렉션은 response time이 throughtput time이나 GC pause보다 더 중요할때 사용된다. 

5. Garbage-First(G1) Collector
- Garbage-First collector는 G1 Collector로 불리며 큰 메모리에서 북수 프로세서 머신을 위해 사용되는 GC이다. 
- 이는 pause time goals를 목표로 하며, 높은 throughtput을 위해 사용된다. 
- 전체 heap 오퍼레이션은 어플리케이션 스레드들과 함께 병렬로 수행된다. 이것은 힙 혹은 라이브 데이터 크기에 비례해서 인터럽션을 방지하는 기능을 가진다. 
- Java SE-7 update 4 이상부터 사용이 가능하며, GMS를 대체하기 위한 목적으로 만들어 졌다. 

Memory management tools

  • visualvm : All-In-One의 자바 트러블 슈팅 툴
  • jconsole : Java Management Extensions 를 따르는 모니터링 툴
  • jinfo : 설정 정보 툴
  • jmap : 메모리 맵 툴
  • jstack : 스택 트레이스 툴
  • jstat : JVM 통계 모니터링 툴
  • jhat : 힙 메모리 분석 툴
  • HPROF Profiler : CPU사용, 힙 통계, 모니터링 상태 프로파일러
  • jdb : 자바 디버깅 툴 

Command-Line Options : 

다음은 GC 관련된 커멘드 라인 옵션들이다. 이것은 Java 인터프리터에 전달된다.
-XX:+PrintGC or -verbose:gc
    힙과 가비지 컬렉션에 대한 각 컬렉션별 일반정보를 출력한다. 

-XX:+PrintCommandLineFlags -version 
    힙 설정, -XX값으로 지정된 값들, 버전 정보들을 출력한다. 

-XX:+PringGCDetails
    힙과 가비지 컬렉션에 대한 정보로 각 컬렉션이 수행되는 동안 상세 정보를 출력

-XX:+PrintGCTimeStamps
    PrintGC 혹은 PrintGCDetails로 부터 출력되는 정보에 타임 스템프를 추가한다. 

-XX:+UseSerialGC
    serial collector를 활성화 한다. 

-XX:+UseParallelGC
    parallel collector를 확성화 한다. 

-XX:+UseParallelOldGC
    parallel compacting collector를 활성화 한다. (노트 : Old는 "old" generation GC를 위해 사용되는 새로운 알고리즘이라는 것을 가리킨다. )

-XX:+UseParNewGC
    parallel young generation collector를 활성화 한다. 이는 concurrent low pause collector과 함께 사용될 수 있다. 

-XX:+UseConcMarkSweepGC
    CMS collector를 활성화 한다. 이는 parallel young generation collector과 함께 사용된다. 

-XX:+UseG1GC
    Garbage-First collector를 활성화 한다.

-XX:+DisableExplicitGC
    명시적인 GC (System.gc())기능을 disable한다. 

-XX:+ParallelGCThreads=[Threads]
    GC쓰레드의 수를 정의한다. 기본적으로 CPU의 수에 따라 결정된다. 이 옵션은 CMS와 Parallel collector에 적용되는 옵션이다. 

-XX:+MaxGCPauseMillis=[milliseconds]
    GC에 힌트를 주는 것으로 최대 pause time goal를 밀리세컨으로 설정하도록 한다. 이 옵션은 parallel collector에 사용된다. 

-XX:+GCTimeRatio=[__value__]
    GC에 힌트를 주는 것으로 어플리케이션 시간대비 GC 시간의 비율을 지정한다. (1 / (1 + [ value ] )) 로 계산되며 이는 요구되는 throughtput goal을 나타낸다.
    기본값은 99이며, 이는 어플리케이션은 99%로 수행되고 GC는 1%의 시간으로 수행됨을 의미한다. 이 옵션은 parallel collector에 사용된다. 

-XX:+CMSIncrementalMode 
    CMS Collector를 위해서만 사용되는 옵션으로 Incremental mode를 켠다. 하나 혹은 2개의 프로세스에 대해서 머신에 사용된다. 

-XX:+CMSIncrementalPacing
    CMS 컬렉서에만 사용되는 것으로 자동 패킹을 활성화 한다. 

-XX:+MinHeapFreeRatio=[percent]
    전체 힙 크기에서 free가 차지하는 최소 비율을 지정한다. 기본은 40%이다. 

-XX:+MaxHeapFreeRatio=[percent]
    전체 힙 크기에서 free가 차지하는 최대 비율을 지정한다. 기본값은 70%이다.

-Xms[bytes]
    최소 힙 크기를 지정한다. 기본 값은 : 시스템 물리 메모리의 1/64 값이다. 초기 힙 크기가 4Mb를 머신을 위해서 설정한다. 

-Max[bytes]
    최대 힙 크기를 지정한다. 기본은 물리메모리의 1/4 보다 작아야 한다. 최대 힙 크기를 64mb를 머신을 위해서 지정한다. 

-Xmn[bytes]
    young generation을 위한 힙의 크기를 지정한다. 

-XX:OnError=[command_line_tool [__options__]]
    fatal에러가 발생했을때 특정 사용자를 위해 스크립트나 커맨드를 사용할 수 있도록 한다. 

-XX+AggressiveOpts
    성능 옵티마이제이션을 수행한다. 앞으로 릴리즈에서는 이 값이 기본이 될 것이다. 

주의 : -XX옵션은 안정화 된 버젼을 보장하지 않는다. 이는 Java Language Specification의 파트가 아니다. 서드파티에서 제공하는 기능이다. 


Metaspace

네이티브 메모리는 클래스 메타의 표현을 위해 사용되는 Metaspace라는 메모리 공간을 생성한다. Metaspace는 PermGen 모델을 계승한다. 이러한 이유로 JDK 8 HotSpot JVM에서는 더이상 PermGen OutOfMemoryError를 볼수 없게된다. JVisualVM은 메타스페이스를 지원하는 분석결과를 제공한다. 만약 메모리 릭이 발생하면 이를 확인할 수 있다. 

























Spring 3.0 Scheduled 어노테이션

Spring 3.0 Scheduled 어노테이션

Spring 3.0 Scheduled Annotation..

@Scheduled annotation 사용하기.
Spring 3.0부터 스케줄된 작업, 비동기 작업을 지원하고 있다.

1. Scheduled를 위한 메타 데이터 등록하기. : 


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:task="http://www.springframework.org/schema/task"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      http://www.springframework.org/schema/task
   http://www.springframework.org/schema/task/spring-task-3.0.xsd">

 <!-- @Scheduled와 @Async 사용 설정 -->
 <task:annotation-driven />
           
</beans>

2. Task 인터페이스 생성하기.  

public interface Task {
public void runTask() ;
}

3. 동기식 태스크 생성하기. 

@Component("syncTask")
public class SyncTask implements Task {
public void runTask() {
System.out.println("Sync Task..."); }

4. 동기식 태스크 호출하기.

@Service
public class TaskExecuteService {
@Autowired
@Qualifier("syncTask")
private Task task;
@Scheduled(cron="*/5 * * * * ?")
public void doSchedule() {
task.runTask();
}
@Scheduled(fixedDelay=5000)
public void doScheduleByFixedDelay() {
task.runTask();
}
@Scheduled(fixedRate=5000)
public void doScheduleByFixedRate() {
task.runTask();
}
}

6. 비동기 처리 수행하기. 

@Async 를 이용하면 비동기적으로 해당 태스크를 수행한다. 이는 해당 스케줄러를 Spring의 TaskExecutor에 제출하는 작업을 수행한다.
@Component("asyncTask")
public class AsyncTask implements Task {
@Async
public void runTask() {
System.out.println("Async Task run" + new Date());
}
}

7. 비동기 처리를 위한 풀 지정하여 수행하기. 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:p="http://www.springframework.org/schema/p"
 xmlns:task="http://www.springframework.org/schema/task"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      http://www.springframework.org/schema/task
   http://www.springframework.org/schema/task/spring-task-3.0.xsd">

 <!-- @Scheduled 와 @Async annotations 동시 사용하기.  -->
 <task:annotation-driven executor="executorWithPoolSizeRange" scheduler="taskScheduler"/>

 <!--
   태스크가 서브밋 되면 executor는 우선 사용하지 않는 thread를 가져와서 사용하게 된다. 만약 사용할 수 있는
   스레드가 없다면 (core size에 도달해버린경우) 태스크는 큐에 해당 작업을 넣게 된다. 만약 큐가 모두 차 있다면
   executor는 태스크를 reject 한다.
 -->
 <!--
  사용할 executor 지정하기.
  ThreadPoolTaskExecutor 인스턴스가 생성이 된다. 여기에는 pool-size, queue-capacity, keep-alive,
  reject-policy 값들을 지정할 수 있다.
 -->
 <task:executor id="executorWithPoolSizeRange"
                pool-size="5-25"
                queue-capacity="100"/>
 
  <!--
  ThreadPoolTaskScheduler 인스턴스에 pool size 설정하기.
  -->          
  <task:scheduler id="taskScheduler" pool-size="1"/>            
</beans>

This article brought from http://krams915.blogspot.kr/2011/01/spring-3-task-scheduling-via.html
























Intellij Live Template 등록하기


Intellij Live Template : 


Intellij 에서 Live Template를 이용하여 간단한 단축 단어를 이용하여 문장을 만들어 낼 수 있다.
반복되는 메소드나 프로그램 패턴을 등록해두고 간단한 단축 단어를 이용하여 문장을 완성시켜보자.

Step 1 : 설정창 열기 

Preference를 열고 > Live Template를 등록한다. (Mac단축키는 Command + , 이다.)


Step 2 : Live Templates Group 등록하기.

'+' 버튼을 누르고 "Template Group...."을 선택하여, 자신이 등록할 템플릿 그룹을 생성한다.

여기서는 UNCLE_BAE_TEMPLATE를 입력하고 OK를 누른다. 

Step 3 : Live Template 생성하기

자신이 생성한 그룹을 선택한 후 
'+' 버튼을 누르고 "Live Template"을 선택하여, 템플릿을 등록한다. 

여기서는 다음과 같이 입력했다. 
Abbreviation : logger
Description  : public static .... (이부분은 단순 설명이다.)
Template text :
    public static final Logger log =  LoggerFactory.getLogger($CLASSNAME$.class);

이렇게 등록하고 "Apply"를 선택한다. 

Step 4 : 등록할 대상 어플리케이션 선택하기 

- No application context yet. Define. 라고 된 부분에서 "Define."클릭하여 원하는 어플리케이션 컨텍스트를 생성한다. 
- 여기서는 Java에서 사용할 템플릿이므로 Java를 선택했다. 

Step 5 : 변수값 등록하기. 

변수를 등록하면, 자동완성할때 변수가 자동세팅 되거나, 커서가 변수 위치에 이동하도록 할 수 있다. 
"$CLASSNAME$ 처럼 변수 이름을 $와 $사이에 넣게 되면 Edit variables 버튼이 활성화 된다. 이를 클릭하자. 



상기 창이 나오면 Expression에 원하는 메소드를 입력할 수 있고, 기본 값을 설정할 수도 있다. 클래스 이름이 필요하다면 Intellij에서 제공하는 className()메소드를 이용하면 된다. 

다른 메소드를 알아보고 싶다면 다음 레퍼런스를 참조하자.  : https://www.jetbrains.com/idea/help/live-template-variables.html

Step 6 : Live Templates 이용하기. 


지정된 abbreviation 단어를 입력하면 위와 같이 나타난다. "Enter"를 선택하면 자동완성된 템플릿이 코드에 대체된다.




우리가 원하는 클래스 이름이 변수값에 대체되어 자동완성 되었다. 
만약 적절하게 import를 해주자. (alt + Enter)를 해주면 자동으로 해당 패키지가 import 된다. 

만약 자동완성이 생성될때 패키지를 자동으로 생성해주고 싶다면
다음과 같이 체크해주자.

중요한 것은 자동 import 기능은 가능한경우에만 수행한다. 동일한 클래스가 여러개 있다면 자동 import는 동작하지 않는다.

이 예제도 동작하지는 않는다. 관련 Logger 가 다양한 패키지에 있기 때문이다...

Let's play programming. ~..