diff --git a/pom.xml b/pom.xml
index ae43a190..1874414b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -78,7 +78,7 @@
1.8
- 2.2.2
+ main-snapshot
https://repo1.maven.org/maven2
diff --git a/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/AvoidNPlusOneProblemInJPAEntitiesCheckIssue.java b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/AvoidNPlusOneProblemInJPAEntitiesCheckIssue.java
new file mode 100644
index 00000000..4c698a6f
--- /dev/null
+++ b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/AvoidNPlusOneProblemInJPAEntitiesCheckIssue.java
@@ -0,0 +1,109 @@
+/*
+ * creedengo - Java language - Provides rules to reduce the environmental footprint of your Java programs
+ * Copyright © 2024 Green Code Initiative (https://green-code-initiative.org/)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.greencodeinitiative.creedengo.java.checks;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.*;
+
+public class AvoidNPlusOneProblemInJPAEntitiesCheckIssue {
+
+ @Autowired
+ private AuthorRepository authorRepository;
+
+ public List smellGetAllAuthors() {
+ List authors = authorRepository.findAll();
+ for (Author author : authors) {
+ List books = author.getBooks(); // Noncompliant {{ Detection of the "N+1 problem" on Spring Data JPA repositories }}
+ }
+ return authors;
+ }
+
+
+ public class Author {
+
+ private Long id;
+ private String name;
+
+ private List books;
+
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public List getBooks() {
+ return books;
+ }
+
+ public void setBooks(List books) {
+ this.books = books;
+ }
+ }
+
+
+ public class Book {
+
+ private Long id;
+ private String title;
+
+ private Author author;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public Author getAuthor() {
+ return author;
+ }
+
+ public void setAuthor(Author author) {
+ this.author = author;
+ }
+ }
+
+ public interface AuthorRepository extends JpaRepository {
+
+ }
+
+}
diff --git a/src/main/java/org/greencodeinitiative/creedengo/java/JavaCheckRegistrar.java b/src/main/java/org/greencodeinitiative/creedengo/java/JavaCheckRegistrar.java
index 791f0cef..0fe301ae 100644
--- a/src/main/java/org/greencodeinitiative/creedengo/java/JavaCheckRegistrar.java
+++ b/src/main/java/org/greencodeinitiative/creedengo/java/JavaCheckRegistrar.java
@@ -50,7 +50,8 @@ public class JavaCheckRegistrar implements CheckRegistrar {
FreeResourcesOfAutoCloseableInterface.class,
AvoidMultipleIfElseStatement.class,
UseOptionalOrElseGetVsOrElse.class,
- MakeNonReassignedVariablesConstants.class
+ MakeNonReassignedVariablesConstants.class,
+ AvoidNPlusOneProblemInJPAEntitiesCheck.class
);
/**
diff --git a/src/main/java/org/greencodeinitiative/creedengo/java/checks/AvoidNPlusOneProblemInJPAEntitiesCheck.java b/src/main/java/org/greencodeinitiative/creedengo/java/checks/AvoidNPlusOneProblemInJPAEntitiesCheck.java
new file mode 100644
index 00000000..c06c3bf4
--- /dev/null
+++ b/src/main/java/org/greencodeinitiative/creedengo/java/checks/AvoidNPlusOneProblemInJPAEntitiesCheck.java
@@ -0,0 +1,102 @@
+package org.greencodeinitiative.creedengo.java.checks;
+
+import org.sonar.check.Rule;
+import org.sonar.plugins.java.api.IssuableSubscriptionVisitor;
+import org.sonar.plugins.java.api.semantic.MethodMatchers;
+import org.sonar.plugins.java.api.semantic.Symbol;
+import org.sonar.plugins.java.api.semantic.Symbol.VariableSymbol;
+import org.sonar.plugins.java.api.tree.*;
+
+import java.util.*;
+
+@Rule(key = "GCI604")
+public class AvoidNPlusOneProblemInJPAEntitiesCheck extends IssuableSubscriptionVisitor {
+
+ protected static final String RULE_MESSAGE = " Detection of the \"N+1 problem\" on Spring Data JPA repositories ";
+
+ private static final String SPRING_REPOSITORY = "org.springframework.data.repository.Repository";
+
+ private static final MethodMatchers SPRING_REPOSITORY_METHOD_FIND_ALL =
+ MethodMatchers.create()
+ .ofSubTypes(SPRING_REPOSITORY)
+ .names("findAll")
+ .withAnyParameters()
+ .build();
+
+ private final Map repositoryFindAllVars = new HashMap<>();
+
+ @Override
+ public List nodesToVisit() {
+ return Arrays.asList(Tree.Kind.VARIABLE, Tree.Kind.METHOD_INVOCATION, Tree.Kind.FOR_EACH_STATEMENT);
+ }
+
+ @Override
+ public void visitNode(Tree tree) {
+ if (tree.is(Tree.Kind.VARIABLE)) {
+ VariableTree variableTree = (VariableTree) tree;
+ ExpressionTree initializer = variableTree.initializer();
+ if (initializer != null && initializer.is(Tree.Kind.METHOD_INVOCATION)) {
+ MethodInvocationTree methodInvocation = (MethodInvocationTree) initializer;
+ if (SPRING_REPOSITORY_METHOD_FIND_ALL.matches(methodInvocation)) {
+ VariableSymbol symbol = (VariableSymbol) variableTree.symbol();
+ repositoryFindAllVars.put(symbol, tree);
+ }
+ }
+ }
+
+ // Cas d'un foreach sur une variable issue de findAll()
+ if (tree.is(Tree.Kind.FOR_EACH_STATEMENT)) {
+ ForEachStatement forEach = (ForEachStatement) tree;
+ ExpressionTree iterable = forEach.expression();
+ if (iterable.is(Tree.Kind.IDENTIFIER)) {
+ Symbol symbol = ((IdentifierTree) iterable).symbol();
+ if (repositoryFindAllVars.containsKey(symbol)) {
+ // On marque la variable d'itération comme issue d'un findAll()
+ VariableSymbol loopVar = (VariableSymbol) forEach.variable().symbol();
+ repositoryFindAllVars.put(loopVar, tree);
+ }
+ }
+ }
+
+ // Détection d'un appel de getter sur une variable issue de findAll()
+ if (tree.is(Tree.Kind.METHOD_INVOCATION)) {
+ MethodInvocationTree methodInvocation = (MethodInvocationTree) tree;
+
+ // Check if the call is something like post.getAuthor() or post.getAuthor().getName()
+ ExpressionTree select = methodInvocation.methodSelect();
+ if (select.is(Tree.Kind.MEMBER_SELECT)) {
+ MemberSelectExpressionTree memberSelect = (MemberSelectExpressionTree) select;
+ ExpressionTree root = memberSelect.expression();
+
+ if (root.is(Tree.Kind.IDENTIFIER)) {
+ Symbol symbol = ((IdentifierTree) root).symbol();
+ if (repositoryFindAllVars.containsKey(symbol) && isGetter(memberSelect.identifier().name())) {
+ reportIssue(methodInvocation, RULE_MESSAGE);
+ }
+ }
+
+ // Handle nested getter chains (e.g., post.getAuthor().getName())
+ if (root.is(Tree.Kind.METHOD_INVOCATION)) {
+ MethodInvocationTree rootInvocation = (MethodInvocationTree) root;
+ ExpressionTree deeperSelect = rootInvocation.methodSelect();
+ if (deeperSelect.is(Tree.Kind.MEMBER_SELECT)) {
+ MemberSelectExpressionTree deeperMemberSelect = (MemberSelectExpressionTree) deeperSelect;
+ ExpressionTree deeperRoot = deeperMemberSelect.expression();
+
+ if (deeperRoot.is(Tree.Kind.IDENTIFIER)) {
+ Symbol rootSymbol = ((IdentifierTree) deeperRoot).symbol();
+ if (repositoryFindAllVars.containsKey(rootSymbol) && isGetter(deeperMemberSelect.identifier().name())) {
+ reportIssue(methodInvocation, RULE_MESSAGE);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Méthode utilitaire pour détecter un getter
+ private boolean isGetter(String methodName) {
+ return methodName.startsWith("get") && methodName.length() > 3 && Character.isUpperCase(methodName.charAt(3));
+ }
+}
diff --git a/src/main/resources/org/greencodeinitiative/creedengo/java/creedengo_way_profile.json b/src/main/resources/org/greencodeinitiative/creedengo/java/creedengo_way_profile.json
index 059bf0f5..299073b9 100644
--- a/src/main/resources/org/greencodeinitiative/creedengo/java/creedengo_way_profile.json
+++ b/src/main/resources/org/greencodeinitiative/creedengo/java/creedengo_way_profile.json
@@ -18,6 +18,7 @@
"GCI78",
"GCI79",
"GCI82",
- "GCI94"
+ "GCI94",
+ "GCI604"
]
}
diff --git a/src/test/files/AvoidNPlusOneProblemInJPAEntitiesCheckIssue.java b/src/test/files/AvoidNPlusOneProblemInJPAEntitiesCheckIssue.java
new file mode 100644
index 00000000..4c698a6f
--- /dev/null
+++ b/src/test/files/AvoidNPlusOneProblemInJPAEntitiesCheckIssue.java
@@ -0,0 +1,109 @@
+/*
+ * creedengo - Java language - Provides rules to reduce the environmental footprint of your Java programs
+ * Copyright © 2024 Green Code Initiative (https://green-code-initiative.org/)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.greencodeinitiative.creedengo.java.checks;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.*;
+
+public class AvoidNPlusOneProblemInJPAEntitiesCheckIssue {
+
+ @Autowired
+ private AuthorRepository authorRepository;
+
+ public List smellGetAllAuthors() {
+ List authors = authorRepository.findAll();
+ for (Author author : authors) {
+ List books = author.getBooks(); // Noncompliant {{ Detection of the "N+1 problem" on Spring Data JPA repositories }}
+ }
+ return authors;
+ }
+
+
+ public class Author {
+
+ private Long id;
+ private String name;
+
+ private List books;
+
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public List getBooks() {
+ return books;
+ }
+
+ public void setBooks(List books) {
+ this.books = books;
+ }
+ }
+
+
+ public class Book {
+
+ private Long id;
+ private String title;
+
+ private Author author;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public Author getAuthor() {
+ return author;
+ }
+
+ public void setAuthor(Author author) {
+ this.author = author;
+ }
+ }
+
+ public interface AuthorRepository extends JpaRepository {
+
+ }
+
+}
diff --git a/src/test/java/org/greencodeinitiative/creedengo/java/checks/AvoidNPlusOneProblemInJPAEntitiesCheckTest.java b/src/test/java/org/greencodeinitiative/creedengo/java/checks/AvoidNPlusOneProblemInJPAEntitiesCheckTest.java
new file mode 100644
index 00000000..a960c569
--- /dev/null
+++ b/src/test/java/org/greencodeinitiative/creedengo/java/checks/AvoidNPlusOneProblemInJPAEntitiesCheckTest.java
@@ -0,0 +1,35 @@
+/*
+ * creedengo - Java language - Provides rules to reduce the environmental footprint of your Java programs
+ * Copyright © 2024 Green Code Initiative (https://green-code-initiative.org/)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.greencodeinitiative.creedengo.java.checks;
+
+import org.greencodeinitiative.creedengo.java.utils.FilesUtils;
+import org.junit.jupiter.api.Test;
+import org.sonar.java.checks.verifier.CheckVerifier;
+
+class AvoidNPlusOneProblemInJPAEntitiesCheckTest {
+
+ @Test
+ void test() {
+ CheckVerifier.newVerifier()
+ .onFile("src/test/files/AvoidNPlusOneProblemInJPAEntitiesCheckIssue.java")
+ .withCheck(new AvoidNPlusOneProblemInJPAEntitiesCheck())
+ .withClassPath(FilesUtils.getClassPath("target/test-jars"))
+ .verifyIssues();
+ }
+
+}