diff options
16 files changed, 214 insertions, 171 deletions
diff --git a/server/sonar-db-dao/src/it/java/org/sonar/db/component/ComponentDaoIT.java b/server/sonar-db-dao/src/it/java/org/sonar/db/component/ComponentDaoIT.java index 2299f88c60f..c3d4d5097cd 100644 --- a/server/sonar-db-dao/src/it/java/org/sonar/db/component/ComponentDaoIT.java +++ b/server/sonar-db-dao/src/it/java/org/sonar/db/component/ComponentDaoIT.java @@ -44,6 +44,7 @@ import org.sonar.api.resources.Scopes; import org.sonar.api.utils.System2; import org.sonar.db.DbSession; import org.sonar.db.DbTester; +import org.sonar.db.Pagination; import org.sonar.db.RowNotFoundException; import org.sonar.db.audit.AuditPersister; import org.sonar.db.audit.NoOpAuditPersister; @@ -79,6 +80,7 @@ import static org.sonar.api.resources.Qualifiers.PROJECT; import static org.sonar.api.resources.Qualifiers.SUBVIEW; import static org.sonar.api.resources.Qualifiers.VIEW; import static org.sonar.api.utils.DateUtils.parseDate; +import static org.sonar.db.Pagination.forPage; import static org.sonar.db.component.BranchDto.DEFAULT_MAIN_BRANCH_NAME; import static org.sonar.db.component.BranchType.BRANCH; import static org.sonar.db.component.BranchType.PULL_REQUEST; @@ -931,35 +933,35 @@ public class ComponentDaoIT { SnapshotDto analyzedPortfolio = db.components().insertProjectAndSnapshot(ComponentTesting.newPortfolio()); Supplier<ComponentQuery.Builder> query = () -> ComponentQuery.builder().setQualifiers(PROJECT).setOnProvisionedOnly(true); - assertThat(underTest.selectByQuery(dbSession, query.get().build(), 0, 10)) + assertThat(underTest.selectByQuery(dbSession, query.get().build(), forPage(1).andSize(10))) .extracting(ComponentDto::uuid) .containsOnly(provisionedProject.uuid()); // pagination - assertThat(underTest.selectByQuery(dbSession, query.get().build(), 2, 10)).isEmpty(); + assertThat(underTest.selectByQuery(dbSession, query.get().build(), forPage(3).andSize(10))).isEmpty(); // filter on qualifiers - assertThat(underTest.selectByQuery(dbSession, query.get().setQualifiers("XXX").build(), 0, 10)).isEmpty(); - assertThat(underTest.selectByQuery(dbSession, query.get().setQualifiers(PROJECT, "XXX").build(), 0, 10)) + assertThat(underTest.selectByQuery(dbSession, query.get().setQualifiers("XXX").build(), forPage(1).andSize(10))).isEmpty(); + assertThat(underTest.selectByQuery(dbSession, query.get().setQualifiers(PROJECT, "XXX").build(), forPage(1).andSize(10))) .extracting(ComponentDto::uuid) .containsOnly(provisionedProject.uuid()); - assertThat(underTest.selectByQuery(dbSession, query.get().setQualifiers(PROJECT, Qualifiers.VIEW).build(), 0, 10)) + assertThat(underTest.selectByQuery(dbSession, query.get().setQualifiers(PROJECT, Qualifiers.VIEW).build(), forPage(1).andSize(10))) .extracting(ComponentDto::uuid) .containsOnly(provisionedProject.uuid(), provisionedPortfolio.uuid()); // match key - assertThat(underTest.selectByQuery(dbSession, query.get().setNameOrKeyQuery(provisionedProject.getKey()).build(), 0, 10)) + assertThat(underTest.selectByQuery(dbSession, query.get().setNameOrKeyQuery(provisionedProject.getKey()).build(), forPage(1).andSize(10))) .extracting(ComponentDto::uuid) .containsExactly(provisionedProject.uuid()); - assertThat(underTest.selectByQuery(dbSession, query.get().setNameOrKeyQuery("pROvisiONed.proJEcT").setPartialMatchOnKey(true).build(), 0, 10)) + assertThat(underTest.selectByQuery(dbSession, query.get().setNameOrKeyQuery("pROvisiONed.proJEcT").setPartialMatchOnKey(true).build(), forPage(1).andSize(10))) .extracting(ComponentDto::uuid) .containsExactly(provisionedProject.uuid()); - assertThat(underTest.selectByQuery(dbSession, query.get().setNameOrKeyQuery("missing").setPartialMatchOnKey(true).build(), 0, 10)).isEmpty(); - assertThat(underTest.selectByQuery(dbSession, query.get().setNameOrKeyQuery("to be escaped '\"\\%").setPartialMatchOnKey(true).build(), 0, 10)) + assertThat(underTest.selectByQuery(dbSession, query.get().setNameOrKeyQuery("missing").setPartialMatchOnKey(true).build(), forPage(1).andSize(10))).isEmpty(); + assertThat(underTest.selectByQuery(dbSession, query.get().setNameOrKeyQuery("to be escaped '\"\\%").setPartialMatchOnKey(true).build(), forPage(1).andSize(10))) .isEmpty(); // match name - assertThat(underTest.selectByQuery(dbSession, query.get().setNameOrKeyQuery("ned proj").setPartialMatchOnKey(true).build(), 0, 10)) + assertThat(underTest.selectByQuery(dbSession, query.get().setNameOrKeyQuery("ned proj").setPartialMatchOnKey(true).build(), forPage(1).andSize(10))) .extracting(ComponentDto::uuid) .containsExactly(provisionedProject.uuid()); } @@ -972,7 +974,7 @@ public class ComponentDaoIT { // the project does not have any analysis ComponentDto project = db.components().insertPublicProject().getMainBranchComponent(); - assertThat(underTest.selectByQuery(dbSession, query.get().build(), 0, 10)) + assertThat(underTest.selectByQuery(dbSession, query.get().build(), forPage(1).andSize(10))) .extracting(ComponentDto::uuid) .containsOnly(project.uuid()); @@ -981,7 +983,7 @@ public class ComponentDaoIT { ComponentDto branchWithoutAnalysis = db.components().insertProjectBranch(project); ComponentDto branchWithAnalysis = db.components().insertProjectBranch(project); db.components().insertSnapshot(branchWithAnalysis); - assertThat(underTest.selectByQuery(dbSession, query.get().build(), 0, 10)) + assertThat(underTest.selectByQuery(dbSession, query.get().build(), forPage(1).andSize(10))) .isEmpty(); } @@ -999,7 +1001,7 @@ public class ComponentDaoIT { .setQualifiers(PROJECT) .setOnProvisionedOnly(true); - List<ComponentDto> results = underTest.selectByQuery(dbSession, query.get().build(), 0, 10); + List<ComponentDto> results = underTest.selectByQuery(dbSession, query.get().build(), forPage(1).andSize(10)); assertThat(results) .extracting(ComponentDto::uuid) .containsExactly( @@ -1172,7 +1174,8 @@ public class ComponentDaoIT { private void assertThatSelectByQueryThrowsIAE(ComponentQuery.Builder query, String expectedMessage) { ComponentQuery componentQuery = query.build(); - assertThatThrownBy(() -> underTest.selectByQuery(dbSession, componentQuery, 0, Integer.MAX_VALUE)) + Pagination pagination = forPage(1).andSize(Integer.MAX_VALUE); + assertThatThrownBy(() -> underTest.selectByQuery(dbSession, componentQuery, pagination)) .isInstanceOf(IllegalArgumentException.class) .hasMessage(expectedMessage); } @@ -1186,12 +1189,12 @@ public class ComponentDaoIT { } ComponentQuery query = ComponentQuery.builder().setNameOrKeyQuery("oJect").setQualifiers(PROJECT).build(); - List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 1, 3); + List<ComponentDto> result = underTest.selectByQuery(dbSession, query, forPage(2).andSize(3)); int count = underTest.countByQuery(dbSession, query); assertThat(result).hasSize(3); assertThat(count).isEqualTo(9); - assertThat(result).extracting(ComponentDto::name).containsExactly("project-2", "project-3", "project-4"); + assertThat(result).extracting(ComponentDto::name).containsExactly("project-4", "project-5", "project-6"); } @Test @@ -1199,8 +1202,8 @@ public class ComponentDaoIT { ComponentDto main = db.components().insertPublicProject().getMainBranchComponent(); ComponentDto branch = db.components().insertProjectBranch(main); - assertThat(underTest.selectByQuery(dbSession, ALL_PROJECTS_COMPONENT_QUERY, 0, 2)).hasSize(1); - assertThat(underTest.selectByQuery(dbSession, ALL_PROJECTS_COMPONENT_QUERY, 0, 2).get(0).uuid()).isEqualTo(main.uuid()); + assertThat(underTest.selectByQuery(dbSession, ALL_PROJECTS_COMPONENT_QUERY, forPage(1).andSize(2))).hasSize(1); + assertThat(underTest.selectByQuery(dbSession, ALL_PROJECTS_COMPONENT_QUERY, forPage(1).andSize(2)).get(0).uuid()).isEqualTo(main.uuid()); } @Test @@ -1216,7 +1219,7 @@ public class ComponentDaoIT { db.components().insertProjectAndSnapshot(newPrivateProjectDto().setName("project-\\_%/-name")); ComponentQuery query = ComponentQuery.builder().setNameOrKeyQuery("-\\_%/-").setQualifiers(PROJECT).build(); - List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 0, 10); + List<ComponentDto> result = underTest.selectByQuery(dbSession, query, forPage(1).andSize(10)); assertThat(result).hasSize(1); assertThat(result.get(0).name()).isEqualTo("project-\\_%/-name"); @@ -1228,7 +1231,7 @@ public class ComponentDaoIT { db.components().insertProjectAndSnapshot(newPrivateProjectDto().setKey("project-key-that-does-not-match")); ComponentQuery query = ComponentQuery.builder().setNameOrKeyQuery("project-_%-key").setQualifiers(PROJECT).build(); - List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 0, 10); + List<ComponentDto> result = underTest.selectByQuery(dbSession, query, forPage(1).andSize(10)); assertThat(result).hasSize(1); assertThat(result.get(0).getKey()).isEqualTo("project-_%-key"); @@ -1242,7 +1245,7 @@ public class ComponentDaoIT { .setNameOrKeyQuery("JECT-K") .setPartialMatchOnKey(true) .setQualifiers(PROJECT).build(); - List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 0, 10); + List<ComponentDto> result = underTest.selectByQuery(dbSession, query, forPage(1).andSize(10)); assertThat(result).hasSize(1); assertThat(result.get(0).getKey()).isEqualTo("project-key"); @@ -1267,19 +1270,11 @@ public class ComponentDaoIT { .containsExactlyInAnyOrder(oldProject.uuid(), recentProject.uuid()); // before date on any branch - assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedBefore(recentTime))) + assertThat(selectProjectUuidsByQuery(q -> q.setAllBranchesAnalyzedBefore(recentTime))) .containsExactlyInAnyOrder(oldProject.uuid()); - assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedBefore(aLongTimeAgo))) + assertThat(selectProjectUuidsByQuery(q -> q.setAllBranchesAnalyzedBefore(aLongTimeAgo))) .isEmpty(); - assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedBefore(recentTime + 1_000L))) - .containsExactlyInAnyOrder(oldProject.uuid(), recentProject.uuid()); - - // after date - assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedAfter(recentTime - 1_000L))) - .containsExactlyInAnyOrder(recentProject.uuid()); - assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedAfter(recentTime + 1_000L))) - .isEmpty(); - assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedAfter(aLongTimeAgo))) + assertThat(selectProjectUuidsByQuery(q -> q.setAllBranchesAnalyzedBefore(recentTime + 1_000L))) .containsExactlyInAnyOrder(oldProject.uuid(), recentProject.uuid()); } @@ -1306,19 +1301,11 @@ public class ComponentDaoIT { assertThat(selectProjectUuidsByQuery(q -> q.setAnalyzedBefore(recentTime + 1_000L))).isEmpty(); // before date on any branch - assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedBefore(recentTime))) + assertThat(selectProjectUuidsByQuery(q -> q.setAllBranchesAnalyzedBefore(recentTime))) .containsExactlyInAnyOrder(oldProject.uuid()); - assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedBefore(aLongTimeAgo))) - .isEmpty(); - assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedBefore(recentTime + 1_000L))) - .containsExactlyInAnyOrder(oldProject.uuid(), recentProject.uuid()); - - // after date - assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedAfter(recentTime - 1_000L))) - .containsExactlyInAnyOrder(recentProject.uuid()); - assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedAfter(recentTime + 1_000L))) + assertThat(selectProjectUuidsByQuery(q -> q.setAllBranchesAnalyzedBefore(aLongTimeAgo))) .isEmpty(); - assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedAfter(aLongTimeAgo))) + assertThat(selectProjectUuidsByQuery(q -> q.setAllBranchesAnalyzedBefore(recentTime + 1_000L))) .containsExactlyInAnyOrder(oldProject.uuid(), recentProject.uuid()); } @@ -1387,7 +1374,7 @@ public class ComponentDaoIT { private List<String> selectUuidsByQuery(String qualifier, Consumer<ComponentQuery.Builder> query) { ComponentQuery.Builder builder = ComponentQuery.builder().setQualifiers(qualifier); query.accept(builder); - return underTest.selectByQuery(dbSession, builder.build(), 0, 5) + return underTest.selectByQuery(dbSession, builder.build(), forPage(1).andSize(5)) .stream() .map(ComponentDto::uuid) .toList(); @@ -1402,9 +1389,9 @@ public class ComponentDaoIT { ComponentQuery publicProjectsQuery = ComponentQuery.builder().setPrivate(false).setQualifiers(PROJECT).build(); ComponentQuery allProjectsQuery = ComponentQuery.builder().setPrivate(null).setQualifiers(PROJECT).build(); - assertThat(underTest.selectByQuery(dbSession, privateProjectsQuery, 0, 10)).extracting(ComponentDto::getKey).containsExactly("private-key"); - assertThat(underTest.selectByQuery(dbSession, publicProjectsQuery, 0, 10)).extracting(ComponentDto::getKey).containsExactly("public-key"); - assertThat(underTest.selectByQuery(dbSession, allProjectsQuery, 0, 10)).extracting(ComponentDto::getKey).containsOnly("public-key", "private-key"); + assertThat(underTest.selectByQuery(dbSession, privateProjectsQuery, forPage(1).andSize(10))).extracting(ComponentDto::getKey).containsExactly("private-key"); + assertThat(underTest.selectByQuery(dbSession, publicProjectsQuery, forPage(1).andSize(10))).extracting(ComponentDto::getKey).containsExactly("public-key"); + assertThat(underTest.selectByQuery(dbSession, allProjectsQuery, forPage(1).andSize(10))).extracting(ComponentDto::getKey).containsOnly("public-key", "private-key"); } @Test @@ -1412,7 +1399,7 @@ public class ComponentDaoIT { db.components().insertPrivateProject().getMainBranchComponent(); ComponentQuery dbQuery = ComponentQuery.builder().setQualifiers(PROJECT).setComponentKeys(emptySet()).build(); - List<ComponentDto> result = underTest.selectByQuery(dbSession, dbQuery, 0, 10); + List<ComponentDto> result = underTest.selectByQuery(dbSession, dbQuery, forPage(1).andSize(10)); int count = underTest.countByQuery(dbSession, dbQuery); assertThat(result).isEmpty(); @@ -1427,7 +1414,7 @@ public class ComponentDaoIT { ComponentQuery query = ComponentQuery.builder().setQualifiers(PROJECT) .setComponentKeys(newHashSet(sonarqube.getKey(), jdk8.getKey())).build(); - List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 0, 10); + List<ComponentDto> result = underTest.selectByQuery(dbSession, query, forPage(1).andSize(10)); assertThat(result).hasSize(2).extracting(ComponentDto::getKey) .containsExactlyInAnyOrder(sonarqube.getKey(), jdk8.getKey()) @@ -1439,7 +1426,7 @@ public class ComponentDaoIT { db.components().insertPrivateProject().getMainBranchComponent(); ComponentQuery dbQuery = ComponentQuery.builder().setQualifiers(PROJECT).setComponentUuids(emptySet()).build(); - List<ComponentDto> result = underTest.selectByQuery(dbSession, dbQuery, 0, 10); + List<ComponentDto> result = underTest.selectByQuery(dbSession, dbQuery, forPage(1).andSize(10)); int count = underTest.countByQuery(dbSession, dbQuery); assertThat(result).isEmpty(); @@ -1454,7 +1441,7 @@ public class ComponentDaoIT { ComponentQuery query = ComponentQuery.builder().setQualifiers(PROJECT) .setComponentUuids(newHashSet(sonarqube.uuid(), jdk8.uuid())).build(); - List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 0, 10); + List<ComponentDto> result = underTest.selectByQuery(dbSession, query, forPage(1).andSize(10)); assertThat(result).hasSize(2).extracting(ComponentDto::uuid) .containsOnlyOnce(sonarqube.uuid(), jdk8.uuid()) diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java index a78e4545a75..0d64637e9e4 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java @@ -31,10 +31,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import javax.annotation.Nullable; import org.apache.ibatis.session.ResultHandler; -import org.apache.ibatis.session.RowBounds; import org.sonar.api.resources.Qualifiers; import org.sonar.db.Dao; import org.sonar.db.DbSession; +import org.sonar.db.Pagination; import org.sonar.db.RowNotFoundException; import org.sonar.db.audit.AuditPersister; import org.sonar.db.audit.model.ComponentNewValue; @@ -104,8 +104,8 @@ public class ComponentDao implements Dao { * @throws IllegalArgumentException if parameter query#getComponentKeys() has more than {@link org.sonar.db.DatabaseUtils#PARTITION_SIZE_FOR_ORACLE} values * @throws IllegalArgumentException if parameter query#getMainComponentUuids() has more than {@link org.sonar.db.DatabaseUtils#PARTITION_SIZE_FOR_ORACLE} values */ - public List<ComponentDto> selectByQuery(DbSession dbSession, ComponentQuery query, int offset, int limit) { - return selectByQueryImpl(dbSession, query, offset, limit); + public List<ComponentDto> selectByQuery(DbSession dbSession, ComponentQuery query, Pagination pagination) { + return selectByQueryImpl(dbSession, query, pagination); } /** @@ -117,12 +117,12 @@ public class ComponentDao implements Dao { return countByQueryImpl(session, query); } - private static List<ComponentDto> selectByQueryImpl(DbSession session, ComponentQuery query, int offset, int limit) { + private static List<ComponentDto> selectByQueryImpl(DbSession session, ComponentQuery query, Pagination pagination) { if (query.hasEmptySetOfComponents()) { return emptyList(); } checkThatNotTooManyComponents(query); - return mapper(session).selectByQuery(query, new RowBounds(offset, limit)); + return mapper(session).selectByQuery(query, pagination); } private static int countByQueryImpl(DbSession session, ComponentQuery query) { diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java index 25424e75349..e09c48a58f9 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java @@ -26,7 +26,7 @@ import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.session.ResultHandler; -import org.apache.ibatis.session.RowBounds; +import org.sonar.db.Pagination; public interface ComponentMapper { @CheckForNull @@ -54,7 +54,7 @@ public interface ComponentMapper { List<ComponentDto> selectComponentsByQualifiers(@Param("qualifiers") Collection<String> qualifiers); - List<ComponentDto> selectByQuery(@Param("query") ComponentQuery query, RowBounds rowBounds); + List<ComponentDto> selectByQuery(@Param("query") ComponentQuery query, @Param("pagination") Pagination pagination); int countByQuery(@Param("query") ComponentQuery query); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentQuery.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentQuery.java index 7baf5d3c409..507cd691700 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentQuery.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentQuery.java @@ -38,8 +38,6 @@ public class ComponentQuery { private final Set<String> componentUuids; private final Set<String> componentKeys; private final Long analyzedBefore; - private final Long anyBranchAnalyzedBefore; - private final Long anyBranchAnalyzedAfter; private final Long allBranchesAnalyzedBefore; private final Date createdAfter; private final boolean onProvisionedOnly; @@ -52,8 +50,6 @@ public class ComponentQuery { this.componentKeys = builder.componentKeys; this.isPrivate = builder.isPrivate; this.analyzedBefore = builder.analyzedBefore; - this.anyBranchAnalyzedBefore = builder.anyBranchAnalyzedBefore; - this.anyBranchAnalyzedAfter = builder.anyBranchAnalyzedAfter; this.allBranchesAnalyzedBefore = builder.allBranchesAnalyzedBefore; this.createdAfter = builder.createdAfter; this.onProvisionedOnly = builder.onProvisionedOnly; @@ -104,16 +100,6 @@ public class ComponentQuery { } @CheckForNull - public Long getAnyBranchAnalyzedBefore() { - return anyBranchAnalyzedBefore; - } - - @CheckForNull - public Long getAnyBranchAnalyzedAfter() { - return anyBranchAnalyzedAfter; - } - - @CheckForNull public Long getAllBranchesAnalyzedBefore() { return allBranchesAnalyzedBefore; } @@ -144,8 +130,6 @@ public class ComponentQuery { private Set<String> componentUuids; private Set<String> componentKeys; private Long analyzedBefore; - private Long anyBranchAnalyzedBefore; - private Long anyBranchAnalyzedAfter; private Long allBranchesAnalyzedBefore; private Date createdAfter; private boolean onProvisionedOnly = false; @@ -193,21 +177,6 @@ public class ComponentQuery { return this; } - /** - * Filter on date of last analysis. On projects, all branches and pull requests are taken into - * account. For example the analysis of a branch is included in the filter - * even if the main branch has never been analyzed. - */ - public Builder setAnyBranchAnalyzedAfter(@Nullable Long l) { - this.anyBranchAnalyzedAfter = l; - return this; - } - - public Builder setAnyBranchAnalyzedBefore(@Nullable Long l) { - this.anyBranchAnalyzedBefore = l; - return this; - } - public Builder setCreatedAfter(@Nullable Date l) { this.createdAfter = l; return this; diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml index e55f64f3fde..6dffe0b6848 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml @@ -210,19 +210,34 @@ </select> <select id="selectByQuery" resultType="Component"> + <include refid="mainBranchAndPortfoliosWith"/> select <include refid="componentColumns"/> <include refid="sqlSelectByQuery"/> ORDER BY LOWER(p.name), p.name, p.created_at + offset (#{pagination.startRowNumber,jdbcType=INTEGER}-1) rows fetch next #{pagination.pageSize,jdbcType=INTEGER} rows only </select> <select id="countByQuery" resultType="int"> + <include refid="mainBranchAndPortfoliosWith"/> select count(p.uuid) <include refid="sqlSelectByQuery"/> </select> + <sql id="mainBranchAndPortfoliosWith"> + WITH main_branch_and_portfolios AS (SELECT p.uuid as projectUuid, p.kee as kee, p.uuid + from portfolios p + UNION + SELECT pb.project_uuid as projectUuid, p.kee as kee, pb.uuid + FROM project_branches pb + INNER JOIN projects p on pb.project_uuid = p.uuid + where pb.is_main = ${_true} + ) + </sql> + <sql id="sqlSelectByQuery"> - from components p + from main_branch_and_portfolios mbp + INNER JOIN components p on mbp.kee = p.kee and p.branch_uuid = mbp.uuid <if test="query.analyzedBefore!=null"> inner join snapshots sa on sa.root_component_uuid=p.uuid and sa.status='P' and sa.islast=${_true} and sa.created_at < #{query.analyzedBefore,jdbcType=BIGINT} @@ -230,8 +245,6 @@ left join project_branches pb on pb.uuid = p.branch_uuid where p.enabled=${_true} - AND <include refid="mainBranchOrPortfolio"/> - AND p.copy_component_uuid is null <if test="query.qualifiers!=null"> and p.qualifier in <foreach collection="query.qualifiers" item="qualifier" open="(" close=")" separator=","> @@ -280,48 +293,6 @@ where pb2.project_uuid = pb.project_uuid ) </if> - <if test="query.anyBranchAnalyzedAfter != null"> - and ( - exists( - -- branches of projects and applications - select 1 from snapshots s - inner join project_branches pb2 on s.root_component_uuid = pb2.uuid - where pb2.project_uuid = pb.project_uuid - and s.status='P' - and s.islast = ${_true} - and s.created_at >= #{query.anyBranchAnalyzedAfter,jdbcType=BIGINT} - ) - or exists ( - -- portfolios - select 1 from snapshots s - where s.root_component_uuid = p.uuid - and s.status='P' - and s.islast = ${_true} - and s.created_at >= #{query.anyBranchAnalyzedAfter,jdbcType=BIGINT} - ) - ) - </if> - <if test="query.anyBranchAnalyzedBefore != null"> - and ( - exists( - -- branches of projects and applications - select 1 from snapshots s - inner join project_branches pb2 on s.root_component_uuid = pb2.uuid - where pb2.project_uuid = pb.project_uuid - and s.status='P' - and s.islast = ${_true} - and s.created_at < #{query.anyBranchAnalyzedBefore,jdbcType=BIGINT} - ) - or exists ( - -- portfolios - select 1 from snapshots s - where s.root_component_uuid = p.uuid - and s.status='P' - and s.islast = ${_true} - and s.created_at < #{query.anyBranchAnalyzedBefore,jdbcType=BIGINT} - ) - ) - </if> <if test="query.allBranchesAnalyzedBefore != null"> and ( diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/SnapshotMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/SnapshotMapper.xml index 259d9da533a..5a2356a79ab 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/SnapshotMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/SnapshotMapper.xml @@ -54,38 +54,54 @@ and coalesce(pb.project_uuid, p.branch_uuid) = #{projectUuid,jdbcType=VARCHAR} </select> - <select id="selectLastAnalysisDateByProjectUuids" resultType="org.sonar.db.component.ProjectLastAnalysisDateDto"> + <select id="selectLastAnalysisDateByProjectUuids" resultType="org.sonar.db.component.ProjectLastAnalysisDateDto"> select - result_with_duplicates.project_uuid as project_uuid , + result_with_duplicates.project_uuid as project_uuid, max(result_with_duplicates.last_analysis_date) as last_analysis_date - from + FROM ( - select - coalesce(pb.project_uuid, c.branch_uuid) as project_uuid, - s.created_at as last_analysis_date - from - snapshots s - inner join components c on s.root_component_uuid = c.branch_uuid - left join project_branches pb on pb.uuid = c.branch_uuid - where - s.islast = ${_true} - and ( - <!-- case of an analysis of a project or app, with entries in project_branches --> - (pb.uuid is not null and pb.project_uuid in - <foreach collection="projectUuids" item="projectUuid" separator="," open="(" close=")"> - #{projectUuid,jdbcType=VARCHAR} - </foreach>) - or - <!-- case of an analysis of a portfolio, where there are no branches --> - (pb.uuid is null and c.branch_uuid in - <foreach collection="projectUuids" item="projectUuid" separator="," open="(" close=")"> - #{projectUuid,jdbcType=VARCHAR} - </foreach>) - ) + <!-- cases of an analysis of a project or app, with entries in project_branches --> + SELECT project_uuid, last_analysis_date + FROM ( + SELECT + COALESCE(pb.project_uuid, c.branch_uuid) AS project_uuid, + s.created_at AS last_analysis_date + FROM snapshots s + INNER JOIN components c ON s.root_component_uuid = c.branch_uuid + LEFT JOIN project_branches pb ON pb.uuid = c.branch_uuid + WHERE + s.islast = ${_true} + AND pb.uuid IS NOT NULL + AND pb.project_uuid IN + <foreach collection="projectUuids" item="projectUuid" separator="," open="(" close=")"> + #{projectUuid,jdbcType=VARCHAR} + </foreach> + ) project_case + + UNION + + <!-- cases of an analysis of a portfolio, where there are no branches --> + SELECT project_uuid, last_analysis_date + FROM ( + SELECT + COALESCE(pb.project_uuid, c.branch_uuid) AS project_uuid, + s.created_at AS last_analysis_date + FROM snapshots s + INNER JOIN components c ON s.root_component_uuid = c.branch_uuid + LEFT JOIN project_branches pb ON pb.uuid = c.branch_uuid + WHERE + s.islast = ${_true} + AND pb.branch_type IS NULL + AND pb.uuid IS NULL + AND c.branch_uuid IN + <foreach collection="projectUuids" item="projectUuid" separator="," open="(" close=")"> + #{projectUuid,jdbcType=VARCHAR} + </foreach> + ) portfolio_case ) result_with_duplicates - group by - result_with_duplicates.project_uuid + GROUP BY project_uuid </select> + <select id="selectLastSnapshotByRootComponentUuid" resultType="Snapshot"> select <include refid="snapshotColumns" /> from snapshots s diff --git a/server/sonar-db-dao/src/schema/schema-sq.ddl b/server/sonar-db-dao/src/schema/schema-sq.ddl index 7944ece37b2..5721307e3f7 100644 --- a/server/sonar-db-dao/src/schema/schema-sq.ddl +++ b/server/sonar-db-dao/src/schema/schema-sq.ddl @@ -690,6 +690,7 @@ CREATE TABLE "PROJECT_BRANCHES"( ); ALTER TABLE "PROJECT_BRANCHES" ADD CONSTRAINT "PK_PROJECT_BRANCHES" PRIMARY KEY("UUID"); CREATE UNIQUE INDEX "UNIQ_PROJECT_BRANCHES" ON "PROJECT_BRANCHES"("BRANCH_TYPE" NULLS FIRST, "PROJECT_UUID" NULLS FIRST, "KEE" NULLS FIRST); +CREATE INDEX "PROJECT_BRANCHES_PROJECT_UUID" ON "PROJECT_BRANCHES"("PROJECT_UUID" NULLS FIRST); CREATE TABLE "PROJECT_LINKS"( "UUID" CHARACTER VARYING(40) NOT NULL, diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentQueryTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentQueryTest.java index ced666d7df6..bef9bf62895 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentQueryTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentQueryTest.java @@ -36,16 +36,14 @@ public class ComponentQueryTest { public void build_query() { ComponentQuery underTest = ComponentQuery.builder() .setNameOrKeyQuery("key") - .setAnyBranchAnalyzedBefore(100L) - .setAnyBranchAnalyzedAfter(200L) + .setAllBranchesAnalyzedBefore(100L) .setCreatedAfter(new Date(300L)) .setQualifiers(PROJECT) .build(); assertThat(underTest.getNameOrKeyQuery()).isEqualTo("key"); assertThat(underTest.getQualifiers()).containsOnly(PROJECT); - assertThat(underTest.getAnyBranchAnalyzedBefore()).isEqualTo(100L); - assertThat(underTest.getAnyBranchAnalyzedAfter()).isEqualTo(200L); + assertThat(underTest.getAllBranchesAnalyzedBefore()).isEqualTo(100L); assertThat(underTest.getCreatedAfter().getTime()).isEqualTo(300L); assertThat(underTest.isOnProvisionedOnly()).isFalse(); assertThat(underTest.isPartialMatchOnKey()).isFalse(); diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v102/CreateIndexProjectUuidInProjectBranches.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v102/CreateIndexProjectUuidInProjectBranches.java new file mode 100644 index 00000000000..552b0717b87 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v102/CreateIndexProjectUuidInProjectBranches.java @@ -0,0 +1,33 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.db.migration.version.v102; + +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.step.CreateIndexOnColumn; + +class CreateIndexProjectUuidInProjectBranches extends CreateIndexOnColumn { + + private static final String TABLE_NAME = "project_branches"; + private static final String COLUMN_NAME = "project_uuid"; + + public CreateIndexProjectUuidInProjectBranches(Database db) { + super(db, TABLE_NAME, COLUMN_NAME, false); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v102/DbVersion102.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v102/DbVersion102.java index 2ee6cb2a90d..60d608993dd 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v102/DbVersion102.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v102/DbVersion102.java @@ -25,7 +25,6 @@ import org.sonar.server.platform.db.migration.version.DbVersion; // ignoring bad number formatting, as it's indented that we align the migration numbers to SQ versions @SuppressWarnings("java:S3937") public class DbVersion102 implements DbVersion { - /** * We use the start of the 10.X cycle as an opportunity to align migration numbers with the SQ version number. * Please follow this pattern: @@ -98,6 +97,7 @@ public class DbVersion102 implements DbVersion { .add(10_2_043, "Create 'previous_non_compliant_value' in 'new_code_periods' table", CreatePreviousNonCompliantValueInNewCodePeriods.class) .add(10_2_044, "Update column 'value' and populate column 'previous_non_compliant_value' in 'new_code_periods' table", UpdateValueAndPopulatePreviousNonCompliantValueInNewCodePeriods.class) - .add(10_2_045, "Alter 'project_uuid' in 'user_dismissed_messages' - make it nullable", MakeProjectUuidNullableInUserDismissedMessages.class); + .add(10_2_045, "Alter 'project_uuid' in 'user_dismissed_messages' - make it nullable", MakeProjectUuidNullableInUserDismissedMessages.class) + .add(10_2_046, "Create index 'project_branches_project_uuid' in 'project_branches' table", CreateIndexProjectUuidInProjectBranches.class); } } diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v102/CreateIndexProjectUuidInProjectBranchesTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v102/CreateIndexProjectUuidInProjectBranchesTest.java new file mode 100644 index 00000000000..bb8a50d25ca --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v102/CreateIndexProjectUuidInProjectBranchesTest.java @@ -0,0 +1,49 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.db.migration.version.v102; + +import java.sql.SQLException; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.db.CoreDbTester; + +public class CreateIndexProjectUuidInProjectBranchesTest { + @Rule + public final CoreDbTester db = CoreDbTester.createForSchema(CreateIndexProjectUuidInProjectBranchesTest.class, "schema.sql"); + + private final CreateIndexProjectUuidInProjectBranches createIndex = new CreateIndexProjectUuidInProjectBranches(db.database()); + + @Test + public void migration_should_create_index() throws SQLException { + db.assertIndexDoesNotExist("project_branches", "project_branches_project_uuid"); + + createIndex.execute(); + + db.assertIndex("project_branches", "project_branches_project_uuid", "project_uuid"); + } + + @Test + public void migration_should_be_reentrant() throws SQLException { + createIndex.execute(); + createIndex.execute(); + + db.assertIndex("project_branches", "project_branches_project_uuid", "project_uuid"); + } +} diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v102/CreateIndexProjectUuidInProjectBranchesTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v102/CreateIndexProjectUuidInProjectBranchesTest/schema.sql new file mode 100644 index 00000000000..4b20881e535 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v102/CreateIndexProjectUuidInProjectBranchesTest/schema.sql @@ -0,0 +1,15 @@ +CREATE TABLE "PROJECT_BRANCHES"( + "UUID" CHARACTER VARYING(50) NOT NULL, + "PROJECT_UUID" CHARACTER VARYING(50) NOT NULL, + "KEE" CHARACTER VARYING(255) NOT NULL, + "BRANCH_TYPE" CHARACTER VARYING(12) NOT NULL, + "MERGE_BRANCH_UUID" CHARACTER VARYING(50), + "PULL_REQUEST_BINARY" BINARY LARGE OBJECT, + "MANUAL_BASELINE_ANALYSIS_UUID" CHARACTER VARYING(40), + "CREATED_AT" BIGINT NOT NULL, + "UPDATED_AT" BIGINT NOT NULL, + "EXCLUDE_FROM_PURGE" BOOLEAN DEFAULT FALSE NOT NULL, + "NEED_ISSUE_SYNC" BOOLEAN NOT NULL +); +ALTER TABLE "PROJECT_BRANCHES" ADD CONSTRAINT "PK_PROJECT_BRANCHES" PRIMARY KEY("UUID"); +CREATE UNIQUE INDEX "UNIQ_PROJECT_BRANCHES" ON "PROJECT_BRANCHES"("BRANCH_TYPE" NULLS FIRST, "PROJECT_UUID" NULLS FIRST, "KEE" NULLS FIRST); diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/ws/ActivityAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/ws/ActivityAction.java index a34d53c3dcd..e9b72c81f61 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/ws/ActivityAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/ce/ws/ActivityAction.java @@ -298,7 +298,7 @@ public class ActivityAction implements CeWsAction { .setNameOrKeyQuery(componentQuery) .setQualifiers(POSSIBLE_QUALIFIERS) .build(); - List<ComponentDto> componentDtos = dbClient.componentDao().selectByQuery(dbSession, componentDtoQuery, 0, CeTaskQuery.MAX_COMPONENT_UUIDS); + List<ComponentDto> componentDtos = dbClient.componentDao().selectByQuery(dbSession, componentDtoQuery, forPage(1).andSize(CeTaskQuery.MAX_COMPONENT_UUIDS)); return dbClient.entityDao().selectByKeys(dbSession, componentDtos.stream().map(ComponentDto::getKey).collect(toSet())); } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/permission/ws/template/BulkApplyTemplateAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/permission/ws/template/BulkApplyTemplateAction.java index 62df55b54b7..f9b67c3b063 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/permission/ws/template/BulkApplyTemplateAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/permission/ws/template/BulkApplyTemplateAction.java @@ -37,6 +37,7 @@ import org.sonar.core.i18n.I18n; import org.sonar.db.DatabaseUtils; import org.sonar.db.DbClient; import org.sonar.db.DbSession; +import org.sonar.db.ce.CeTaskQuery; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentQuery; import org.sonar.db.entity.EntityDto; @@ -54,6 +55,7 @@ import static java.util.Collections.singleton; import static java.util.Objects.requireNonNull; import static java.util.Optional.ofNullable; import static org.sonar.api.utils.DateUtils.parseDateOrDateTime; +import static org.sonar.db.Pagination.forPage; import static org.sonar.server.permission.PermissionPrivilegeChecker.checkGlobalAdmin; import static org.sonar.server.permission.ws.template.WsTemplateRef.newTemplateRef; import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; @@ -156,7 +158,7 @@ public class BulkApplyTemplateAction implements PermissionsWsAction { checkGlobalAdmin(userSession); ComponentQuery componentQuery = buildDbQuery(request); - List<ComponentDto> components = dbClient.componentDao().selectByQuery(dbSession, componentQuery, 0, Integer.MAX_VALUE); + List<ComponentDto> components = dbClient.componentDao().selectByQuery(dbSession, componentQuery, forPage(1).andSize(CeTaskQuery.MAX_COMPONENT_UUIDS)); Set<String> entityUuids = components.stream() .map(ComponentDto::getKey) diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java index 1e2e947e031..85e324f47ab 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java @@ -57,6 +57,7 @@ import static java.util.stream.Collectors.toSet; import static org.sonar.api.resources.Qualifiers.APP; import static org.sonar.api.resources.Qualifiers.PROJECT; import static org.sonar.api.resources.Qualifiers.VIEW; +import static org.sonar.db.Pagination.forPage; import static org.sonar.server.project.ws.SearchAction.buildDbQuery; import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_002; @@ -150,7 +151,7 @@ public class BulkDeleteAction implements ProjectsWsAction { checkIfAnalyzedBeforeIsFutureDate(searchRequest); ComponentQuery query = buildDbQuery(searchRequest); - Set<ComponentDto> componentDtos = new HashSet<>(dbClient.componentDao().selectByQuery(dbSession, query, 0, Integer.MAX_VALUE)); + Set<ComponentDto> componentDtos = new HashSet<>(dbClient.componentDao().selectByQuery(dbSession, query, forPage(1).andSize(Integer.MAX_VALUE))); List<EntityDto> entities = dbClient.entityDao().selectByKeys(dbSession, componentDtos.stream().map(ComponentDto::getKey).collect(toSet())); Set<String> entityUuids = entities.stream().map(EntityDto::getUuid).collect(toSet()); Map<String, String> mainBranchUuidByEntityUuid = dbClient.branchDao().selectMainBranchesByProjectUuids(dbSession, entityUuids).stream() diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/SearchAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/SearchAction.java index 5dae2765f75..454617944fd 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/SearchAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/SearchAction.java @@ -53,6 +53,7 @@ import static org.sonar.api.resources.Qualifiers.PROJECT; import static org.sonar.api.resources.Qualifiers.VIEW; import static org.sonar.api.utils.DateUtils.formatDateTime; import static org.sonar.api.utils.DateUtils.parseDateOrDateTime; +import static org.sonar.db.Pagination.forPage; import static org.sonar.server.project.Visibility.PRIVATE; import static org.sonar.server.project.Visibility.PUBLIC; import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; @@ -167,7 +168,7 @@ public class SearchAction implements ProjectsWsAction { ComponentQuery query = buildDbQuery(request); Paging paging = buildPaging(dbSession, request, query); - List<ComponentDto> components = dbClient.componentDao().selectByQuery(dbSession, query, paging.offset(), paging.pageSize()); + List<ComponentDto> components = dbClient.componentDao().selectByQuery(dbSession, query, forPage(paging.pageIndex()).andSize(paging.pageSize())); Set<String> componentUuids = components.stream().map(ComponentDto::uuid).collect(Collectors.toSet()); List<BranchDto> branchDtos = dbClient.branchDao().selectByUuids(dbSession, componentUuids); Map<String, String> componentUuidToProjectUuid = branchDtos.stream().collect(Collectors.toMap(BranchDto::getUuid,BranchDto::getProjectUuid)); |