2016년 3월 23일 수요일

JPA,QueryDSL4.X강좌 –쿼리결과를 특정빈에 담기(Bean population) 

 쿼리 결과를 엔티티가 아닌 특정한 자바객체(DTO, VO)로 받고 싶은 경우 Pjojections를 사용하면 되는데 .bean()을 사용하면 Setter로 값을 채우고 .field()라고 하면 필드(멤버변수)에 직접 접근하여 값을 채우며, .constructor() 라고 하면 생성자를 통해 값을 채운다. 

[ename, sal 필드를 갖는 EmpDTO가 있다면] 

아래는 Setter를 통해 값을 저장한다. 

List<EmpDTO> dtos = query 
.select( 
Projections.bean(EmpDTO.class, emp.ename, emp.sal)) 
.fetch(); 

아래는 필드에 직접 값을 저장한다. 

List<EmpDTO> dtos = query 
.select( 
 Projections.fields(EmpDTO.class, emp.ename, emp.sal)) 
.fetch(); 
JPA,QueryDSL4.X강좌 – 프로젝션(Projection)과 결과반환

 프로젝션 과 결과반환 
조회를 원하는 SELECT의 결과대상 칼럼을 지정하는 것을 프로젝션(Projection)이라 한다. 

//단일 칼럼 
 
public List<String> selectDeptExistsEmp() { 
List<String> depts = queryFactory.select(dept.dname).from(dept) 
.where(JPA__EXPRESSION__s 
      .selectFrom(emp)
      .where(emp.dept.deptno.eq(dept.deptno)).exists() 
                ) 
      .fetch();
return depts; 


// 여러 칼럼의 값을 SELECT 하는 경우 
// com.querydsl.core.Tuple을 이용하고,  조회는 get() 메소드를 이용하면 된다. 
 
public List<Tuple> selectEmpEnameDnameJoinDept(Long deptno) { 
List<Tuple> emps = queryFactory 
.select(emp.ename, dept.dname) 
.from(emp) 
.innerJoin(emp.dept, dept) 
.where(emp.dept.deptno.eq(deptno)) 
.fetch(); 

return emps; 


결과를 받는쪽에서는 다음과 같이 Map을 이용하면 된다. 

Map<String, String> m = new HashMap<String, String>(); 
QEmp emp = QEmp.emp; 
List<Tuple> result = empService.selectEnameJobByEmpno(empno); 
for (Tuple row : result) { 
m.put(row.get(emp.ename), row.get(emp.job)); 
} 
JPA,QueryDSL4.X강좌[그룹핑페이징  정렬조인, orderBy, groupBy, paging, join]

첨부 파일 참조하세요.
JPA,Querydsl4.X강좌 – 서브쿼리, JPA__EXPRESSION__s 

 서브 쿼리 
Sub Query는 JPA__EXPRESSION__s를 만들어서 사용한다. 


 
public List<Emp> selectEmpMaxSal() { 
QEmp e = new QEmp("e"); 

List<Emp> emps = queryFactory.selectFrom(emp) 
.where(emp.sal.eq( 
JPA__EXPRESSION__s.select(e.sal.max()).from(e))) 
.fetch();
return emps; 


 
public List<Emp> selectEmpMaxSalOfDept() { 
QEmp e = new QEmp("e"); 
List<Emp> emps = queryFactory.selectFrom(emp) 
        .where(emp.sal.eq( 
JPA__EXPRESSION__s 
      .select(e.sal.max()).from(e)       .where(emp.dept.deptno.eq(e.dept.deptno)) 
))
.fetch();
return emps; 


 
public List<Emp> selectEmpGreaterThanAvgSal() { 
QEmp e = new QEmp("e"); 
List<Emp> emps = queryFactory.selectFrom(emp) 
.where(emp.sal.gt( 
JPA__EXPRESSION__s 
      .select(e.sal.avg()).from(e)       .where(emp.dept.deptno.eq(e.dept.deptno)) 
))
.fetch();
return emps; 


 
 
public List<Emp> selectEmpEqualsEmpno(Long empno) { 
QEmp e = new QEmp("e"); 
List<Emp> emps = queryFactory.selectFrom(emp) 
.where(emp.sal.eq( 
JPA__EXPRESSION__s 
      .select(e.sal).from(e)       .where(e.empno.eq(empno)) 
))
.where(emp.empno.ne(empno)) 
.fetch();
return emps; 


 
public List<Emp> selectEmpMaxSalTop3() { 
List<Emp> emps = queryFactory.selectFrom(emp) 
.orderBy(emp.sal.desc()) 
.limit(3) 
.fetch();
return emps; 


 
public List<String> selectDeptExistsEmp() { 
List<String> depts = queryFactory.select(dept.dname).from(dept) 
.where(JPA__EXPRESSION__s 
      .selectFrom(emp)
      .where(emp.dept.deptno.eq(dept.deptno)).exists() 
                        ) 
.fetch();
return depts; 
} 
[JPA,Querydsl강좌]스프링에서Querydsl,SQLQueryFactory생성하기

        @Autowired 
DataSource dataSource; 

@Transactional() 
public void run(String... args) { 
///////// 


public PlatformTransactionManager transactionManager() { 
return new DataSourceTransactionManager(dataSource); 


public Configuration querydslConfiguration() { 
SQLTemplates templates = MySQLTemplates.builder().build(); 
Configuration configuration = new Configuration(templates); 
configuration.setExceptionTranslator(new SpringExceptionTranslator()); 
return configuration; 


public SQLQueryFactory queryFactory() { 
Provider<Connection> provider = new SpringConnectionProvider(dataSource); 
return new SQLQueryFactory(querydslConfiguration(), provider); 
} 
[JPA팁] @ManyToMany 설정, 삭제 작업 시 의도치 않은 테이블 정보가 삭제 될 때-송석원

User 테이블과 Authority 테이블 n:m 관계이다.
이를 처리하기 위해 @ManyToMany 애노테이션을 사용하고 있다.

이에 따라 테이블 3개가 만들어진다.
User - User_Authority - Authority

증상
User 정보를 삭제 시 User, User_Authority 정보가 삭제된다. 이는 원하는 결과다.
그런데, 원하지 않는 Authority 정보도 삭제가 되고 있다. 이러면 큰 사고다.

원인
@ManyToMany 설저에 CascadeType.ALL을 설정하고 있다.
그에 따라 삭제시 캐스케이드 작업이 발생한다.
User --> User_Authority --> Authority 

해결
이를 CascadeType.PERSIST,CascadeType.MERGE 으로 변경한다.
정보의 수정은 사실 PERSIST로 충분하다. 지나친 설정은 피하자.
추가로, User, Authority 클래스 모두에 @JoinTable설정을 한다.

User
--------------------
@ManyToMany(fetch=FetchType.EAGER, cascade={CascadeType.PERSIST,CascadeType.MERGE})
@JoinTable(name = "UserAuthority",
    joinColumns = @JoinColumn(name = "user_id"),
    inverseJoinColumns = @JoinColumn(name = "authority_id"))
private Set<Authority> authorities = new HashSet<>();

Authority 
--------------------
@ManyToMany(fetch=FetchType.LAZY, cascade={CascadeType.PERSIST,CascadeType.MERGE})
@JoinTable(name = "UserAuthority",
    joinColumns = @JoinColumn(name = "authority_id"),
    inverseJoinColumns = @JoinColumn(name = "user_id"))
private Set<User> users = new HashSet<>();

결론
해결책은 비 연관관계 주인이었던 엔티티도 연관관계 주인으로 만드는 것이다.
[JPA강좌]스프링부트,Data JPA, MVC 심플 예제입니다.

Spring Data JPA Simple Example

n  Spring Boot, WEB MVC, Spring Data JPA, Maria DB를 이용하여 간단히 예제를 만들어 보자.
 첨부파일 참조하세요~

감사합니다.
[JPA/Spring Data JPA]SQL쿼리방법정리,예문(JPQL,Querydsl,Criteria Query,NativeSQL,Query Method,@Query), JPA 및 Spring Data JPA에서 쿼리 하는 방법에 대해 그림으로 요약했으며 간단히 예문을 추가 했습니다. 

참조하세요~ 열공하시구요, 

감사합니다. 
[JPA강좌]OneToOne 단방향,주테이블에 외래키가 있는 경우(마리아DB) 개요및예제입니다.

- 첨부 파일 참조하세요.

[JPA강좌]N:1양방향연관관계,@ OneToMany, @ManyToOne, @JoinTable, @OnDelete, @ForeignKey,엔티티매핑실습
n  테이블 간의 관계는 N:1 로 동일하지만 방향성이 양방향 이다이전 단방향 에서 추가적으로 DEPT 쪽에서 EMP를 참조하므로 양방향 관계가 된다.
n  @OneToMany쪽에 mappedBy 속성으로 Owner테이블(외래키 테이블)의 필드(속성)을 지정하면 된다.(Owner쪽은@ManyToOne이 된다.) 양방향 관계에서 관계 연결처리를 하는 쪽을 Owner라 한다. (외래키가 있는 쪽)
첨부 파일 참조 하세요.
[스프링JPA교육]WEB MVC,@NamedQuery,@Query,메소드 이름을 통해 쿼리를 정의하는 예제(마리아DB이용한예제)

다양한 형태의 Spring Data JPA 쿼리 예제를 실습해 보세요...

천천히 하나씩 따라 하시면서 스프링 Data JPA에 대해서 이해해 보세요~

[스프링JPA강좌1대1양방향,주테이블에 외래키,@OneToOne, mappedBy, @JoinColumn, CascadeType.ALL

첨부 파일 참조하세요.
JPA,Queryr강좌(QueryDSL4.X) – distinct , where(and, or), 정렬(SORT)

[JPA연관관계매핑]복합키와 식별관계,비식별관례,엔티티매핑
 
엔티티 매핑(Entity Mapping) – 연관관계
 
n  두 엔티티가 연관을 맺는 연관관계에는 방향성(direction), 다양()(multiplicity), 주체(owner)라는 개념이 존재한다.
ü  Direction : 두 객체가 관계를 맺을 때 한쪽에서만 참조하는 경우 단 방향이라 하고 양쪽 객체가 서로 참조하는 경우를 양방향 연관관계라고 한다.
ü  Multiplicity : 하나의 객체가 참조하는 객체가 한 개한 개 이상 등 다양성이 있는데 1:1, 1:N, M:N관계가 있다.
ü  Owner : 연관관계에서의 주인이 되는 객체
 
2-4-1. 복합키와 비식별 관계
 
n  외래키가 기본키에 포함되는지 여부에 따라 식별관계비식별 관계로 구별할 수 있다.
n  아래의 경우 부모테이블의 키가 자식테이블에 참조되는데 비 식별관계이다부모테이블의 PK가 자식테이블에 외래키(FK)로 참조될 때 기본키(PK)로 내려가는 것이 아니라 일반 칼럼으로 참조되는 것이다.
 

 
:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />
@Entity
public class Parent {
   @EmbeddedId
   protected ParentKey pkey;
          
   @Embeddedable
   public static class ParentKey implements Serializable
   {                
      @Column
      private String id1;
      @Column
      private String id2;            
      //getter, setter, equals, hashCode는 구현해야 한다.
   }
}
 
@Entity
public class Child {
   @Id
   private String id;
          
   @ManyToOne
   @JoinColumns({     
       @JoinColumn(name="parent_id1",referencedColumnName="id1"),
       @JoinColumn(name="parent_id2", referencedColumnName="id2")
    })
   private Parent parent;
}
 
 
 
2-4-2. 복합키와 식별 관계
 
n  아래의 경우 부모테이블의 키가 자식테이블의 칼럼으로 참조되는데 식별관계이다즉 부모테이블의 PK가 자식테이블에 외래키(FK)로 참조될 때 자식의 기본키(PK)로 참조된다.
n  @EmbeddedId를 식별관계로 매핑하기 위해서는 @MapsId를 지정해야 하고 속성값으로는 @Embeddedable로 지정된 식별자에서 참조하는 연관객체의 기본키를 지정하면 된다.
 

 
@Entity
public class Child {
   @EmbeddedId
   private ChildKey id;
          
   @ManyToOne
   @JoinColumns({
     @JoinColumn(name="parent_id1", referencedColumnName="id1"),
     @JoinColumn(name="parent_id2", referencedColumnName="id2")
   })
   //Embeddedable로 지정한 식별자의 기본키필드지정
   //PK(키값)으로 매핑됨을 의미한다.
   @MapsId("parentKey") 
   private Parent parent;
 
@Embeddedable
public static class ChildKey implements Serializable {
   private ParentKey parentKey;          
    @Column
    private String id;
    //getter, setter, equals, hashCode는구현해야
   }
}

[JPA강좌,Querydsl쿼리사용법정리]쿼리(SQL)사용하는방법정리(JPQL, Querydsl, Creteria쿼리,Native SQL)

첨부파일 확인 부탁드립니다.

[JPA,JPQL강좌]Spring Data JPA에서 Native SQL사용하기,@Query 어노테이션에 nativeQuery=true라고 하면 된다. 물론 DB에서 사용하는 SQL형식으로 쿼리를 사용할 수 있다.

//제너릭 인터페이스
public interface EmpRepository<T> extends Repository<T, Long>
{
//Native SQL, SQL구문은 JPQL형태가 아니라 DB에서 사용하는 SQL형식을 쓰면 된다.
//nativeQuery 값의 default false
//#entityName SpEL 표현이며 위 Repository<T, Long> T객체를 가리킨다.
           @Query(value="select * from #{#entityName} e where e.ename = ?1",
                        nativeQuery=true)
           List<Emp> findByEname(String ename);
          
// 아래는 Native SQL 아님
           @Query(value="select ename, job, sal from Emp e where e.sal > ?1 and e.sal < ?2 ")
           List<Emp> findBySalRange(Long sal1, Long sal2);
}
[스프링JPA교육기초실습]QueryDSL4.0.8,기본쿼리,서브쿼리,조인,페이징@Query,NamedQuery실습,PageRequest,JPAQuery,JPA__EXPRESSION__s,limit,fetch,exists,innerJoin,JPAUpdateClause,JPADeleteClause,orderBy 

첨부 파일 참조하세요. 

Spring Boot, Spring Data JPA, Spring WEB MVC, QueryDSL @NamedQuery, @Query, 메소드 이름으로 쿼리생성(Query Method), 페이징처리, 서브쿼리, 조인 기본 예제를 마리아DB를 이용하여 작성해 보자. 

QueryDSL  MAVEN 설정은 아래 URL에서 참조 
http://ojc.asia/bbs/board.php?bo_table=LecSpring&wr_id=543 

마리아 DB 설치는 다음 URL 참조 
http://ojc.asia/bbs/board.php?bo_table=LecSpring&wr_id=524 

롬복(Lombok)설치는 다음 URL 참조 
http://ojc.asia/bbs/board.php?bo_table=LecSpring&wr_id=561 

감사합니다. 
[스프링JPA강좌,Querydsl]SQLQueryFactory(MAVEN설정,서브쿼리,조인,orderby,groupBy, insert/update 배치쿼리,partitionBy, over), 스프링부트

첨부 파일 참조하셔서 천천히 따라해 보세요~ 
감사합니다. 
[JPA교육,엔티티매핑]N:1단방향 연관관계,@ManyToOne, @JoinTable, @OnDelete, @ForeignKey

n  사원부서관계 처럼 N:1 형태의 관계를 이야기 하는데 Emp에서 Dept의 칼럼을 참조하는 경우이다. (하나의 부서에는 사원이 N하나의 사원은 하나의 부서에만 속하는 관계)

첨부 파일 참조하세요.
감사합니다.
[JPA,Querydsl강좌]SQLQueryFactory,JPA,조인,서브쿼리,그룹핑,정렬예문(JPA, NativeSQL) 

SQLQueryFactory를 이용하면 자바쪽에 엔티티를 만들지 않고 DB로 쿼리 할 수 있다. 
(물론 이를 위해 쿼리타입이라는 클래스는 만들어져 있어야 한다.) 

기존 NativeSQL을 이용하여 DB의 SQL구문형식대로 만들어 쿼리했던 방식을 JPA의 메소드 기반으로 쿼리할 수 있는 장점이 있다. 

간단히 예문을 보자. 

// Emp, Dept를 조인하여 사원명, 부서명 출력, 5길동은 부서가 없으므로 출력안됨 
List<Tuple> emps1 = queryFactory.select(emp.ename, dept.dname) 
.from(emp) 
.innerJoin(dept) 
.on(emp.deptno.eq(dept.deptno)) 
.fetch(); 

for (Tuple row : emps1) { 
System.out.println(row.get(emp.ename) + ":" + row.get(dept.dname)); 

System.out.println("----------------2"); 

// Emp, Dept를 조인하여 사원명, 부서명 출력, 부서없는 5길동도 출력 
List<Tuple> emps2 = queryFactory.select(emp.ename, dept.dname) 
.from(emp) 
.leftJoin(dept) 
.on(emp.deptno.eq(dept.deptno)) 
.fetch(); 

for (Tuple row : emps2) { 
System.out.println(row.get(emp.ename) + ":" + row.get(dept.dname)); 

System.out.println("----------------3"); 

// Emp 테이블에서 부서별로 그룹핑하여 부서명, 급여합 출력 
// 단 급여평균이 2000 이상인 부서만 
List<Tuple> emps3 = queryFactory.select(dept.dname, emp.sal.sum()) 
.from(emp) 
.innerJoin(dept) 
.on(emp.deptno.eq(dept.deptno)) 
.groupBy(emp.deptno) 
.having(emp.sal.avg().gt(2000))
.fetch(); 

for (Tuple row : emps3) { 
System.out.println(row.get(dept.dname) + ":" 
                    + row.get(1, Long.class)); 

System.out.println("----------------4"); 

//Emp 테이블에서 급여최소인 사원의 모든 칼럼 추출 
SEmp e = new SEmp("e"); 
List<Tuple> emps4 = queryFactory.select(emp.all()) 
                .from(emp) 
            .where(emp.sal.eq( 
              SQL__EXPRESSION__s.select(e.sal.min()).from(e))) 
            .fetch(); 
for (Tuple row : emps4) { 
System.out.println(row.get(emp.ename) + ":" 
                    + row.get(emp.sal)); 

System.out.println("----------------4"); 

//Emp 테이블에서 job별 최소급여 사원의 ename, job, sal 출력 
List<Tuple> emps5 = queryFactory.select(emp.ename, emp.job, emp.sal) 
      .from(emp) 
      .where(emp.sal.eq( 
            SQL__EXPRESSION__s.select(e.sal.min()).from(e) 
              .where(emp.job.eq(e.job)))) 
  .fetch(); 

for (Tuple row : emps5) { 
System.out.println(row.get(emp.job) + ":" 
                    + row.get(emp.ename) + ":" 
        + row.get(emp.sal)); 

System.out.println("----------------5"); 

//"1길동" 사원과 같은 부서에 있는 사원중 최대급여 사원의 이름 및 급여, 부서명출력 
List<Tuple> emps6 = queryFactory.select(emp.ename, emp.sal, dept.dname) 
.from(emp) 
.innerJoin(dept).on(emp.deptno.eq(dept.deptno)) 
.where(emp.deptno.eq( 
SQL__EXPRESSION__s 
.select(e.deptno) 
.from(e) 
.where(e.ename.eq("1길동"))
    ).and(emp.sal.eq( 
    SQL__EXPRESSION__s 
    .select(e.sal.max()) 
    .from(e) 
    .where(emp.deptno.eq(e.deptno)) 
    ))) 
                .fetch(); 

for (Tuple row : emps6) { 
System.out.println(row.get(emp.ename) + ":" 
                    + row.get(emp.sal) + ":" 
        + row.get(dept.dname)); 

System.out.println("----------------6");