(JPA,SpringData) ORM 기본 개념 정리

|

여러 게시글들을 기반으로 정리해 보았습니다.

ORM(Object-Relational Mapping)이란?

  • 객체(Object)와 관계형 데이터베이스(Relational)의 관계 설정을 자동으로 처리해줍니다.
  • 실제 데이터와 객체와의 개념적 일치하지 않는 부분을 자동으로 매핑해주는데 ResultSet을 받아 Bean에 열심히 넣어주던것을 대신해주는 것과 비슷하게 생각하면 됩니다.
  • 관계형 데이터베이스의 데이터를 객체형 데이터처럼 사용할 수 있습니다.

ORM의 장점

  • 데이터베이스 종류에 제약을 최대한 받지 않습니다.(Native Query 사용시 무효)
  • 객체 중심으로 설계하기 때문에 좀 더 직관적이고 빠르게 개발할 수 있습니다.
  • 객체 지향적 설계로 인해 좀 더 직관적이고 비지니스로직에 집중할 수 있으며 생산성,유지보수성이 향상됩니다.

ORM의 단점

  • 모든 기능을 ORM으로만 작성하기에는 쿼리가 복잡해지면 쓰기 어렵습니다.(통계, 데이터분석등…)
  • 성능이슈, 몇몇 글에 따르면 ORM을 사용시 느리다는 평이 있습니다.
  • SP를 많이 쓰거나 기존 SQL문이 많은 프로그램에는 객체지향의 장점을 활용하기 어렵습니다.

ORM에 적합한 모델

  • Entity를 개별적으로 업데이트
  • 간헐적으로 Set기반 작업 수행 (ex: 고객 레코드및 주문내역을 개별적으로 업데이트)

ORM에 적합하지 않은 모델

  • 많은 수의 레코드를 잦은 빈도로 벌크 업데이트 수행
  • 통계, 데이터분석처리(OLAP)
  • 이미 작성된 핸드코딩/프로시저를 이용하는 환경(MyBatis를 쓰면 좋다, 다른 ORM도 이런부분을 지원합니다.)
  • 순수 SQL문을 쓰는게 더 나을때

참고

ORM (Object Relation Mapping) ORM 의 장점과 단점

(vi 명령어) 비주얼모드(영역지정),복사,붙여넣기,실행취소(undo),다시실행(redo)

|

v : 비주얼모드(영역지정)

문자단위로 선택영역 지정을 할 수 있습니다. v-비주얼모드

V : 라인단위 비주얼모드(영역지정)

라인단위로 선택영역 지정을 할 수 있습니다. V-비주얼모드

y : 복사

문자단위 또는 선택영역을 복사하여 버퍼에 저장합니다.
복사한 문자는 다른 버퍼를 사용하는 명령어(ex:x,d,dd,p …)를 사용할 경우 없어집니다.

Y : 라인단위 복사

한 줄을 복사하여 버퍼에 저장합니다.
복사한 문자는 다른 버퍼를 사용하는 명령어(ex:x,d,dd,p …)를 사용할 경우 없어집니다.

p : 붙여넣기(현재 커서 앞)

버퍼의 내용을 현재 커서 앞에 붙여넣습니다.

P : 붙여넣기(현재 커서 뒤)

버퍼의 내용을 현재 커서 뒤에 붙여넣습니다.

u : 실행취소

이전 실행한 내용을 취소합니다.

ctrl + r : 다시실행

실행 취소한 내용을 다시 실행합니다.

(JPA,SpringData) Sort 사용하기

|

일반적으로 JPA에서 Sort기능을 사용하기 위해서 아래와 같이 메서드명에 OrderBy를 붙여 사용합니다.

public Page<T> findAllByNameOrderByCreatedDateDesc();

이와 같이 만드는 경우 일반적인 목록조회 페이지에서 다수의 정렬기능(ex:이름순,날짜순,날짜역순…)을 필요로 할 경우 위와 같은 메소드를 정렬 방식 개수대로 만들어야 하는 단점이 있습니다. 이 때 사용하기 좋은것이 Sort클래스입니다. Controller에 아래와 같이 인자값으로 등록만 하면 알아서 정렬정보가 세팅됩니다.

@Controller
public List<T> list(Sort sort)
{
  List<T> tList = jpaRepository.findAll(sort);
  return tList;
}

파라메터 형식은 아래와 같이 넘길 수 있습니다.

[이름으로 정렬]
/path?sort=name,asc
[이름 역순으로 정렬]
/path?sort=name,desc
[이름으로 정렬 + ID로 정렬]
/path?sort=name,id
[이름으로 정렬 + ID 역순으로 정렬]
/path?sort=name,asc&sort=id,desc

파라메터에서 전달받은 정렬조건외에 추가적으로 정렬조건을 추가하고 싶을 경우 아래와 같이 and메소드를 이용하여 추가적으로 정렬조건을 넣을수 있습니다.

@Controller
public List<T> list(Sort sort)
{
  sort = sort.and(new Sort(Sort.Direction.DESC, "count"))
  List<T> tList = jpaRepository.findAll(sort);
  return tList;
}

보통 목록에서 정렬은 paging과 같이 하는 경우가 많은데 아래와 같이 Pageable을 인자값으로 받으면 자동적으로 정렬값이 추가됩니다.

@Controller
public List<T> list(Pageable pageable)
{
  List<T> tList = jpaRepository.findAll(pageable);
  return tList;
}

참고 : Spring Data JPA Tutorial: Sorting

JVM 구조 정리

|

그 동안 미뤄왔던 JVM의 기본 구조에 대해 여기 저기 사이트를 참고하여 최종 구조를 그려보았습니다. 여기서 제일 고민했던 부분은 보통 Heap의 Permanent Area로 불리는 영역이 Method Area와 별개인가? 하는 점이였는데 최종 결론은 같은 영역이다!! 라고 결론을 내렸습니다. (RUNTIME DATA AREAS – JAVA’S MEMORY MODEL)참고

JVM 구조

JVM 전체구조

참고

(Spring Cache) @Cacheable key값 정하기

|

Spring Cache는 @Cacheable어노테이션만 붙이면 알아서 인자값을 종류에 맞게 캐쉬된 데이터를 사용합니다.

@Cacheable
public List<String> getList(int page, String query){ ... }

위와 같은 경우 두 인자값이 모두 같아야 같은 캐쉬값을 내보내는데 리턴값에 영향을 미치는 요소가 page 인자값만 영향을 미친다면 아래와 같이 캐쉬 키값을 별도로 설정 할 수 있습니다.

@Cacheable(key = "#page")
public List<String> getList(int page, String query){ ... }

#인자값이름으로 특정 인자를 지정할 수 있는데 위와 같이 하면 같은 page값일 경우 query인자값은 어떤 값이 들어오더라도 같은 캐쉬값을 반환합니다. 그런데 아래와 같은 경우 캐쉬가 정상적으로 동작하지 않습니다.

class Person
{
  private String name;

  public String getName(){ return name; }
}

@Cacheable(key = "#kim")
public List<String getList(Person kim){ ... }

이유는 Spring 4.0 이전 버전에서 사용하는 기본 KeyGenerator인 DefaultKeyGenerator가 아래와 같은 방식으로 키를 생성하기 때문입니다.

Spring Cache Abstraction Default Key Generation - 원문보기 Spring Cache Abstraction Default Key Generation

객체의 native값을 이용하거나 Object일 경우 hashCode()만을 사용하여 키 값을 생성하는데 Object의 hachCode는 객체에서 재정의 하지 않은 이상 무조건 다른 값이 들어가게 되기 때문입니다. Spring 4.0 이후 버전에서의 기본 KeyGenerator는 SimpleKeyGenerator를 사용하며 hashCode만이 아닌 복합키를 사용한다고 합니다.

인자값이 Object 객체인 경우 객체내 값을 이용하여 키 값을 쓸 수 있습니다.(물론 이 때 내부 값도 native값이거나 String같은 heap내 주소가 유일한 경우에만 가능합니다.)

@Cacheable(key = "#kim.name")
public List<String getList(Person kim){ ... }

인자값이 null일 경우를 체크하려면 아래와 같이 사용할 수 있습니다.

@Cacheable(key = "#kim?.name")
public List<String getList(Person kim){ ... }

위 식은 if-then-else 구문에서 else만 빠진것으로 전부 표시하면 아래 같이도 사용 가능합니다.

@Cacheable(key = "#kim?.name:'Unknown'")
public List<String getList(Person kim){ ... }

특정 메소드를 사용하고 싶다면 아래와 같이 사용할 수도 있습니다.

@Cacheable(key = "#kim.getName()")
public List<String getList(Person kim){ ... }

List나 Map값은 객체들은 hachCode값을 이미 내부 객체의 hashCode값들을 이용하여 정해진 규칙대로 만들기 때문에 아래의 경우에는 별도의 처리 없이 사용가능합니다. 하지만 내부에 포함된 데이터가 native타입이 아닐 경우엔 List, Map도 문제가 생깁니다.

@Cacheable(key = "#strList")
public List<String getListByList(List<String> strList){ ... }

@Cacheable(key = "#map")
public List<String getListByMap(Map<Long, String> map){ ... }

마지막으로 인자값이 여러개 있을 경우에 표시하는 예제는 아래와 같습니다.

@Cacheable(key = "'KeyIs' + #kim?.getName():'Unknown' + #page + #list")
public List<String getList(Person kim, int page, List<String> list){ ... }

key값에 사용되는 문법은 sqEL 표현식으로 아래 링크에서 자세한 내용은 확인 가능합니다.

  • (Spring Expression Language (SpEL))[https://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html]