• SpringJPA를 알아보자

    2017. 8. 13. 09:55

    by. 위지원


    요근래 공부한것중 jpa는 너무나도 흥미로운 것인것같다 오늘은 spring jpa를 아침부터 공부해보자!

    자바 orm jpa 프로그래밍 김영한지음을보고 공부한내용


    개요


    jpa 데이터 접근계층은 일명 CRUD로 부르는 등록,수정,삭제,조회를 반복해서 개발해야한다.. 

    이것은 직관적으로 봐도 꽤나 비효율적이다. 그렇다면 이런 기능을 가진 클래스를 상속받는건 어떨까?

    하지만 이방법은 부모클래스에 너무 종속적이며 구현 클래스 상속이 가지는 단점에 노출되어버린다!


    그래서 오늘 공부하려는게 spring jpa다.  

    spring data jpa는 spring framework에서 jpa를 편리하게 사용할 수 있도록 지원하는 프로젝트이다.

    위에서 말한 CRUD의 반복을 피하기 위해 CRUD를 지원해주는 인터페이스를 제공해 인터페이스만 작성해주면 개발을 할 수 있도록 해준다


    SpringDataProject?

    jpa,몽고db,hadoop,neo4j,gemfire,같은 다양한 데이터 저장소에 접근을 추상화해서 데이터접근 코드를 줄여 편의를 증대시킨다.


    환경설정


    라이브러리

    groupId : org.springframework.data

    artifactId : spring-data-jpa 

    version : 1.8.0.RELEASE


    로 dependency를 작성해 라이브러리르 추가해주자


    pom.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"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

    http://www.springframework.org/schema/beans/spring-beans.xsd

    http://www.springframework.org/schema/data/jpa

    http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">


    <jpa:repositories base-package="리포지토리(데이터접근계층)을 검색할 기본패키지경로"> #하위패키지까지 다검색함 </beans>


    spring data jpa는 app을 실행할때 베이스패키지에 지정한 경로 아래에 리포지토리 인터페이스를 찾아 해당 인터페이스를 구현한 클래스를 동적으로 생성한다음 스프링 빈으로 등록한다.구현 클래스를 그래 안!만들어도된다


    주요 메서드


    -save(엔티티와 그자식타입)

    새로운엔티티는 저장하고 이미 있는 엔티티는 수정한다

    -delete(엔티티)

    엔티티하나를 삭제한다

    -findOne(엔티티의 식별자타입)

    엔티티하나를 검색한다.

    -getOne(엔티티의 식별자타입)

    엔티티를 프록시로 조회한다

    -findAll(...)

    모든 엔티티를 조회한다 정렬,페이징 조건을 파라미터로 제공한다


    쿼리메소드기능 


     *사용자 정의 리포지토리를 만들때에는 class 이름을 인터페이스이름+Impl로 지어야한다.


    내가공부하면서 보다가 너무너무너무 반한기능이다! 

    메서드를 선언하면 메서드이름으로 적절한 jpql 쿼리를 생성해서 메서드 이름만으로 쿼리를 생성할 수있다!


    spring jpa가 제공하는 쿼리 메소드 기능은 크게 3가지가 있다.

    -메서드 이름으로 쿼리생성

    -메서드 이름으로 jpa nameQuery 호출

    -@query 어노테이션을 사용해서 리포지토리 인터페이스에 쿼리 직접정의



    1.메서드 이름으로 쿼리 새생성

    ex.findByEmailAndName 으로 이메일과 이름으로 회원을 조회하는 메서드를 정의할  수 있다.


    KeywordSampleJPQL snippet

    And

    findByLastnameAndFirstname

    … where x.lastname = ?1 and x.firstname = ?2

    Or

    findByLastnameOrFirstname

    … where x.lastname = ?1 or x.firstname = ?2

    Is,Equals

    findByFirstname,findByFirstnameIs,findByFirstnameEquals

    … where x.firstname = ?1

    Between

    findByStartDateBetween

    … where x.startDate between ?1 and ?2

    LessThan

    findByAgeLessThan

    … where x.age < ?1

    LessThanEqual

    findByAgeLessThanEqual

    … where x.age <= ?1

    GreaterThan

    findByAgeGreaterThan

    … where x.age > ?1

    GreaterThanEqual

    findByAgeGreaterThanEqual

    … where x.age >= ?1

    After

    findByStartDateAfter

    … where x.startDate > ?1

    Before

    findByStartDateBefore

    … where x.startDate < ?1

    IsNull

    findByAgeIsNull

    … where x.age is null

    IsNotNull,NotNull

    findByAge(Is)NotNull

    … where x.age not null

    Like

    findByFirstnameLike

    … where x.firstname like ?1

    NotLike

    findByFirstnameNotLike

    … where x.firstname not like ?1

    StartingWith

    findByFirstnameStartingWith

    … where x.firstname like ?1(parameter bound with appended %)

    EndingWith

    findByFirstnameEndingWith

    … where x.firstname like ?1(parameter bound with prepended %)

    Containing

    findByFirstnameContaining

    … where x.firstname like ?1(parameter bound wrapped in %)

    OrderBy

    findByAgeOrderByLastnameDesc

    … where x.age = ?1 order by x.lastname desc

    Not

    findByLastnameNot

    … where x.lastname <> ?1

    In

    findByAgeIn(Collection<Age> ages)

    … where x.age in ?1

    NotIn

    findByAgeNotIn(Collection<Age> age)

    … where x.age not in ?1

    True

    findByActiveTrue()

    … where x.active = true

    False

    findByActiveFalse()

    … where x.active = false

    IgnoreCase

    findByFirstnameIgnoreCase

    … where UPPER(x.firstame) = UPPER(?1)

    출처 : http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation



    2.JPA named Query

    쿼리에 이름을 부여해서 사용하는 방법으로 어노테이션으로 정의하거나


    @Entity

    @NamedQuery(

    name="Member.findByUsername",

    query="select m from member m where m.username= : username")


    public class member(

    ...

    )


    또는 xml로 정의할 수 있다.



    <named-query name="Member.findByUsername">

    <query><CDATA[    

    select m 

    from Member m

    where m.username =:username

    ]></query>

    </named-query>



    스프링jpa를 사용하면 이렇게 정의한 메서드를 이름만으로 named 쿼리를 호출할 수 있다.


    public interface MemberRepository extends JpaRePositroty<Member,Long>{

    List<Member> findByUserName(@Param("username") String username);

    }



    3.@Query, 리포지토리 메소드에 쿼리 정의


    리포지토리 메소드에 @Query 어노테이션을 추가하면 직접 정의할 수 있다.

    쿼리르 직접 작성하므로 이름없는 네임드쿼리라 할 수 있으며 jpa named 쿼리처럼 app 실행시점에 문법오류를 발견할 수 있는 장점이 

    있다.


    public interface MemberRepository extends JpaRePositrory<Member,Long>{

    @Query("select m from member m where m.username=?1") #뒤에 숫자가 오타인줄알았더니 파라미터바인딩 순서이다.

    member findbyusername(Srring username);

    }


    *쿼리어노테이션 문장에 nativeQuery=ture 옵션을 주면 네이티브 sql을 사용하고 파라미터 바인딩은 0부터 시작한다.

    =?1 이 =?0으로 

    이게 처음에 뭔가했다. 근데 앞서 우리가 봤었던 =:name 이런식의 매핑을 위치기반으로 표현할떄 =?1 로 하는것이였다!



    4.파라미터 바인딩


    기본은 위치기반이지만 위치기반(=?1),이름기반(=:name) 바인딩 모두 지원한다. 유지보수의 편리를 위래 이름기반으로 하는게 좋다고한다. 


    @Query("select m from mever m where m.username=:name")

    member findbyusername(@Param("name") String username);



    5.벌크성 수정쿼리는 원래 executeUpdate() 를 쓰던구문을 spring jpa에서는 @Modifying 어노테이션을 쓰면된다

    @Modifying
    @Query("update User u set u.firstname = ?1 where u.lastname = ?2")
    int setFixedFirstnameFor(String firstname, String lastname);




    6.반환타입


    메서드이름으로 호출할때 메서드의 타입으로 반환타입을 설정해주면 된다.


    List<Member> findByname(String name);

    Member findByEmail(String email);



    조회결과가 없으면 null을 반환한다.


    7.페이징과 정렬

    Sort,Pageable(Sort기능이 내부에 포함됨) 두가지 특별한 파라미터를 제공하여 페이징과 정렬을 할 수 있게 해준다.

    반환타입으로 Page를 설정해주면 검색된 전체 데이터 건수를 조회하는 count 쿼리를 추가로 호출해준다


    Page<Member> findByName(String name,Pageable pageable);


    아래와같이 그냥 List를 써도된다.

    List<~~~~


    List<Member> findByname(String name,Sort sort);



    실제로 어떻게 사용하는지 보자 (이름이 김으로 시작하는 회원을 이름으로 내림차순'desc',페이지당10개보여줌)


    //정의


    public interface MemberRepository extends Repository<Member,Long>{

    page<Member> findBynameStartingWith(String name,pageable pageable);}


    //실행


    PageRequest pageRequest=

    new PageRequest(0,10,new Sort(Direction.DESC,"name"));


    Page<Member> result=memberRepository.findByNameSTartingWith("김",pageRequest);


    List<Member> members=result.getCountent(); //조회된 데이터

    int totalPages=result.getTotalPages(); //전체페이지수

    boolean hasNextPage=result.hasNextPage(); // 다음페이지 존재여부


    그 외에도 

    getNumber = 현재페이지

    getSize= 페이지크기

    getTotalPages = 전체 페이지수

    getNumberOfElements 현재페이지에 나올 데이터 수

    등의 많은 메서드를 지원한다.


    명세


    And,Or 같은연산자를 조합하여 참,거짓으로 평가되는 술어를 만들 수 있다. 예를들어 데이터를 검색하기 위한 제약조건 하나하나를 술어라 할 수 있다.


    Specification 클래스는 검색조건을 다양하게 조립해서 새로운 검색조건을 만들 수 있다.


    JpaSpecificationExecutor<T> 인터페이스를 상속받아 사용하면 된다.


    public interface JpaSpecificationExecutor<T>{


    T findOne(Specification<T> spec);

    List<T> findAll(Specifiaction<T> spec);

    Page<T> findAll(Specifiaction<T> spec,Pageable pageable);

    List<T> findAll(Specifiaction<T> spec, Sort sort);

    long count(Specifiaction<T> spec);

    }



    명세는 잘 이해가 안되어서 다른 블로그를 참조했다 . http://astrod.github.io/2015/10/11/11.html


    '2017년 > web Development' 카테고리의 다른 글

    jpa를 알아보자  (0) 2017.08.10
    mysql과 spring을 연동해보자  (0) 2017.08.07
    웹개발환경을 구축해보자 [spring framework,maven]  (0) 2017.08.07
    세션과 자스의 이해  (0) 2017.07.19
    xml과 json  (0) 2017.07.05