Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[4주차 과제] 유진 #22

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ plugins {
id 'java'
id 'org.springframework.boot' version '3.2.2'
id 'io.spring.dependency-management' version '1.1.4'
id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10'
}

// 프로젝트의 그룹(도메인) 및 버전
Expand Down Expand Up @@ -38,9 +39,42 @@ dependencies {

// Database
implementation 'com.h2database:h2'

// Querydsl
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
// implementation 'com.querydsl:querydsl-apt:5.0.0:jakarta'
annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jakarta'
annotationProcessor 'jakarta.persistence:jakarta.persistence-api'
annotationProcessor 'jakarta.annotation:jakarta.annotation-api'
}

// 테스트 의존성 라이브러리
tasks.named('test') {
useJUnitPlatform()
}


// Querydsl 설정
def querydslDir = "build/generated/querydsl"

querydsl {
jpa = true
querydslSourcesDir = querydslDir
}
sourceSets {
main.java.srcDir querydslDir
}
compileQuerydsl {
options.annotationProcessorPath = configurations.querydsl
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
querydsl.extendsFrom compileClasspath
}

// gradle clean 시에 QClass 디렉토리 삭제
clean {
delete file(querydslDir)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.example.urlshortener.common.configuration;

import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class QueryDslConfiguration {

@Bean
public JPAQueryFactory jpaQueryFactory(EntityManager entityManager) {
return new JPAQueryFactory(entityManager);
}
}
25 changes: 21 additions & 4 deletions src/main/java/com/example/urlshortener/domain/url/UrlService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,25 @@
import com.example.urlshortener.domain.url.dto.ShortenedUrlDto;
import com.example.urlshortener.domain.url.entity.ShortenedUrl;
import com.example.urlshortener.domain.url.exception.UrlNotFoundException;
import com.example.urlshortener.domain.url.repository.ShortenedUrlQueryRepository;
import com.example.urlshortener.domain.url.repository.ShortenedUrlRepository;
import jakarta.transaction.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;

@Service
@RequiredArgsConstructor
public class UrlService {

private final ShortenedUrlRepository shortenedUrlRepository;
private final ShortenedUrlQueryRepository shortenedUrlQueryRepository;

@Transactional
public ShortenedUrlDto createShortUrl(String url) {
ShortenedUrl existingShortenedUrl = shortenedUrlRepository.findByOriginUrl(url).orElse(null);
ShortenedUrl existingShortenedUrl = shortenedUrlRepository.findByOriginUrl(url)
.orElse(null);

if (existingShortenedUrl != null) {
return ShortenedUrlDto.from(existingShortenedUrl);
Expand All @@ -36,7 +40,8 @@ public ShortenedUrlDto getShortUrl(String shortId) {
ShortenedUrl shortenedUrl = shortenedUrlRepository.findByShortUrl(shortId)
.orElseThrow(UrlNotFoundException::new);

return new ShortenedUrlDto(shortenedUrl.getId(), shortenedUrl.getShortUrl(), shortenedUrl.getOriginUrl(), shortenedUrl.getCreatedAt());
return new ShortenedUrlDto(shortenedUrl.getId(), shortenedUrl.getShortUrl(),
shortenedUrl.getOriginUrl(), shortenedUrl.getCreatedAt());
}

public String getOriginUrl(String shortId) {
Expand All @@ -45,4 +50,16 @@ public String getOriginUrl(String shortId) {

return shortenedUrl.getOriginUrl();
}


public List<ShortenedUrlDto> getShortUrlsWithJpa(String inquiry) {
List<ShortenedUrl> shortenedUrls = shortenedUrlRepository.findAllByOriginUrlContains(
inquiry);

return ShortenedUrlDto.from(shortenedUrls);
}

public List<ShortenedUrlDto> getShortUrlsWithQueryDsl(String inquiry) {
return shortenedUrlQueryRepository.getShortUrlsWithQueryDsl(inquiry);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,16 @@
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotBlank;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.view.RedirectView;

@RequiredArgsConstructor
Expand All @@ -33,19 +41,19 @@ public Response<ShortUrlResponse> createShortUrl(@RequestBody CreateShortUrlRequ
return Response.data(ShortUrlResponse.from(shortenedUrl));
}

@Operation(
summary = "단축 URL 조회하기",
responses = {
@ApiResponse(responseCode = "200", description = "OK"),
@ApiResponse(responseCode = "404", description = "URL_NOT_FOUND"),
@ApiResponse(responseCode = "500", description = "INTERNAL_SERVER_ERROR")
}
)
@GetMapping("/{short_id}")
public Response<ShortUrlResponse> getShortUrl(@PathVariable("short_id") String shortId) {
ShortenedUrlDto shortenedUrl = urlService.getShortUrl(shortId);
return Response.data(ShortUrlResponse.from(shortenedUrl));
}
// @Operation(
// summary = "단축 URL 조회하기",
// responses = {
// @ApiResponse(responseCode = "200", description = "OK"),
// @ApiResponse(responseCode = "404", description = "URL_NOT_FOUND"),
// @ApiResponse(responseCode = "500", description = "INTERNAL_SERVER_ERROR")
// }
// )
// @GetMapping("/{short_id}")
// public Response<ShortUrlResponse> getShortUrl(@PathVariable("short_id") String shortId) {
// ShortenedUrlDto shortenedUrl = urlService.getShortUrl(shortId);
// return Response.data(ShortUrlResponse.from(shortenedUrl));
// }

@Operation(
summary = "Short URL 리디렉션",
Expand All @@ -61,4 +69,37 @@ public RedirectView redirectShortUrl(@PathVariable("short_id") String shortId) {
redirectView.setUrl(originUrl);
return redirectView;
}

/**
* 4주차 과제입니다.
*/

@Operation(
summary = "JPA로 URL 조회하기",
responses = {
@ApiResponse(responseCode = "200", description = "OK"),
@ApiResponse(responseCode = "500", description = "INTERNAL_SERVER_ERROR")
}
)
@GetMapping("/jpa")
public Response<List<ShortenedUrlDto>> getShortUrlsWithJpa(
@NotBlank @RequestParam("inquiry") String inquiry) {
List<ShortenedUrlDto> shortenedUrls = urlService.getShortUrlsWithJpa(inquiry);
return Response.data(shortenedUrls);
}

@Operation(
summary = "QueryDsl로 URL 조회하기",
responses = {
@ApiResponse(responseCode = "200", description = "OK"),
@ApiResponse(responseCode = "500", description = "INTERNAL_SERVER_ERROR")
}
)

@GetMapping("/query-dsl")
public Response<List<ShortenedUrlDto>> getShortUrl(
@NotBlank @RequestParam("inquiry") String inquiry) {
List<ShortenedUrlDto> shortenedUrls = urlService.getShortUrlsWithQueryDsl(inquiry);
return Response.data(shortenedUrls);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package com.example.urlshortener.domain.url.dto;

import com.example.urlshortener.domain.url.entity.ShortenedUrl;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

import java.time.LocalDateTime;

@Getter
@AllArgsConstructor
@Builder
Expand All @@ -25,4 +26,11 @@ public static ShortenedUrlDto from(ShortenedUrl shortenedUrl) {
.createdAt(shortenedUrl.getCreatedAt())
.build();
}

public static List<ShortenedUrlDto> from(List<ShortenedUrl> shortenedUrls) {
return shortenedUrls.stream()
.map(ShortenedUrlDto::from)
.collect(Collectors.toList());

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.example.urlshortener.domain.url.repository;

import com.example.urlshortener.domain.url.dto.ShortenedUrlDto;
import com.example.urlshortener.domain.url.entity.QShortenedUrl;
import com.example.urlshortener.domain.url.entity.ShortenedUrl;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;


@Service
@RequiredArgsConstructor
public class ShortenedUrlQueryRepository {

private final JPAQueryFactory jpaQueryFactory;

public List<ShortenedUrlDto> getShortUrlsWithQueryDsl(String inquiry) {
QShortenedUrl shortenedUrl = QShortenedUrl.shortenedUrl;
List<ShortenedUrl> shortenedUrls = jpaQueryFactory
.selectFrom(shortenedUrl)
.where(shortenedUrl.originUrl.contains(inquiry))
.fetch();

return ShortenedUrlDto.from(shortenedUrls);
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package com.example.urlshortener.domain.url.repository;

import com.example.urlshortener.domain.url.entity.ShortenedUrl;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ShortenedUrlRepository extends JpaRepository<ShortenedUrl, Long> {

Optional<ShortenedUrl> findByOriginUrl(String longUrl);

Optional<ShortenedUrl> findByShortUrl(String shortUrl);

List<ShortenedUrl> findAllByOriginUrlContains(String inquiry);
}
11 changes: 9 additions & 2 deletions src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,16 @@ spring:
jpa:
database-platform: org.hibernate.dialect.H2Dialect
hibernate:
ddl-auto: create # (none, create, create-drop, update, validate)
ddl-auto: none # (none, create, create-drop, update, validate)
properties:
hibernate:
dialect: org.hibernate.dialect.H2Dialect # Dialect: 데이터베이스에 대한 특정 SQL 문법과 데이터 타입 매핑에 대한 규칙을 제공
format_sql: true # SQL 쿼리를 포맷팅하여 출력할지 여부 설정
show_sql: true # SQL 쿼리를 출력할지 여부 설정
show_sql: true # SQL 쿼리를 출력할지 여부 설정
defer-datasource-initialization: true

sql:
init:
mode: always # ddl-auto: none, H2는 필요없음(어차피 사라지기 때문)
schema-locations: classpath:db-init-scripts/schema.sql
data-locations: classpath:db-init-scripts/import.sql
6 changes: 6 additions & 0 deletions src/main/resources/db-init-scripts/import.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
insert into shortened_url (short_url, origin_url, created_at) values
('abc', 'http://example.com/page1', '2024-04-01 10:00:00'),
('def', 'http://example.com/page2', '2024-04-02 12:00:00'),
('ghi', 'http://example.com/page3', '2024-04-03 14:00:00'),
('jkl', 'http://example.com/page4', '2024-04-04 16:00:00'),
('mno', 'http://example.com/page5', '2024-04-05 18:00:00');
15 changes: 15 additions & 0 deletions src/main/resources/db-init-scripts/schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
CREATE TABLE shortened_url (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
short_url VARCHAR(2048) NOT NULL,
origin_url VARCHAR(2048) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);

CREATE TABLE url_click (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
shortened_url_id BIGINT NOT NULL,
clicks BIGINT DEFAULT 0 NOT NULL,
click_date DATE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
FOREIGN KEY (shortened_url_id) REFERENCES shortened_url(id)
);