我相信我已经正确设置了 Spring Boot 版本 3.1.8 的依赖项,如下面的 build.gradle 代码所示。
我正在尝试实现一项功能,将文章中的唯一主题标签列表发送给用户。
为了实现这一目标,我使用 Querydsl 自定义存储库以从 Article 表中获取此数据
我还将此功能添加到服务层和控制器层。我使用 Thymeleaf 将此数据发送给用户,并且还编写了 HTML。
虽然我的所有测试都通过了,但该功能在实际应用程序中不起作用。有人可以帮我找到解决方案吗?
我确认所有存储库都指定了“最终”,但问题仍然存在。
其他 Querydsl 查询(例如 findByContentContaining)运行良好。
我还尝试更改构建和运行设置。
[构建并运行:Intellij,运行并取消:Gradle]
[构建并运行:Intellij,运行并取消:Intellij]
[构建并运行:Gradle,运行并取消: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\]
经过两天的努力,我终于修复了这个错误。这是我的解决方案,以防其他人遇到同样的问题:
确保检查依赖关系。 我通过更改解决了该错误
implementation "com.querydsl:querydsl-jpa"
到
implementation "com.querydsl:querydsl-jpa:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
仅指定实现“com.querydsl:querydsl-jpa”时,Querydsl 似乎无法找到 jakarta.persistence.api。
这个调整对我有用。 伙计们,祝您编码愉快!