728x90
반응형

저장은 Json으로 하고, 불러올 땐 Object로 불러오게끔 하는 Converter를 만들어보자.

CRUD가 필요하지 않은 단순 조회용 데이터면 Json으로 저장하는 방법 또한 괜찮다고 생각한다.

 

1. Json으로 저장하고 Object로 불러오는 법

@Entity
@NoArgsConstructor
@Getter
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Convert(converter = UserDataDtoConverter.class)
    @Column(columnDefinition = "json")
    @ColumnTransformer(write = "?::json")
    private UserDataDto userData;
}

 

@Data
@AllArgsConstructor
@NoArgsConstructor
public class userDataDto {

    private String name;
    private String id;

}

 

@Slf4j
public class UserDataDtoConverter implements AttributeConverter<userDataDto, String> {

    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public String convertToDatabaseColumn(userDataDto attribute) {
        try {
            return objectMapper.writeValueAsString(attribute);
        } catch (JsonProcessingException e) {
            log.error("fail to serialize as object into Json : {}", attribute, e);
            throw new IllegalArgumentException(e);
        }
    }

    @Override
    public userDataDto convertToEntityAttribute(String dbData) {
        try {
            return objectMapper.readValue(dbData, userDataDto.class);
        } catch (IOException e) {
            log.error("fail to deserialize as Json into Object : {}", dbData, e);
            throw new IllegalArgumentException(e);
        }
    }
}

 

2. Json으로 Object 저장하는 법

    @Column(nullable = false, columnDefinition = "jsonb")
    @Type(JsonBinaryType.class)
    private Map<KEPCORateCode, KRRatePlan> plan; // Price Plan

    @Column(nullable = false, columnDefinition = "jsonb")
    @Type(JsonBinaryType.class)
    private CommonPricePlan commonPlan;

 

3. String으로 저장하고, List<String>으로 불러오는 법

    @Convert(converter = StringListConverter.class)
    @Column(columnDefinition = "text")
    private List<String> content = new ArrayList<>();
@Converter
public class StringListConverter implements AttributeConverter<List<String>, String> {

    private final ObjectMapper mapper;

    public StringListConverter(ObjectMapper mapper) {
        this.mapper = mapper;
    }

    @Override
    public String convertToDatabaseColumn(List<String> attribute) {
        try {
            return mapper.writeValueAsString(attribute);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public List<String> convertToEntityAttribute(String dbData) {
        try {
            return mapper.readValue(dbData, new TypeReference<List<String>>() {
            });
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}

 

4. String으로 저장하고 List<Object>로 불러오는 법

    @Convert(converter = LoadProfileConverter.class)
    // @Column(columnDefinition = "text")
    private List<Profile> profile;

 

@Slf4j
public class LoadProfileConverter implements AttributeConverter<List<Profile>, String> {

    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public String convertToDatabaseColumn(List<Profile> attribute) {
        try {
            return objectMapper.writeValueAsString(attribute);
        } catch (JsonProcessingException e) {
            log.error("fail to serialize as object into Json : {}", attribute, e);
            throw new IllegalArgumentException(e);
        }
    }

    @Override
    public List<Profile> convertToEntityAttribute(String dbData) {
        try {
            return objectMapper.readValue(dbData, new TypeReference<List<Profile>>() {
            });
        } catch (IOException e) {
            log.error("fail to deserialize as Json into Object : {}", dbData, e);
            throw new IllegalArgumentException(e);
        }
    }
}
728x90
반응형
728x90
반응형
public List<FreePostsCreateResponseDto> findByTitle(String title){
        return freePostsRepository.findByTitle(title).stream()
                .map(FreePostsCreateResponseDto::new)
                .collect(Collectors.toList());

 

728x90
반응형
728x90
반응형

1. 의존성 주입

QueryDsl EntityManager 를 전역으로 의존성을 주입해주기 위해 다음을 만들어준다.

@Configuration
public class QueryDslConfig {

    @PersistenceContext
    private EntityManager entityManager;

    @Bean
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(entityManager);
    }
}

 

그 다음 Repository에서 다음과 같이 깔끔하게 해줄 수 있다.

@Repository
@RequiredArgsConstructor
public class PostCustomRepositoryImpl implements PostCustomRepository {

    private final JPAQueryFactory jpaQueryFactory;


2. 2번 이상의 Depth로 JoinFetch 할 경우

 

JPQL 같은 경우는 Join Fetch를 여러번 타고 갈 때 

select u from User u join fetch u.team t join fetch t.site

이런 식으로 가지만, QueryDsl 같은 경우는 

query
.selectFrom(user)
.leftJoin(user.team, team)
.leftJoin(team.site, site)

이런식으로 타고가야 한다.

 

3. 같은 객체를 2번 사용할 경우 ( 같은 query 내에 같은 객체를 다른 곳에서도 Join 할 경우 )

var a = new QUser("testUser");
var b = new QUser("sequenceUser");

이런 식으로 Q객체를 변수로 할당하여 사용하면 된다.

 

4. 메소드화

public JPAQuery<User> findUser() {
    return query
        .selectFrom(user)
        .leftJoin(user.team, team)
        .leftJoin(team.site, site);
}

이런 식으로 Query 메소드를 분리할 수도 있다.

 

5. Optional 사용

public Optional<JPAQuery<User>> findUser() {
    return Optional.ofNullable(query
        .selectFrom(user)
        .leftJoin(user.team, team)
        .leftJoin(team.site, site)
        .fetchFirst());
}

Optinal 을 사용하여 아래와 같이 NoSuchElementException을 던질 수도 있다.

public User findLatestOne() {
        return findUser().orElseThrow();
    }

 

6. Optional 사용 2

fetch종류에 따라 메소드를 분할 할 경우 아래와 같이 사용 할 수 도 있다.

public JPAQuery<User> findUser() {
    return query
        .selectFrom(user)
        .leftJoin(user.team, team)
        .leftJoin(team.site, site)
        .fetchFirst();
}
public User findLatestOne() {
        return Optional.ofNullable(findUser().fetchFirst()).orElseThrow();
    }
public List<User> findUsers() {
        return findUser().fetch();
    }
728x90
반응형
728x90
반응형

@Enumerated 어노테이션으로 사용할 땐 다음과 같이 저장하는 방법이 있다.

  • ORDINAL
  • STRING

STRING으로 저장하면 한 눈에 알기 쉽지만 아무래도 Interger로 저장하는 것보다 데이터의 용량이 많아진다.

ORDINAL로 저장하는 것은 데이터 용량은 적지만 순서가 무조건 0부터 시작되고, 저장되는 값을 커스터마이징하기가 힘들며 나중에 관리하기가 힘들다.

 

가장 좋은 방법은 

  • 데이터를 저장할 때는 name에 맞는 value를 Interger로 저장한다.
  • 데이터를 불러올 때는 저장된 value에 맞는 name을 불러온다.

이거다. 쓰는 방법을 알아보자.

 

참고 -> Persisting Enums in JPA | Baeldung

 

- 기본 Enum

public enum Category {
    SPORT("S"), MUSIC("M"), TECHNOLOGY("T");

    private String code;

    private Category(String code) {
        this.code = code;
    }

    public String getCode() {
        return code;
    }
}

1. 기존 @Enumerated 어노테이션을 삭제한다.

private Category category;

 

2. Converter 를 만들어준다.

@Converter(autoApply = true)
public class CategoryConverter implements AttributeConverter<Category, String> {
 
    @Override
    public String convertToDatabaseColumn(Category category) {
        if (category == null) {
            return null;
        }
        return category.getCode();
    }

    @Override
    public Category convertToEntityAttribute(String code) {
        if (code == null) {
            return null;
        }

        return Stream.of(Category.values())
          .filter(c -> c.getCode().equals(code))
          .findFirst()
          .orElseThrow(IllegalArgumentException::new);
    }
}

 

3. 상속관계에 포함된 Entity라면 다음과 같이 바꿔준다.

- 부모 Entity ( dtype의 자료형을 Interger로 바꿈 )

@DiscriminatorColumn(name = "item", discriminatorType = DiscriminatorType.INTEGER)

 

- 자식 Entity ( 숫자로 바꾸면 저장할 때 Interger로 저장됨 )

@DiscriminatorValue("1")
728x90
반응형
728x90
반응형

참고 사이트 : vladmihalcea/hypersistence-utils: The Hypersistence Utils library (previously known as Hibernate Types) gives you Spring and Hibernate utilities that can help you get the most out of your data access layer. (github.com)

 

GitHub - vladmihalcea/hypersistence-utils: The Hypersistence Utils library (previously known as Hibernate Types) gives you Sprin

The Hypersistence Utils library (previously known as Hibernate Types) gives you Spring and Hibernate utilities that can help you get the most out of your data access layer. - GitHub - vladmihalcea/...

github.com

 

1. build.gradle에 다음을 추가한다.

implementation 'io.hypersistence:hypersistence-utils-hibernate-60:3.3.2'

깃허브에서 hibernate 버전과 일치하는 의존성을 찾아야한다. ( 본인은 6 버전 )

 

2. Entity Class에서 정의한다.

@Type(JsonType.class)
    @Column(name="detail",columnDefinition = "json")
    private Map<String,String> detail = new HashMap<>();

 

728x90
반응형
728x90
반응형
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
 
public class MapIterationSample {
    public static void main(String[] agrs) {
        Map<String, String> map = new HashMap<String, String>();
         
        map.put("키1", "값1");
        map.put("키2", "값2");
        map.put("키3", "값3");
        map.put("키4", "값4");
        map.put("키5", "값5");
        map.put("키6", "값6");
         
         
        // 방법1
        Iterator<String> keys = map.keySet().iterator();
        while( keys.hasNext() ){
            String key = keys.next();
            System.out.println( String.format("키 : %s, 값 : %s", key, map.get(key)) );
        }
         
        // 방법2
        for( Map.Entry<String, String> elem : map.entrySet() ){
            System.out.println( String.format("키 : %s, 값 : %s", elem.getKey(), elem.getValue()) );
        }
         
        // 방법3
        for( String key : map.keySet() ){
            System.out.println( String.format("키 : %s, 값 : %s", key, map.get(key)) );
        }
    }
}
728x90
반응형
728x90
반응형

1. 엔티티에서 평소에 쓰는 @JsonIgnore 삭제

@ManyToOne(fetch = LAZY)
@JoinColumn(name = "item_id")
//@JsonIgnore
private Item item;

 

2. Query에 fetch join 적용

@Query("select d from Product p join fetch p.item i order by p.createdAt")
    List<Device> findAllByOrderByCreatedAt();

 

다만, @JsonIgnore 을 삭제하면 이제부터 모든 find query에는 fetch join이 들어가야한다.

 

@OnetoMany에서는 다음과 같이 쓰면된다.

@OnetoMany(mappedby="item")
public List<Item> itemList = new ArrayList<>();
728x90
반응형
728x90
반응형
FROM node:17-alpine as staged

WORKDIR /opt/app

COPY ["package.json", "package-lock.json", "./"]
RUN ["npm", "install"]

COPY ["tsconfig.build.json", "tsconfig.json", "./"]
COPY ["src/", "./src/"]
RUN ["npm", "run", "build"]

RUN ["/bin/sh", "-c", "find . ! -name dist ! -name node_modules -maxdepth 1 -mindepth 1 -exec rm -rf {} \\\\;"]

FROM node:17-alpine as completed
WORKDIR /opt/app
COPY --from=staged /opt/app ./
ENTRYPOINT ["node", "dist/src/main"]
EXPOSE 8080/tcp
728x90
반응형
728x90
반응형
FROM gradle:8-jdk17-alpine as builder
WORKDIR /build

# 그래들 파일이 변경되었을 때만 새롭게 의존패키지 다운로드 받게함.
COPY build.gradle settings.gradle /build/
RUN gradle build -x test --parallel --continue > /dev/null 2>&1 || true

# 빌더 이미지에서 애플리케이션 빌드
COPY . /build
RUN gradle build -x test --parallel

# APP
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app

# 빌더 이미지에서 jar 파일만 복사
COPY --from=builder /build/build/libs/my-app-*-SNAPSHOT.jar .

EXPOSE 8080

CMD java -jar ./my-app-*-SNAPSHOT.jar

이렇게 설정해주면 도커 이미지의 크기를 많이 줄일 수 있다.

 

참고 

Gradle을 사용할 때 도커 빌드를 빠르게 하는 방법 - Soo Story (findstar.pe.kr)

728x90
반응형
728x90
반응형

build.gradle 에 Next.js 를 추가하는 방법은 다음과 같다.

// npm install
task appNpmInstall(type: Exec) {
    workingDir "$projectDir/projectname"
    inputs.dir  "$projectDir/projectname"
    if (System.getProperty("os.name").toLowerCase(Locale.ROOT).contains('windows')) {
        commandLine "npm.cmd", "install"
    } else {
        commandLine "npm", "install"
    }
}

//npm build
task npmBuild(type: Exec) {
    dependsOn("appNpmInstall")
    workingDir "$projectDir/projectname"
    inputs.dir "$projectDir/projectname"
    if (System.getProperty("os.name").toLowerCase(Locale.ROOT).contains('windows')) {
        commandLine "npm.cmd", "run", "build"
    } else {
        commandLine "npm", "run", "build"
    }
}
// build 경로를 webpack으로 미리 설정 하였으므로, build 결과 이동 관련 Task 제외
compileJava.dependsOn("npmBuild")
728x90
반응형

+ Recent posts