Skip to content

Commit

Permalink
fix: ๐Ÿ› ์ •๊ธฐ ํ‘ธ์‹œ ์•Œ๋ฆผ ๋ฐฐ์น˜ ์ฟผ๋ฆฌ ํ”ฝ์Šค & ItemReader ๊ธฐ๋Šฅ ์ˆ˜์ • (#140)
Browse files Browse the repository at this point in the history
* fix: device_token_owner dto query ์ตœ์ ํ™”๋ฅผ ์œ„ํ•œ device_token pk ํ•„๋“œ ์ถ”๊ฐ€

* fix: querydsl_no_offset_string_options id_name ๋ฐ›๋Š” ์ •์  ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ์—์„œ field=null ์ˆ˜์ •

* fix: query dsl no offset paging item reader id_select_query ์ถ”๊ฐ€

* feat: step builder ํŒจํ„ด์„ ์ ์šฉํ•œ querydsl_no_offset_paging_item_reader_builder ํด๋ž˜์Šค ์ •์˜

* fix: active_device_token_reader query ์ˆ˜์ •
  • Loading branch information
psychology50 authored Jul 25, 2024
1 parent ab0de1e commit 6bd74c2
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
public record DeviceTokenOwner(
Long userId,
Long deviceTokenId,
String name,
String deviceToken
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

public class QuerydslNoOffsetPagingItemReader<T> extends QuerydslPagingItemReader<T> {
private QuerydslNoOffsetOptions<T> options;
private Function<JPAQueryFactory, JPAQuery<T>> idSelectQuery;

private QuerydslNoOffsetPagingItemReader() {
super();
Expand All @@ -28,6 +29,15 @@ public QuerydslNoOffsetPagingItemReader(EntityManagerFactory entityManagerFactor
this.options = options;
}

public QuerydslNoOffsetPagingItemReader(EntityManagerFactory entityManagerFactory,
int pageSize,
QuerydslNoOffsetOptions<T> options,
Function<JPAQueryFactory, JPAQuery<T>> queryFunction,
Function<JPAQueryFactory, JPAQuery<T>> idSelectQuery) {
this(entityManagerFactory, pageSize, options, queryFunction);
this.idSelectQuery = idSelectQuery;
}

@Override
@SuppressWarnings("unchecked")
protected void doReadPage() {
Expand All @@ -47,7 +57,7 @@ protected void doReadPage() {
protected JPAQuery<T> createQuery() {
JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
JPAQuery<T> query = queryFunction.apply(queryFactory);
options.initKeys(query, getPage()); // ์ œ์ผ ์ฒซ๋ฒˆ์งธ ํŽ˜์ด์ง•์‹œ ์‹œ์ž‘ํ•ด์•ผํ•  ID ์ฐพ๊ธฐ
options.initKeys((idSelectQuery != null) ? idSelectQuery.apply(queryFactory) : query, getPage()); // ์ œ์ผ ์ฒซ๋ฒˆ์งธ ํŽ˜์ด์ง•์‹œ ์‹œ์ž‘ํ•ด์•ผํ•  ID ์ฐพ๊ธฐ

return options.createQuery(query, getPage());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package kr.co.pennyway.batch.common.reader;

import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManagerFactory;
import kr.co.pennyway.batch.common.reader.options.QuerydslNoOffsetOptions;

import java.util.function.Function;

/**
* {@link QuerydslNoOffsetPagingItemReader}๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•œ ๋นŒ๋” ํด๋ž˜์Šค
* <p>
* Step Builder ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜์˜€์œผ๋ฉฐ, ๊ฐ ๋ฉ”์†Œ๋“œ๋Š” ํ•ด๋‹นํ•˜๋Š” ์„ค์ •๊ฐ’์„ ์„ค์ •ํ•˜๊ณ  ๋‹ค์Œ ๋‹จ๊ณ„์˜ ๋นŒ๋”๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
*
* @author YANG JAESEO
*/
public class QuerydslNoOffsetPagingItemReaderBuilder<T> {
private QuerydslNoOffsetPagingItemReaderBuilder() {
}

public static <T> EntityManagerFactoryStep<T> builder() {
return new Steps<>();
}

public interface EntityManagerFactoryStep<T> {
/**
* The {@link EntityManagerFactory} to be used for executing the configured queryFunction.
*
* @param emf {@link EntityManagerFactory} used to create
* {@link jakarta.persistence.EntityManager}
* @return this instance for method chaining
*/
PageSizeStep<T> entityManagerFactory(EntityManagerFactory emf);
}

public interface PageSizeStep<T> {
/**
* The number of items to be read with each page.
*
* @param pageSize number of items
* @return this instance for method chaining
*/
OptionsStep<T> pageSize(int pageSize);
}

public interface OptionsStep<T> {
/**
* The {@link QuerydslNoOffsetOptions} to be used for configuring the reader.
*
* @param options {@link QuerydslNoOffsetOptions} to be used
* @return this instance for method chaining
*/
QueryFunctionStep<T> options(QuerydslNoOffsetOptions<T> options);
}

public interface QueryFunctionStep<T> {
/**
* The function that creates the query to be executed.
*
* @param queryFunction function that creates the query
* @return this instance for method chaining
*/
BuildStep<T> queryFunction(Function<JPAQueryFactory, JPAQuery<T>> queryFunction);
}

public interface BuildStep<T> {
/**
* The function that creates the query to be executed to select the currentId and lastId.
* This is used to determine the currentId when the reader is not on the last page.
* If this is not provided, the reader will use the queryFunction to determine the currentId and lastId.
*
* @param idSelectQuery function that creates the query to select the currentId and lastId
* @return this instance for method chaining
*/
BuildStep<T> idSelectQuery(Function<JPAQueryFactory, JPAQuery<T>> idSelectQuery);

QuerydslNoOffsetPagingItemReader<T> build();
}

private static class Steps<T> implements
EntityManagerFactoryStep<T>, PageSizeStep<T>, OptionsStep<T>, QueryFunctionStep<T>, BuildStep<T> {
private EntityManagerFactory entityManagerFactory;
private int pageSize;
private QuerydslNoOffsetOptions<T> options;
private Function<JPAQueryFactory, JPAQuery<T>> queryFunction;
private Function<JPAQueryFactory, JPAQuery<T>> idSelectQuery;

@Override
public PageSizeStep<T> entityManagerFactory(EntityManagerFactory emf) {
this.entityManagerFactory = emf;
return this;
}

@Override
public OptionsStep<T> pageSize(int pageSize) {
this.pageSize = pageSize;
return this;
}

@Override
public QueryFunctionStep<T> options(QuerydslNoOffsetOptions<T> options) {
this.options = options;
return this;
}

@Override
public BuildStep<T> queryFunction(Function<JPAQueryFactory, JPAQuery<T>> queryFunction) {
this.queryFunction = queryFunction;
return this;
}

@Override
public BuildStep<T> idSelectQuery(Function<JPAQueryFactory, JPAQuery<T>> idSelectQuery) {
this.idSelectQuery = idSelectQuery;
return this;
}

@Override
public QuerydslNoOffsetPagingItemReader<T> build() {
return new QuerydslNoOffsetPagingItemReader<>(
entityManagerFactory,
pageSize,
options,
queryFunction,
idSelectQuery
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class QuerydslNoOffsetStringOptions<T> extends QuerydslNoOffsetOptions<T>
private final StringPath field;
private String currentId;
private String lastId;

private QuerydslNoOffsetStringOptions(@Nonnull StringPath field,
@Nonnull Expression expression) {
super(field, expression);
Expand All @@ -22,7 +22,7 @@ private QuerydslNoOffsetStringOptions(@Nonnull StringPath field,
@Nonnull Expression expression,
String idName) {
super(idName, expression);
this.field = null;
this.field = field;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package kr.co.pennyway.batch.reader;

import com.querydsl.core.types.ConstructorExpression;
import com.querydsl.core.types.Projections;
import jakarta.persistence.EntityManagerFactory;
import kr.co.pennyway.batch.common.dto.DeviceTokenOwner;
import kr.co.pennyway.batch.common.reader.QuerydslNoOffsetPagingItemReader;
import kr.co.pennyway.batch.common.reader.QuerydslNoOffsetPagingItemReaderBuilder;
import kr.co.pennyway.batch.common.reader.expression.Expression;
import kr.co.pennyway.batch.common.reader.options.QuerydslNoOffsetNumberOptions;
import kr.co.pennyway.batch.common.reader.options.QuerydslNoOffsetOptions;
Expand All @@ -27,20 +29,29 @@ public class ActiveDeviceTokenReader {
@Bean
@StepScope
public QuerydslNoOffsetPagingItemReader<DeviceTokenOwner> querydslNoOffsetPagingItemReader() {
QuerydslNoOffsetOptions<DeviceTokenOwner> options = QuerydslNoOffsetNumberOptions.of(user.id, Expression.ASC, "userId");
QuerydslNoOffsetOptions<DeviceTokenOwner> options = QuerydslNoOffsetNumberOptions.of(deviceToken.id, Expression.ASC, "deviceTokenId");

return new QuerydslNoOffsetPagingItemReader<>(emf, 1000, options, queryFactory -> queryFactory
.select(
Projections.constructor(
DeviceTokenOwner.class,
user.id,
user.name,
deviceToken.token
)
return QuerydslNoOffsetPagingItemReaderBuilder.<DeviceTokenOwner>builder()
.entityManagerFactory(emf)
.pageSize(1000)
.options(options)
.queryFunction(queryFactory -> queryFactory
.select(createConstructorExpression())
.from(deviceToken)
.innerJoin(user).on(deviceToken.user.id.eq(user.id))
.where(deviceToken.activated.isTrue().and(user.notifySetting.accountBookNotify.isTrue()))
)
.from(deviceToken)
.innerJoin(user).on(deviceToken.user.id.eq(user.id))
.where(deviceToken.activated.isTrue().and(user.notifySetting.accountBookNotify.isTrue()))
.idSelectQuery(queryFactory -> queryFactory.select(createConstructorExpression()).from(deviceToken))
.build();
}

private ConstructorExpression<DeviceTokenOwner> createConstructorExpression() {
return Projections.constructor(
DeviceTokenOwner.class,
user.id,
deviceToken.id,
user.name,
deviceToken.token
);
}
}

0 comments on commit 6bd74c2

Please sign in to comment.