I believe I have correctly set up the dependencies for my Spring Boot version 3.1.8 as shown in the build.gradle code below.
I am trying to implement a feature where I send a unique list of hashtags from articles to the user.
To achieve this, I customized the repository using Querydsl to fetch this data from the Article table
I have also added this functionality to the service and controller layers. I used Thymeleaf to send this data to the user and have also written HTML.
Although all my tests pass, the functionality doesn't work in the actual application. Can someone please assist me in finding a solution?
I confirmed that all repository had "final" specified, but the issue persists.
Other Querydsl queries (e.g., findByContentContaining) are functioning well.
I also tried changing the build and run settings.
[ build and run : Intellij, Run and unsing : Gradle ]
[ build and run : Intellij, Run and unsing : Intellij ]
[ build and run : Gradle , Run and unsing : Gradle ]
< build.gradle >
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
implementation 'org.springframework.boot:spring-boot-starter-web'
// JSON API
implementation 'org.springframework.boot:spring-boot-starter-data-rest'
implementation 'org.springframework.data:spring-data-rest-hal-explorer'
// server side
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
// Auth
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
// DB
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'com.mysql:mysql-connector-j'
// queryDSL setting
implementation "com.querydsl:querydsl-jpa:5.0.0"
implementation "com.querydsl:querydsl-core:5.0.0"
implementation "com.querydsl:querydsl-collections:5.0.0"
annotationProcessor(
"com.querydsl:querydsl-apt:5.0.0:jakarta",
"jakarta.annotation:jakarta.annotation-api",
"jakarta.persistence:jakarta.persistence-api"
)
}
< ArticleRepositoryCustom >
public interface ArticleRepositoryCustom {
List\<String\> findAllDistinctHashtags();
}
< ArticleRepositoryCustomImpl >
public class ArticleRepositoryCustomImpl extends QuerydslRepositorySupport implements ArticleRepositoryCustom {
public ArticleRepositoryCustomImpl() {
super(Article.class);
}
@Override
public List<String> findAllDistinctHashtags() {
QArticle article = QArticle.article;
return from(article)
.select(article.hashtag)
.fetch();
}
}
< part of AticleService >
@Slf4j
@RequiredArgsConstructor // 필수 필드에 대한 생성자를 자동 생성
@Transactional
@Service // 서비스 빈으로 등록
public class ArticleService {
private final ArticleRepository articleRepository;
// omit
@Transactional(readOnly = true)
public Page<ArticleDto> searchArticlesViaHashtag(String hashtag, Pageable pageable) {
log.error("[해시태그 검색] Find Articles With Hashtag = {}", hashtag);
if (hashtag == null || hashtag.isEmpty() || hashtag.isBlank()) {
log.error("[해시태그 검색] Search Params is Null");
return Page.empty(pageable);
}
return articleRepository.findByHashtag(hashtag, pageable).map(ArticleDto::from);
}
@Transactional(readOnly = true)
public List<String> getHashtags() {
List<String> uniqueHashtags = articleRepository.findAllDistinctHashtags();
log.error("[유니크 해시태그 조회] uniqueHashtags = {}", uniqueHashtags);
System.out.println(uniqueHashtags);
return uniqueHashtags;
}
< part of ArticleController >
@Slf4j
@RequiredArgsConstructor
@RequestMapping("/articles")
@Controller
public class ArticleController {
private final ArticleService articleService;
private final PaginationService paginationService;
// omit
@GetMapping("/search-hashtag")
public String searchHashtag(
@RequestParam(required = false) String searchValue,
@PageableDefault(size = 10, sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable,
ModelMap map
) {
log.error("[컨트롤러] search-hashtag 확인, searchValue: {}", searchValue);
Page<ArticleResponse> articles = articleService.searchArticlesViaHashtag(searchValue, pageable).map(ArticleResponse::from);
log.error("[컨트롤러] searchArticlesViaHashtag에서 패스");
List<Integer> barNumbers = paginationService.getPaginationBarNumbers(pageable.getPageNumber(), articles.getTotalPages());
log.error("[컨트롤러] getPaginationBarNumbers에서 패스");
List<String> hashtags = articleService.getHashtags();
log.error("[컨트롤러] getHashtags에서 패스");
log.error("[컨트롤러] articles: {}, barNumbers: {}, hashtag: {}", articles, barNumbers, hashtags);
map.addAttribute("articles", articles);
map.addAttribute("hashtags", hashtags);
map.addAttribute("paginationBarNumbers", barNumbers);
map.addAttribute("searchType", SearchType.HASHTAG);
return "articles/search-hashtag";
}
< error content >
2024-02-21T10:31:29.047+09:00 ERROR 18720 --- \[nio-8082-exec-1\] c.f.m.controller.ArticleController : \[컨트롤러\] searchArticlesViaHashtag에서 패스
2024-02-21T10:31:29.049+09:00 ERROR 18720 --- \[nio-8082-exec-1\] c.f.m.controller.ArticleController : \[컨트롤러\] getPaginationBarNumbers에서 패스
2024-02-21T10:31:29.094+09:00 ERROR 18720 --- \[nio-8082-exec-1\] o.a.c.c.C.\[.\[.\[/\].\[dispatcherServlet\] : Servlet.service() for servlet \[dispatcherServlet\] in context with path \[\] threw exception \[Handler dispatch failed: java.lang.NoClassDefFoundError: javax/persistence/NoResultException\] with root cause
java.lang.ClassNotFoundException: javax.persistence.NoResultException
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) \~\[na:na\]
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) \~\[na:na\]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520) \~\[na:na\]
at org.springframework.data.jpa.repository.support.Querydsl.createQuery(Querydsl.java:84) \~\[spring-data-jpa-3.1.8.jar:3.1.8\]
at org.springframework.data.jpa.repository.support.Querydsl.createQuery(Querydsl.java:100) \~\[spring-data-jpa-3.1.8.jar:3.1.8\]
at org.springframework.data.jpa.repository.support.QuerydslRepositorySupport.from(QuerydslRepositorySupport.java:110) \~\[spring-data-jpa-3.1.8.jar:3.1.8\]
at com.fastcampus.mvcboardproject.repository.querydsl.ArticleRepositoryCustomImpl.findAllDistinctHashtags(ArticleRepositoryCustomImpl.java:21) \~\[classes/:na\]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) \~\[na:na\]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) \~\[na:na\]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) \~\[na:na\]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) \~\[na:na\]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343) \~\[spring-aop-6.0.16.jar:6.0.16\]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) \~\[spring-aop-6.0.16.jar:6.0.16\]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) \~\[spring-aop-6.0.16.jar:6.0.16\]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:751) \~\[spring-aop-6.0.16.jar:6.0.16\]
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137) \~\[spring-tx-6.0.16.jar:6.0.16\]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) \~\[spring-aop-6.0.16.jar:6.0.16\]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:751) \~\[spring-aop-6.0.16.jar:6.0.16\]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:703) \~\[spring-aop-6.0.16.jar:6.0.16\]
at com.fastcampus.mvcboardproject.repository.querydsl.ArticleRepositoryCustomImpl$$SpringCGLIB$$0.findAllDistinctHashtags(\<generated\>) \~\[classes/:na\]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) \~\[na:na\]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) \~\[na:na\]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) \~\[na:na\]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) \~\[na:na\]
at org.springframework.data.repository.core.support.RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0(RepositoryMethodInvoker.java:288) \~\[spring-data-commons-3.1.8.jar:3.1.8\]
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:136) \~\[spring-data-commons-3.1.8.jar:3.1.8\]
at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:120) \~\[spring-data-commons-3.1.8.jar:3.1.8\]
at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:516) \~\[spring-data-commons-3.1.8.jar:3.1.8\]
at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:285) \~\[spring-data-commons-3.1.8.jar:3.1.8\]
at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:628) \~\[spring-data-commons-3.1.8.jar:3.1.8\]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) \~\[spring-aop-6.0.16.jar:6.0.16\]
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:168) \~\[spring-data-commons-3.1.8.jar:3.1.8\]
at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:143) \~\[spring-data-commons-3.1.8.jar:3.1.8\]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) \~\[spring-aop-6.0.16.jar:6.0.16\]
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:70) \~\[spring-data-commons-3.1.8.jar:3.1.8\]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) \~\[spring-aop-6.0.16.jar:6.0.16\]
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) \~\[spring-tx-6.0.16.jar:6.0.16\]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:391) \~\[spring-tx-6.0.16.jar:6.0.16\]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) \~\[spring-tx-6.0.16.jar:6.0.16\]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) \~\[spring-aop-6.0.16.jar:6.0.16\]
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137) \~\[spring-tx-6.0.16.jar:6.0.16\]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) \~\[spring-aop-6.0.16.jar:6.0.16\]
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:164) \~\[spring-data-jpa-3.1.8.jar:3.1.8\]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) \~\[spring-aop-6.0.16.jar:6.0.16\]
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) \~\[spring-aop-6.0.16.jar:6.0.16\]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) \~\[spring-aop-6.0.16.jar:6.0.16\]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:244) \~\[spring-aop-6.0.16.jar:6.0.16\]
at jdk.proxy4/jdk.proxy4.$Proxy163.findAllDistinctHashtags(Unknown Source) \~\[na:na\]
at com.fastcampus.mvcboardproject.service.ArticleService.getHashtags(ArticleService.java:101) \~\[classes/:na\]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) \~\[na:na\]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) \~\[na:na\]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) \~\[na:na\]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) \~\[na:na\]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343) \~\[spring-aop-6.0.16.jar:6.0.16\]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) \~\[spring-aop-6.0.16.jar:6.0.16\]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) \~\[spring-aop-6.0.16.jar:6.0.16\]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:751) \~\[spring-aop-6.0.16.jar:6.0.16\]
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) \~\[spring-tx-6.0.16.jar:6.0.16\]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:391) \~\[spring-tx-6.0.16.jar:6.0.16\]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) \~\[spring-tx-6.0.16.jar:6.0.16\]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) \~\[spring-aop-6.0.16.jar:6.0.16\]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:751) \~\[spring-aop-6.0.16.jar:6.0.16\]
I finally fixed the error after two days of struggling. Here's my solution in case someone else faces the same problem:
Make sure to check the dependencies. I resolved the error by changing from
to
It seems that Querydsl couldn't locate the jakarta.persistence.api when only specifying implementation "com.querydsl:querydsl-jpa".
This adjustment worked for me. Have fun coding away, folks!