Archive: 2022

0

Django - 장고 시작하기

목차 Django - 블로그 post list와 post detail 페이지에 카테고리 추가하기 Django - Comment (댓글) 작성창 구현하기 Django - Comment (댓글) view 구현하기 Django - Comment (댓글) 모델 구현하기 Django - 로그인 사용자만 접속 가능하게 하기 Django - Post 작성 화면 / 기능 구현하기 Django - Post 수정 화면 / 기능 구현하기 Django - 마크다운적용하기, tag field에 공란 허용하기 Django - Post Detail 개선사항 도출하고 Test 코드 만들기 Django - Category 페이지 만들기 (slugField) Django - 사소한 문제들 해결(불필요한 내용 삭제하기; category 복수형 수정하기 (categorys -> categories)) Django - 블로그 post에 카테고리 추가하기 Django - 블로그 post에 카테고리 추가하기 Django - 블로그 post에 카테고리 추가하기 Django - read more 버튼 동작하게 하기, post_detail 페이지 개선하기 Django - HTML 분리해서 관리하기 Django - Post Detail 페이지 테스트 코드 작성하기 Django - Post List 페이지 테스트 코드 작성하기 part A Django - Post Detail 페이지 만들기 Django - template(html)에서 if문 사용하기 Django - 사진 업로드를 위한 ImageField와 업로드된 파일 관리 방법 Django - Bootstrap Grid Django - navigation bar 만들기 Django - static File 관리하기 bootstrap 적용을 위해 Django - FBV -> MBV 블로그 포스트 리스트 Django - MTV 구조 맛보기 model, views, templates 사용하기 Django - 지역(시간) 세팅 수정하기, Post 개선하기 str Django - blog 앱 만들기 & Post 모델 만들기 Django - 장고 시작하기 장고 설치하기pip install django 장고 프로젝트 시작하기django 를 설치하면 django-admin 명령어를 사용할 수 있습니다. 설치된 django-admin 에 startproject 명령어를 사용하면 새롭게 장고 프로젝트를 시작할 수 있습니다. django-admin startproject mysite 새로운 장고 프로젝트에 들어가면 manage.py 파일이 있습니다. runserver 명령어를 사용하면 장고 서버를 실행할 수 있습니다. python manage.py runserver

0

React - TypeScript 사용하기

참고 https://create-react-app.dev/docs/adding-typescript/ React TypeScript 사용하기create react app 명령어에 --template typescript 파라미터만 추가해주면 초기 구성이 타입스크립트로 설정되는 것을 확인할 수 있다. # npm 사용npx create-react-app my-app --template typescript# yarn 사용yarn create react-app my-app --template typescript

0

Spring Web Socket - STOMP (Simple Text Oriented Message Protocol)

목차 Spring Web Socket - Chat 프로그램 만들기 1 Spring Web Socket - STOMP (Simple Text Oriented Message Protocol) 네트워크 - Web Socket 참고 https://docs.spring.io/spring-framework/docs/4.3.x/spring-framework-reference/html/websocket.html https://www.baeldung.com/websockets-spring STOMP (Simple/Stream Text Oriented Message Protocol) WebSocket 기반 프로토콜이며 Client 와 Server 가 negotiate 하기 위한 sub-protocol 이 정의 돼 있다. STOMP 은 메세징 전송을 효율적으로 하기 위해 탄생한 Text 지향 프로토콜 이며 Message Payload에는 Text or Binary 데이터를 포함 해 전송할 수 있다. 또한 sub-protocol 이 정의 돼 있어 Client 에서 서버로 전송할 메시지 유형, 형식, 내용등이 정의 돼 있다. pub/sub 구조로 되어있어 메세지를 전송하고 메세지를 받아 처리하는 부분이 확실히 정해져 있기 때문에 개발자 입장에서 명확하게 인지하고 개발할 수 있는 이점이 있다. 또한 STOMP를 이용하면 메세지의 헤더에 값을 줄 수 있어 헤더 값을 기반으로 통신 시 인증 처리(ChannelInterceptor) 를 구현하는 것도 가능하며 STOMP 스펙에 정의한 규칙만 잘 지키면 여러 언어 및 플랫폼 간 메세지를 상호 운영할 수 있다. 만약 Spring에서 지원하는 STOMP를 사용하면 Spring WebSocket 어플리케이션은 STOMP Broker로 동작하게 된다. STOMP (Simple Text Oriented Message Protocol) 구성 SimpAnnotationMethod Client 로 부터 전달 받은 Message 를 처리한다. clientInboundChannel WebSocket client 로부터 전달 받은 메시지를 전송해준다. clientOutboundChannel Server 메시지를 WebSocket Client 에 전송해준다. brokerChannel Server 내부에서 사용하는 Channel, Message Broker 에 메시지를 전송해준다.

0

Spring boot - StreamingResponseBody

목차 Spring boot - StreamingResponseBody Spring boot - ResourceRegion Spring boot - 파일 다운로드 서비스 구현하기 Spring boot - 파일 업로드 서비스 구현하기 Spring boot - Resource 추상화 Spring boot - MultipartFile 에서 발생하는 예외 처리 Spring boot - MultipartFile 를 이용한 파일 업로드 Spring boot - Part 객체를 이용한 파일 업로드 참고 https://www.baeldung.com/spring-mvc-sse-streams StreamingResponseBody Spring 에서 제공하는 비동기 요청 처리를 위한 객체, 응답값을 Byte 로 변환해 Streaming 형태로 줄 때 사용하는 객체 @GetMapping("/video3")public ResponseEntity<StreamingResponseBody> handleRbe() throws FileNotFoundException { File file = new File("/Users/dongwoo-yang/spring-file/mysong.mp4"); InputStream inputStream = new FileInputStream(file); StreamingResponseBody stream = out -> { byte[] data = new byte[1024]; int length = 0; while((length = inputStream.read(data)) >= 0){ out.write(data, 0, length); } inputStream.close(); out.flush(); }; HttpHeaders headers = new HttpHeaders(); headers.add("Content-Type", "video/mp4"); headers.add("Content-Length", Long.toString(file.length())); return ResponseEntity.ok().headers(headers).body(stream);}

0

Spring boot - Actuator + Prometheus + Grafana

목차 Spring boot - Actuator + Prometheus + Grafana Spring boot - 메시지 국제화 MessageSource Spring Boot - WebMvcConfigurer Spring Boot - 자동 설정 이해 참고 https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#actuator https://www.baeldung.com/spring-boot-actuators https://docs.spring.io/spring-boot/docs/2.1.8.RELEASE/reference/html/production-ready.html https://covenant.tistory.com/244 https://devbksheen.tistory.com/182 Spring boot - Actuator Actuator 는 별도의 구현 없이 Metric 정보, 트래픽 정보, 데이터 베이스 등 운영환경에서 Application 상태 정보에 접근 할 수 있는 기능을 제공하고, HTTP 와 JMX 를 이용해 접근할 수 있다. https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#actuator.endpoints 공식 문서에서 Actuator 가 지원하는 End Point 를 확인할 수 있다. Actuator 의존성 추가implementation 'org.springframework.boot:spring-boot-starter-actuator' 의존성을 추가한 뒤 Spring Application 을 실행 시킨 후 http://localhost:8080/actuator 로 접속하면 현재 Application 에서 End Point 들을 확인할 수 있다.

0

Spring boot - ResourceRegion

목차 Spring boot - StreamingResponseBody Spring boot - ResourceRegion Spring boot - 파일 다운로드 서비스 구현하기 Spring boot - 파일 업로드 서비스 구현하기 Spring boot - Resource 추상화 Spring boot - MultipartFile 에서 발생하는 예외 처리 Spring boot - MultipartFile 를 이용한 파일 업로드 Spring boot - Part 객체를 이용한 파일 업로드 Http Range Request 서버에서 클라이언트로 HTTP 메시지 중 일부만 전송할 수 있도록 허용하는 기술 대용량의 미디어 파일, 파일 전송 중 일시 정지 및 다시 시작이 가능하다 Client 가 Range Header 를 통해 특정 리소스의 일부를 요청하면 서버가 그 부분만 전송하는 방식으로 동작한다. Server 가 Range Request 를 지원하면 Response Http Header 에 Content-Range 가 존재한다. HTTP Range 요청에 대한 정상 응답 코드로는 PARTIAL_CONTENT(206) 을 반환한다. Range 요청에 대한 응답 값이 Body 에 담겨져 있다. Spring boot - ResourceRegion HttpRange 는 Range Header 정보를 담든 객체다. Request Header 로부터 정보를 얻어올 수 있다. ResourceRegion 는 전달 받은 Resource 객체 로부터 Range 범위 만큼 나눠 가져오는 객체다. @RestController@Slf4jpublic class VideoController { @GetMapping(value = "/video") public ResponseEntity<ResourceRegion> streamVideo(@RequestHeader HttpHeaders headers) throws IOException { UrlResource video = new UrlResource("file:/Users/dongwoo-yang/spring-file/mysong.mp4"); ResourceRegion resourceRegion; final long size = 1000000L; long contentLength = video.contentLength(); Optional<HttpRange> optional = headers.getRange().stream().findFirst(); HttpRange httpRange; if (optional.isPresent()) { httpRange = optional.get(); long start = httpRange.getRangeStart(contentLength); long end = httpRange.getRangeEnd(contentLength); long rangeLength = Long.min(size, end - start + 1); resourceRegion = new ResourceRegion(video, start, rangeLength); } else { long rangeLength = Long.min(size, contentLength); resourceRegion = new ResourceRegion(video, 0, rangeLength); } return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT) .contentType(MediaTypeFactory.getMediaType(video).orElse(MediaType.APPLICATION_OCTET_STREAM)) .body(resourceRegion); }} 서버에서 전송해주는 Resource Size 가 1 MB(1000000) 로 잡혀 있어 1 MB 씩 부분적으로 응답받는 것을 확인할 수 있다.

0

Redis

RedisRedis 특징 Key-Value Store Collection 지원 Pub/Sub 지원 디스크 저장 Replication (복제) Redis 는 싱글 스레드다.

0

데이터 베이스 - Index

Index 란?인덱스는 데이터베이스에서 테이블의 검색 속도를 높이기 위한 자료 구조 입니다. 인덱스는 테이블의 컬럼을 기반으로 만들어지며, 이를 사용하여 특정 데이터를 빠르게 찾을 수 있습니다. 일반적으로 인덱스는 B-트리나 해시 테이블과 같은 자료 구조를 사용하여 구현됩니다. B-트리 인덱스는 매우 일반적인 인덱스 유형으로, 테이블의 키 값이 정렬된 트리 구조를 사용하여 저장됩니다. 이 구조는 검색, 삽입 및 삭제 작업에 대해 매우 효율적입니다. 해시 인덱스는 테이블의 키 값을 해시 함수를 사용하여 저장하며, 빠른 검색 속도를 제공하지만 범위 검색이나 정렬 기능이 없습니다. 인덱스를 사용하면 특정 행을 검색하는 데 걸리는 시간이 줄어듭니다. 특히 대량의 데이터가 있는 경우 검색 성능을 향상시키는 데 매우 유용합니다. 그러나 인덱스는 테이블의 크기와 관련하여 저장 공간을 차지하므로 인덱스를 너무 많이 사용하면 데이터베이스 성능이 떨어질 수 있습니다. 또한 인덱스를 사용하면 데이터를 삽입, 수정 또는 삭제할 때 추가 작업이 필요하므로 데이터베이스 성능에 영향을 미칠 수 있습니다. 따라서 인덱스는 데이터베이스의 검색 성능을 향상시키는 데 매우 유용하지만, 사용에 주의해야 합니다. 적절한 인덱스를 사용하면 데이터베이스의 성능을 최적화할 수 있습니다. Index Scan 종류 Table Full Scan : 테이블 전체 탐색 Index Unique Scan : 인덱스 수직 탐색 Index Full Sacn : 인덱스 전체 탐색 Index Range Scan : 인덱스 범위 탐색 Index Skip Scan : 인덱스 스킵 탐색 Table Full Scan - 테이블 전체 스캔 전체 테이블의 모든 레코드를 검색하는 방식

0

Spring Data JPA - 벌크성 수정 쿼리

목차 Spring Data JPA - 벌크성 수정 쿼리 Spring Data JPA - Convertor Spring Data JPA - Auditing Spring Data JPA - Paging Request Paramater Spring Data JPA - 페이징과 정렬 Post not found: spring/spring-data-jpa/07-spring-data-jpa Spring Data JPA - 반환 타입 Spring Data JPA - Query 파라미터 바인딩 Spring Data JPA - Query 를 이용한 조회 결과를 특정 값으로 반환하기 Spring Data JPA - JPQL (Java Persistence Query Lange) 사용하기 Spring Data JPA - 메소드 이름으로 쿼리 생성하기 Spring Data JPA - 시작하기 벌크성 수정 쿼리public int bulkAgePlus(int age) { int resultCount = em.createQuery("update Member m set m.age = m.age + 1 where m.age >= :age") .setParameter("age", age) .executeUpdate(); return resultCount;} @Testpublic void bulkUpdate(){ memberJpaRepository.save(new Member("member1", 10)); memberJpaRepository.save(new Member("member2", 19)); memberJpaRepository.save(new Member("member3", 20)); memberJpaRepository.save(new Member("member4", 21)); memberJpaRepository.save(new Member("member5", 40)); int resultCount = memberJpaRepository.bulkAgePlus(20); assertThat(resultCount).isEqualTo(3);} Spring JPA 사용하기@Modifying@Query("update Member m set m.age = :age+1 where m.age >= :age")int bulkAgePlus(@Param("age") int age); 벌크성 쿼리는 영속성 Context를 무시하고 바로 DB에 업데이트를 진행 시키기 때문에 벌크성 쿼리를 수행한 후에는 영속성 Context를 초기화 해줄 필요가 있다. 안그러면 기존에 남아있는데이터가 꼬여서 문제가 발생할 수 있다. @Testpublic void bulkUpdate(){ memberRepository.save(new Member("member1", 10)); memberRepository.save(new Member("member2", 19)); memberRepository.save(new Member("member3", 20)); memberRepository.save(new Member("member4", 21)); memberRepository.save(new Member("member5", 40)); int resultCount = memberRepository.bulkAgePlus(20); // 벌크성 쿼리를 실행한 후 영속성 컨텍스트를 날려준다. entityManager.flush(); entityManager.clear(); assertThat(resultCount).isEqualTo(3);}

0

QueryDSL - QueryDsl 페이징 사용하기

목차 QueryDSL - QueryDsl 페이징 사용하기 QueryDSL - 사용자 정의 Repository QueryDSL - 동적 쿼리와 성능 최적화 조회 QueryDSL - 순수 JPA 리포지토리와 Querydsl QueryDSL - SQL Function 호출하기 QueryDSL - 수정, 삭제 벌크 연산 QueryDSL - 동적 쿼리 QueryDSL - 프로젝션과 결과 반환 QueryDSL - 조인 QueryDSL - 집계 QueryDSL - 결과 조회 QueryDSL - 검색 조건 쿼리 QueryDSL - Q-Type 활용 QueryDSL - JPA JPQL vs JPA QueryDSL QueryDSL 시작하기 Querydsl 페이징public interface MemberRepositoryCustom { List<MemberTeamDto> search(MemberSearchCondition condition); Page<MemberTeamDto> searchPageSimple(MemberSearchCondition condition, Pageable pageable); Page<MemberTeamDto> searchPageComplex(MemberSearchCondition condition, Pageable pageable);} QueryDSL 에서 제공하는 fetchResults 를 사용하게 되면 데이터를 가져오는 쿼리 와 가져온 데이터 수를 확인하는 Count 쿼리 총 2개의 쿼리가 발생하게 된다. @Overridepublic Page<MemberTeamDto> searchPageSimple(MemberSearchCondition condition, Pageable pageable) { QueryResults<MemberTeamDto> results = queryFactory .select(new QMemberTeamDto( member.id.as("memberId"), member.username, member.age, team.id.as("teamId"), team.name.as("teamName") )) .from(member) .leftJoin(member.team, team) .where( usernameEq(condition.getUsername()), teamNameEq(condition.getTeamName()), ageGoe(condition.getAgeGoe()), ageLoe(condition.getAgeLoe()) ) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) .fetchResults(); // fetchResults 를 사용하게 되면 count 쿼리가 같이 나가게 된다. List<MemberTeamDto> content = results.getResults(); long total = results.getTotal(); return new PageImpl<>(content, pageable, total);} Querydsl 페이징 쿼리와 Count 쿼리 분리@Overridepublic Page<MemberTeamDto> searchPageComplex(MemberSearchCondition condition, Pageable pageable) { // 전체 내용을 가져오는 쿼리 List<MemberTeamDto> content = queryFactory .select(new QMemberTeamDto( member.id.as("memberId"), member.username, member.age, team.id.as("teamId"), team.name.as("teamName") )) .from(member) .leftJoin(member.team, team) .where( usernameEq(condition.getUsername()), teamNameEq(condition.getTeamName()), ageGoe(condition.getAgeGoe()), ageLoe(condition.getAgeLoe()) ) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) .fetch(); // 전체 Count 를 가져오는 쿼리 long total = queryFactory .select(ExpressionUtils.count(member)) .from(member) .leftJoin(member.team, team) .where( usernameEq(condition.getUsername()), teamNameEq(condition.getTeamName()), ageGoe(condition.getAgeGoe()), ageLoe(condition.getAgeLoe()) ) .fetchCount(); return new PageImpl<>(content, pageable, total);} 스프링 데이터 페이징 활용2 - CountQuery 최적화

0

QueryDSL - 사용자 정의 Repository

목차 QueryDSL - QueryDsl 페이징 사용하기 QueryDSL - 사용자 정의 Repository QueryDSL - 동적 쿼리와 성능 최적화 조회 QueryDSL - 순수 JPA 리포지토리와 Querydsl QueryDSL - SQL Function 호출하기 QueryDSL - 수정, 삭제 벌크 연산 QueryDSL - 동적 쿼리 QueryDSL - 프로젝션과 결과 반환 QueryDSL - 조인 QueryDSL - 집계 QueryDSL - 결과 조회 QueryDSL - 검색 조건 쿼리 QueryDSL - Q-Type 활용 QueryDSL - JPA JPQL vs JPA QueryDSL QueryDSL 시작하기 참고 https://docs.spring.io/spring-data/jpa/docs/2.1.3.RELEASE/reference/html/#repositories.custom-implementations 사용자 정의 Repository 사용법 사용자 정의 인터페이스 작성 사용자 정의 인터페이스 구현 스프링 데이터 Repository 에 사용자 정의 인터페이스 상속 1. 사용자 정의 인터페이스 작성public interface MemberRepositoryCustom { List<MemberTeamDto> search(MemberSearchCondition condition);} 2. 사용자 정의 인터페이스 구현

0

QueryDSL - 순수 JPA 리포지토리와 Querydsl

목차 QueryDSL - QueryDsl 페이징 사용하기 QueryDSL - 사용자 정의 Repository QueryDSL - 동적 쿼리와 성능 최적화 조회 QueryDSL - 순수 JPA 리포지토리와 Querydsl QueryDSL - SQL Function 호출하기 Post not found: jpa/querydsl/querydsl-10 QueryDSL - 동적 쿼리 QueryDSL - 프로젝션과 결과 반환 QueryDSL - 조인 QueryDSL - 집계 QueryDSL - 결과 조회 QueryDSL - 검색 조건 쿼리 QueryDSL - Q-Type 활용 QueryDSL - JPA JPQL vs JPA QueryDSL QueryDSL 시작하기 순수 JPA 리포지토리와 Querydsl@Repositorypublic class MemberJpaRepository { private final EntityManager em; private final JPAQueryFactory queryFactory; public MemberJpaRepository(EntityManager em){ this.em = em; this.queryFactory = new JPAQueryFactory(em); } public void save(Member member){ em.persist(member); } public Optional<Member> findById(Long id){ Member findMember = em.find(Member.class, id); return Optional.ofNullable(findMember); } public List<Member> findAll(){ return em.createQuery("select m from Member m", Member.class) .getResultList(); } public List<Member> findByUsername(String username){ return em.createQuery("select m from Member m where m.username = :username", Member.class) .setParameter("username", username) .getResultList(); }} SpringBootTest@Transactionalclass MemberJpaRepositoryTest { @Autowired EntityManager em; @Autowired MemberJpaRepository memberJpaRepository; @Test public void basicTest(){ Member member = new Member("member1", 10); memberJpaRepository.save(member); Member findMember = memberJpaRepository.findById(member.getId()).get(); assertThat(findMember).isEqualTo(member); List<Member> result = memberJpaRepository.findAll(); assertThat(result).containsExactly(member); List<Member> result2 = memberJpaRepository.findByUsername("member1"); assertThat(result2).containsExactly(member); }} JPQL QueryDsl 로 변경public List<Member> findAll(){ return em.createQuery("select m from Member m", Member.class) .getResultList();}public List<Member> findAll_Querydsl(){ return queryFactory .selectFrom(member) .fetch();}public List<Member> findByUsername(String username){ return em.createQuery("select m from Member m where m.username = :username", Member.class) .setParameter("username", username) .getResultList();}public List<Member> findByUsername_Querydsl(String username){ return queryFactory .selectFrom(member) .where(member.username.eq(username)) .fetch();} @Test public void basicQueryDsl(){ Member member = new Member("member1", 10); memberJpaRepository.save(member); Member findMember = memberJpaRepository.findById(member.getId()).get(); assertThat(findMember).isEqualTo(member); List<Member> result = memberJpaRepository.findAll_Querydsl(); assertThat(result).containsExactly(member); List<Member> result2 = memberJpaRepository.findByUsername_Querydsl("member1"); assertThat(result2).containsExactly(member); }

0

QueryDSL - 동적 쿼리와 성능 최적화 조회

목차 QueryDSL - QueryDsl 페이징 사용하기 QueryDSL - 사용자 정의 Repository QueryDSL - 동적 쿼리와 성능 최적화 조회 QueryDSL - 순수 JPA 리포지토리와 Querydsl QueryDSL - SQL Function 호출하기 QueryDSL - 수정, 삭제 벌크 연산 QueryDSL - 동적 쿼리 QueryDSL - 프로젝션과 결과 반환 QueryDSL - 조인 QueryDSL - 집계 QueryDSL - 결과 조회 QueryDSL - 검색 조건 쿼리 QueryDSL - Q-Type 활용 QueryDSL - JPA JPQL vs JPA QueryDSL QueryDSL 시작하기 동적 쿼리와 성능 최적화 조회 - Builder 사용public List<MemberTeamDto> searchByBuilder(MemberSearchCondition condition){ BooleanBuilder builder = new BooleanBuilder(); if (StringUtils.hasText(condition.getUsername())) { builder.and(member.username.eq(condition.getUsername())); } if(StringUtils.hasText(condition.getTeamName())){ builder.and(team.name.eq(condition.getUsername())); } if(condition.getAgeGoe()!=null){ builder.and(member.age.goe(condition.getAgeGoe())); } if(condition.getAgeLoe()!=null){ builder.and(member.age.loe(condition.getAgeLoe())); } return queryFactory .select(new QMemberTeamDto( member.id.as("memberId"), member.username, member.age, team.id.as("teamId"), team.name.as("teamName") )) .from(member) .leftJoin(member.team, team) .where(builder) .fetch();} @Testpublic void searchTest(){ Team teamA = new Team("teamA"); Team teamB = new Team("teamB"); em.persist(teamA); em.persist(teamB); Member member1 = new Member("member1", 10, teamA); Member member2 = new Member("member2", 20, teamA); Member member3 = new Member("member3", 30, teamB); Member member4 = new Member("member4", 40, teamB); em.persist(member1); em.persist(member2); em.persist(member3); em.persist(member4); MemberSearchCondition condition = new MemberSearchCondition(); condition.setAgeGoe(35); condition.setAgeLoe(40); condition.setTeamName("teamB"); List<MemberTeamDto> result = memberJpaRepository.searchByBuilder(condition); assertThat(result).extracting("username").containsExactly("member4");} /* select member1.id as memberId, member1.username, member1.age, team.id as teamId, team.name as teamName from Member member1 left join member1.team as team where team.name = ?1 and member1.age >= ?2 and member1.age <= ?3 */ select member0_.member_id as col_0_0_, member0_.username as col_1_0_, member0_.age as col_2_0_, team1_.team_id as col_3_0_, team1_.name as col_4_0_ from member member0_ left outer join team team1_ on member0_.team_id=team1_.team_id where team1_.name=? and member0_.age>=? and member0_.age<=? 동적 쿼리와 성능 최적화 조회 - where 절 파라미터 사용public List<MemberTeamDto> searchByBuilder(MemberSearchCondition condition){ BooleanBuilder builder = new BooleanBuilder(); if (StringUtils.hasText(condition.getUsername())) { builder.and(member.username.eq(condition.getUsername())); } if(StringUtils.hasText(condition.getTeamName())){ builder.and(team.name.eq(condition.getUsername())); } if(condition.getAgeGoe()!=null){ builder.and(member.age.goe(condition.getAgeGoe())); } if(condition.getAgeLoe()!=null){ builder.and(member.age.loe(condition.getAgeLoe())); } return queryFactory .select(new QMemberTeamDto( member.id.as("memberId"), member.username, member.age, team.id.as("teamId"), team.name.as("teamName") )) .from(member) .leftJoin(member.team, team) .where(builder) .fetch();} @Test public void searchTest(){ Team teamA = new Team("teamA"); Team teamB = new Team("teamB"); em.persist(teamA); em.persist(teamB); Member member1 = new Member("member1", 10, teamA); Member member2 = new Member("member2", 20, teamA); Member member3 = new Member("member3", 30, teamB); Member member4 = new Member("member4", 40, teamB); em.persist(member1); em.persist(member2); em.persist(member3); em.persist(member4); MemberSearchCondition condition = new MemberSearchCondition(); condition.setAgeGoe(35); condition.setAgeLoe(40); condition.setTeamName("teamB"); List<MemberTeamDto> result = memberJpaRepository.searchByBuilder(condition); assertThat(result).extracting("username").containsExactly("member4"); }

0

QueryDSL - 수정, 삭제 벌크 연산

목차 QueryDSL - QueryDsl 페이징 사용하기 QueryDSL - 사용자 정의 Repository QueryDSL - 동적 쿼리와 성능 최적화 조회 QueryDSL - 순수 JPA 리포지토리와 Querydsl QueryDSL - SQL Function 호출하기 QueryDSL - 수정, 삭제 벌크 연산 QueryDSL - 동적 쿼리 QueryDSL - 프로젝션과 결과 반환 QueryDSL - 조인 QueryDSL - 집계 QueryDSL - 결과 조회 QueryDSL - 검색 조건 쿼리 QueryDSL - Q-Type 활용 QueryDSL - JPA JPQL vs JPA QueryDSL QueryDSL 시작하기 수정, 삭제 벌크 연산 Bulk 연산은 영속성 Context 상태를 무시 하고 쿼리가 발생하기 때문에 영속성 Context 와 DB 상태가 다르게 된다. UPDATE, DELETE 와 같은 대량의 데이터를 한번에 처리하는 Bulk 연산을 진행하게 되면 JPA 가 영속성 컨텍스트에 저장된 엔티티를 우회하고 직접 데이터베이스에 접근하기 때문에, 영속성 컨텍스트에 저장된 엔티티 상태와 DB 상태가 다르게 됩니다. 따라서 Bulk 연산시에는 영속성 컨텍스트와 DB 상태간의 데이터 동기화에 주의를 해야 합니다. 수정@Testpublic void bulkUpdate(){ long count = queryFactory .update(member) .set(member.username, "비회원") .where(member.age.lt(28)) .execute();} /* update Member member1 set member1.username = ?1 where member1.age < ?2 */ update member set username=? where age<? @Testpublic void bulkUpdate(){ long count = queryFactory .update(member) .set(member.username, "비회원") .where(member.age.lt(28)) .execute(); List<Member> result = queryFactory .selectFrom(member) .fetch(); for (Member member1 : result) { System.out.println("member1 = " + member1); }}

0

QueryDSL - SQL Function 호출하기

목차 QueryDSL - QueryDsl 페이징 사용하기 QueryDSL - 사용자 정의 Repository QueryDSL - 동적 쿼리와 성능 최적화 조회 QueryDSL - 순수 JPA 리포지토리와 Querydsl QueryDSL - SQL Function 호출하기 Post not found: jpa/querydsl/querydsl-10 QueryDSL - 동적 쿼리 QueryDSL - 프로젝션과 결과 반환 QueryDSL - 조인 QueryDSL - 집계 QueryDSL - 결과 조회 QueryDSL - 검색 조건 쿼리 QueryDSL - Q-Type 활용 QueryDSL - JPA JPQL vs JPA QueryDSL QueryDSL 시작하기 SQL Function 호출하기@Testpublic void sqlFunction(){ List<String> result = queryFactory .select( Expressions.stringTemplate("function('replace', {0}, {1}, {2})", member.username, "member", "M") ).from(member) .fetch(); for (String s : result) { System.out.println("s = " + s); }} @Testpublic void sqlFunction2(){ queryFactory .select(member.username) .from(member) .where(member.username.eq(Expressions.stringTemplate("function('lower', {0})", member.username)));} registerColumnType( Types.BOOLEAN, "boolean" );registerColumnType( Types.BIGINT, "bigint" );registerColumnType( Types.BINARY, "binary" );registerColumnType( Types.BIT, "boolean" );registerColumnType( Types.CHAR, "char($l)" );registerColumnType( Types.DATE, "date" );registerColumnType( Types.DECIMAL, "decimal($p,$s)" );registerColumnType( Types.NUMERIC, buildId >= 201 ? "numeric($p,$s)" : "decimal($p,$s)" );registerColumnType( Types.DOUBLE, "double" );registerColumnType( Types.FLOAT, "float" );registerColumnType( Types.INTEGER, "integer" );registerColumnType( Types.LONGVARBINARY, "longvarbinary" );// H2 does define "longvarchar", but it is a simple alias to "varchar"registerColumnType( Types.LONGVARCHAR, String.format( "varchar(%d)", Integer.MAX_VALUE ) );registerColumnType( Types.REAL, "real" );registerColumnType( Types.SMALLINT, "smallint" );registerColumnType( Types.TINYINT, "tinyint" );registerColumnType( Types.TIME, "time" );registerColumnType( Types.TIMESTAMP, "timestamp" );registerColumnType( Types.VARCHAR, "varchar($l)" );registerColumnType( Types.VARBINARY, buildId >= 201 ? "varbinary($l)" : "binary($l)" );registerColumnType( Types.BLOB, "blob" );registerColumnType( Types.CLOB, "clob" );if ( isVersion2 ) { registerColumnType( Types.LONGVARCHAR, "character varying" ); registerColumnType( Types.BINARY, "binary($l)" ); registerFunction( "str", new SQLFunctionTemplate( StandardBasicTypes.STRING, "cast(?1 as character varying)") );}// Aggregations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~registerFunction( "avg", new AvgWithArgumentCastFunction( "double" ) );// select topic, syntax from information_schema.help// where section like 'Function%' order by section, topic//// see also -> http://www.h2database.com/html/functions.html// Numeric Functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~registerFunction( "acos", new StandardSQLFunction( "acos", StandardBasicTypes.DOUBLE ) );registerFunction( "asin", new StandardSQLFunction( "asin", StandardBasicTypes.DOUBLE ) );registerFunction( "atan", new StandardSQLFunction( "atan", StandardBasicTypes.DOUBLE ) );registerFunction( "atan2", new StandardSQLFunction( "atan2", StandardBasicTypes.DOUBLE ) );registerFunction( "bitand", new StandardSQLFunction( "bitand", StandardBasicTypes.INTEGER ) );registerFunction( "bitor", new StandardSQLFunction( "bitor", StandardBasicTypes.INTEGER ) );registerFunction( "bitxor", new StandardSQLFunction( "bitxor", StandardBasicTypes.INTEGER ) );registerFunction( "ceiling", new StandardSQLFunction( "ceiling", StandardBasicTypes.DOUBLE ) );registerFunction( "cos", new StandardSQLFunction( "cos", StandardBasicTypes.DOUBLE ) );registerFunction( "compress", new StandardSQLFunction( "compress", StandardBasicTypes.BINARY ) );registerFunction( "cot", new StandardSQLFunction( "cot", StandardBasicTypes.DOUBLE ) );registerFunction( "decrypt", new StandardSQLFunction( "decrypt", StandardBasicTypes.BINARY ) );registerFunction( "degrees", new StandardSQLFunction( "degrees", StandardBasicTypes.DOUBLE ) );registerFunction( "encrypt", new StandardSQLFunction( "encrypt", StandardBasicTypes.BINARY ) );registerFunction( "exp", new StandardSQLFunction( "exp", StandardBasicTypes.DOUBLE ) );registerFunction( "expand", new StandardSQLFunction( "compress", StandardBasicTypes.BINARY ) );registerFunction( "floor", new StandardSQLFunction( "floor", StandardBasicTypes.DOUBLE ) );registerFunction( "hash", new StandardSQLFunction( "hash", StandardBasicTypes.BINARY ) );registerFunction( "log", new StandardSQLFunction( "log", StandardBasicTypes.DOUBLE ) );registerFunction( "log10", new StandardSQLFunction( "log10", StandardBasicTypes.DOUBLE ) );registerFunction( "pi", new NoArgSQLFunction( "pi", StandardBasicTypes.DOUBLE ) );registerFunction( "power", new StandardSQLFunction( "power", StandardBasicTypes.DOUBLE ) );registerFunction( "radians", new StandardSQLFunction( "radians", StandardBasicTypes.DOUBLE ) );registerFunction( "rand", new NoArgSQLFunction( "rand", StandardBasicTypes.DOUBLE ) );registerFunction( "round", new StandardSQLFunction( "round", StandardBasicTypes.DOUBLE ) );registerFunction( "roundmagic", new StandardSQLFunction( "roundmagic", StandardBasicTypes.DOUBLE ) );registerFunction( "sign", new StandardSQLFunction( "sign", StandardBasicTypes.INTEGER ) );registerFunction( "sin", new StandardSQLFunction( "sin", StandardBasicTypes.DOUBLE ) );registerFunction( "tan", new StandardSQLFunction( "tan", StandardBasicTypes.DOUBLE ) );registerFunction( "truncate", new StandardSQLFunction( "truncate", StandardBasicTypes.DOUBLE ) );// String Functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~registerFunction( "ascii", new StandardSQLFunction( "ascii", StandardBasicTypes.INTEGER ) );registerFunction( "char", new StandardSQLFunction( "char", StandardBasicTypes.CHARACTER ) );registerFunction( "concat", new VarArgsSQLFunction( StandardBasicTypes.STRING, "(", "||", ")" ) );registerFunction( "difference", new StandardSQLFunction( "difference", StandardBasicTypes.INTEGER ) );registerFunction( "hextoraw", new StandardSQLFunction( "hextoraw", StandardBasicTypes.STRING ) );registerFunction( "insert", new StandardSQLFunction( "lower", StandardBasicTypes.STRING ) );registerFunction( "left", new StandardSQLFunction( "left", StandardBasicTypes.STRING ) );registerFunction( "lcase", new StandardSQLFunction( "lcase", StandardBasicTypes.STRING ) );registerFunction( "ltrim", new StandardSQLFunction( "ltrim", StandardBasicTypes.STRING ) );registerFunction( "octet_length", new StandardSQLFunction( "octet_length", StandardBasicTypes.INTEGER ) );registerFunction( "position", new StandardSQLFunction( "position", StandardBasicTypes.INTEGER ) );registerFunction( "rawtohex", new StandardSQLFunction( "rawtohex", StandardBasicTypes.STRING ) );registerFunction( "repeat", new StandardSQLFunction( "repeat", StandardBasicTypes.STRING ) );registerFunction( "replace", new StandardSQLFunction( "replace", StandardBasicTypes.STRING ) );registerFunction( "right", new StandardSQLFunction( "right", StandardBasicTypes.STRING ) );registerFunction( "rtrim", new StandardSQLFunction( "rtrim", StandardBasicTypes.STRING ) );registerFunction( "soundex", new StandardSQLFunction( "soundex", StandardBasicTypes.STRING ) );registerFunction( "space", new StandardSQLFunction( "space", StandardBasicTypes.STRING ) );registerFunction( "stringencode", new StandardSQLFunction( "stringencode", StandardBasicTypes.STRING ) );registerFunction( "stringdecode", new StandardSQLFunction( "stringdecode", StandardBasicTypes.STRING ) );registerFunction( "stringtoutf8", new StandardSQLFunction( "stringtoutf8", StandardBasicTypes.BINARY ) );registerFunction( "ucase", new StandardSQLFunction( "ucase", StandardBasicTypes.STRING ) );registerFunction( "utf8tostring", new StandardSQLFunction( "utf8tostring", StandardBasicTypes.STRING ) );// Time and Date Functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~registerFunction( "curdate", new NoArgSQLFunction( "curdate", StandardBasicTypes.DATE ) );registerFunction( "curtime", new NoArgSQLFunction( "curtime", StandardBasicTypes.TIME ) );registerFunction( "curtimestamp", new NoArgSQLFunction( "curtimestamp", StandardBasicTypes.TIME ) );registerFunction( "current_date", new NoArgSQLFunction( "current_date", StandardBasicTypes.DATE ) );// H2 made a nasty breaking change that changed the type of// - current_timestamp to timestamp with time zone// - current_time to time with time zone// and also refuses to implicitly convert the typeregisterFunction( "current_time", new NoArgSQLFunction( buildId >= 200 ? "localtime" : "current_time", StandardBasicTypes.TIME ) );registerFunction( "current_timestamp", new NoArgSQLFunction( buildId >= 200 ? "localtimestamp" : "current_timestamp", StandardBasicTypes.TIMESTAMP ) );registerFunction( "datediff", new StandardSQLFunction( "datediff", StandardBasicTypes.INTEGER ) );registerFunction( "dayname", new StandardSQLFunction( "dayname", StandardBasicTypes.STRING ) );registerFunction( "dayofmonth", new StandardSQLFunction( "dayofmonth", StandardBasicTypes.INTEGER ) );registerFunction( "dayofweek", new StandardSQLFunction( "dayofweek", StandardBasicTypes.INTEGER ) );registerFunction( "dayofyear", new StandardSQLFunction( "dayofyear", StandardBasicTypes.INTEGER ) );registerFunction( "monthname", new StandardSQLFunction( "monthname", StandardBasicTypes.STRING ) );registerFunction( "now", new NoArgSQLFunction( "now", StandardBasicTypes.TIMESTAMP ) );registerFunction( "quarter", new StandardSQLFunction( "quarter", StandardBasicTypes.INTEGER ) );registerFunction( "week", new StandardSQLFunction( "week", StandardBasicTypes.INTEGER ) );// System Functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~registerFunction( "database", new NoArgSQLFunction( "database", StandardBasicTypes.STRING ) );registerFunction( "user", new NoArgSQLFunction( "user", StandardBasicTypes.STRING ) );getDefaultProperties().setProperty( AvailableSettings.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE );// http://code.google.com/p/h2database/issues/detail?id=235getDefaultProperties().setProperty( AvailableSettings.NON_CONTEXTUAL_LOB_CREATION, "true" );