Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Info

RDB를 조작하는 언어인 SQL문을 JPA와 연관하여 SQL문을 함께 정리를하였습니다.

일반적인 SQL문이 JPA에서는 어떻게 표현이 되는지? 샘플화 하였습니다.

Table of Contents


SELECT 구문

데이터베이스의 핵심처리는 검색입니다. 저장되어 있는 테이블에서 필요한 데이터를 뽑아내는 것이며

질의(query),추출(retrieve)이라고도 부릅니다. SQL은 검색과 관련되어 굉장히 많은 기능을 제공합니다.


Expand
title샘플데이터준비

주소 테이블 생성

Select 구문을 익히기전에 JPA를 통해 테이블을 생성하고 샘플 데이터를 입력하겠습니다.


 어플리케이션에서는 데이터모델뿐 아니라 JSON유틸리티/리퀘스트유틸/라우터처리 등

여러가지 성격이 다른 클래스를 다루게 됩니다. 여기서는 data모듈하위에 데이터베이스에

관련된 테이블의 정의를 집합하도록 하겠습니다. 


JPA-주소테이블

Code Block
languagejava
themeEmacs
@Entity
public class Address {
	@Id
    @GeneratedValue
    @Column(name = "ADDRESS_ID")
    private Integer id;
	
	private String name;	
	private String phoneNbr;	
	private String address;	
	private String sex;	
	private int	   age;	
	//getter,setter 생략	
}


SQL-주소테이블

Code Block
languagesql
themeEmacs
CREATE TABLE `address` (
  `address_id` int(11) NOT NULL AUTO_INCREMENT,
  `address` varchar(255) DEFAULT NULL,
  `age` int(11) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `phone_nbr` varchar(255) DEFAULT NULL,
  `sex` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`address_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8


JPA-주소 데이터 입력

Code Block
languagejava
themeEmacs
package com.example.demo.data;

import org.springframework.data.repository.CrudRepository;

public interface AddressRepo extends CrudRepository<Address, Long>{

}
//사용예
	@Autowired
	private AddressRepo addressRepo;

	public void InsertAddressData(){		
		String[] addArray = {
				"서울","대구","부산","창원","마산","제주"				
		};
		
		for(int i=0; i<100;i++) {
			
			double random = Math.random();
		    double x = random*100;
		    int y = (int)x + 1; //Add 1 to change the range to 1 - 100 instead of 0 - 99
		    
			Address address = new Address();
			address.setAge(y);
			address.setAddress( addArray[i%6] );
			address.setPhoneNbr(String.format("010-3333-1%04d", i));
			
			if( i%2 == 0 ) {
				address.setName(String.format("민수%d", i+1));
				address.setSex("남");				
			}else {
				address.setName(String.format("영희%d", i+1));
				address.setSex("여");				
			}			
			addressRepo.save(address);			
		}		
	}

JPA에서는, SQL문을 직접사용하여 Insert를 하지 않습니다. JPA로 작성된 데이터모델에

Insert를 하기위해서는, CrudRepository 인터페이스를 작성후, 배열을 조작하고

관련 함수를 사용하는것만으로 실제 데이터베이스 테이블에 데이터를 추가하는것과  동일한 효과를 낼수 있습니다.


Info


JPA의 Crud객체는, 데이터 Insert를 효율적으로 수행하기위해

개별 기본 엔티티 추가 / 리스트형태의 엔티티추가 두개의 저장 인터페이스를 지원합니다.


SQL-주소 데이터 입력

No Format
Hibernate: 
    /* insert com.example.demo.data.Address
        */ insert 
        into
            address
            (address, age, name, phone_nbr, sex) 
        values
            (?, ?, ?, ?, ?)

위와같은 SQL문을 실행하여 실제 데이터베이스에 입력을 하게 됩니다.

JPA를 활용하면 Hibernate 기능이 귀찮은 SQL문을 작성해준다라고 보면됩니다.



SELECT와 FROM

전체선택

SQLJPA

addressRepo.findAll();


WHERE

SQLJPA

JPA 함수쿼리 작성

public interface AddressRepo extends CrudRepository<Address, Long>{
List<Address> findBySex(String sex);
}


조회함수사용

addressRepo.findBySex("여");



필터 조합및 연산자

검색을위한 SQL의 연산자조합및 필터기능을 JPA 함수에서 대부분 지원합니다.

...

처음작성은 어색하지만, 완성이되면 가독성및 사용성이 훨씬더 간편해집니다.

GROUP BY

draw.io Diagram
bordertrue
viewerToolbartrue
fitWindowfalse
diagramNamegroupby2
simpleViewerfalse
width
diagramWidth271
revision2

...

다양한 함수를 이용하여, 그룹별 집계처리가 가능합니다.

집계함수

함수용도함수용도
AVG평균MAX최대값
COUNTROW수SUM
MIN최소값






집계 DTO정의

Address데이터를 가지고  특정집계 수행을 한다고 하면 , ex> 성별에따른 회원수 라고 한다면

...

Code Block
languagejava
themeEmacs
package com.example.demo.data;

public class AddressStatistics {
	private String 	fname;  //집계대상 필드는 변동이될수 있기때문에 한정짓지말고,공용이름을 사용합니다.
	private Long	cnt;
	
	public AddressStatistics(String fname,Long cnt) {
		this.fname = fname;
		this.cnt = cnt;
	}
}


쿼리 정의

쿼리검색 조건을 JPA함수형이 아닌, SQL문과 비슷한 JPQL문을 사용하였습니다.

...

Code Block
languagejava
themeEmacs
title사용예
		List<AddressStatistics> addressStaticBySex = addressRepo.findSexCount();		
		addressStaticBySex.forEach(item -> {
			String itemString = String.format("%s %d", item.getFname(), item.getCnt());
			System.out.println(itemString);
		});


HAVING 절

draw.io Diagram
bordertrue
viewerToolbartrue
fitWindowfalse
diagramNamesql_having1
simpleViewerfalse
width
diagramWidth418
revision1

...

Code Block
languagejava
themeEmacs
title사용예
List<AddressStatistics> addressStaticByRegion = addressRepo.findRegionAvgage(10,49);
// 결과
//대구 51.40
//마산 57.20
//부산 54.13
//서울 66.75
//제주 54.69
//창원 58.12


Order BY

지금까지 SELECT구문의 몇가지 예제를 살펴보았습니다. 그런데 그 결과 레코드는 어떠한 순일까요?

...

SQL문으로 처리하고 싶으면, 앞장에서 배운 @Query 어노테이션을 통해 쿼리문 수행이 가능합니다.



데이터 베이스를 사용하다보면, 자주 사용하는 Select문이 생기며 개발중에 읽기 전용으로 공유되어

...

==> addressViewRepo는 조회가능한 함수쿼리 작성만 가능합니다. 


서브쿼리



뷰에서 데이터 선택

select * from address_view;

서브 쿼리로 전개select * from (SELECT t.address_id ,t.name,t.phone_nbr FROM address t) as add;

...

서브쿼리가 어떻게 활용이 되는지 살펴보도록 하겠습니다.


서브 쿼리를 이용한 편리한 매칭조건 설정

No Format
select * from address_view av where av.name in( select name from vipUsers );

...

이러한 서브쿼리는 하드코딩을 없앨수 있는 요소로 활용이 될수 있습니다.

JPQL에서 서브쿼리 사용

JPQL 에서 서브쿼리는 몇가지 제약조건이 있습니다.

...

위 샘플은, View테이블 조회 조건을 서브쿼리로 처리한 예입니다.


서브쿼리 실행계획 검증

서브쿼리는 성능적으로 안좋은 영향을 줄수 있기때문에, 서브쿼리 사용시에는

...

JPA와 관련한 실행계획은 이후에 다시 다룰예정입니다.


JPA Query 수행방법

JPA에서 전략에따라, 다양한 방법으로  쿼리문 수행이 가능하다란것을 알아보았습니다.

...

SQL문을 Builder하는 몇가지 방법을 더 살펴보겠습니다. ( Criteria / QueryDSL)


Query Method

No Format
findByAgeGreaterThan(int age);
findBySex(String sex); 

...

어플리케이션에서도 자주 사용하는,검색 조건 3가지 이내 비교적 간단한 SQL문으로 유추되는 검색조건

에서 유용합니다.

JPQL

No Format
@Query("select new com.example.demo.data.AddressStatistics(t.address,AVG(t.age))
	from Address t where t.age > :minage
	GROUP BY t.address HAVING AVG(t.age) > :filterage ")
List<AddressStatistics> findRegionAvgage(
	@Param("minage") int minage,
	@Param("filterage") double filterage
)

...

만약 오라클에만 있는 Connect BY기능을 사용하려면 NativeQuery를 사용해야합니다.


Criteria Query

No Format
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Member> query = cb.createQuery(Member.class);
Root<Member> m = query.from(Member.class);
CreteriaQuery<Member> cq = query.select(m).where(cb.equal(m.get("username","kim"));
List<Member> resultList = em.createQuery(cq).getResultList();

...

동일한 목표를 가지고, 가독성이 더좋은 Query DSL이 권장됩니다.

Query DSL

No Format
JPAQuery query = new JPAQuery(em);
QMember = member = Qmember.member;
List<Member> members = 
	query.from(member)
	.where(member.username.eq("Kim"))
	.list(member);



...