001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2009 SonarSource SA 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * Sonar is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020 package org.sonar.core.purge; 021 022 import org.sonar.api.batch.Purge; 023 import org.sonar.api.database.DatabaseSession; 024 import org.sonar.api.database.model.*; 025 import org.sonar.api.design.DependencyDto; 026 import org.sonar.api.utils.TimeProfiler; 027 028 import javax.persistence.Query; 029 import java.util.List; 030 031 public abstract class AbstractPurge implements Purge { 032 033 private static final int MAX_IN_ELEMENTS = 950; 034 035 private int sqlInPageSize = MAX_IN_ELEMENTS; 036 private DatabaseSession session; 037 private TimeProfiler profiler = new TimeProfiler().setLevelToDebug(); 038 039 public AbstractPurge(DatabaseSession session) { 040 this.session = session; 041 } 042 043 protected DatabaseSession getSession() { 044 return session; 045 } 046 047 /** 048 * Delete SNAPSHOTS and all dependent tables (MEASURES, ...) 049 */ 050 protected void deleteSnapshotData(List<Integer> snapshotIds) { 051 deleteMeasuresBySnapshotId(snapshotIds); 052 deleteSources(snapshotIds); 053 deleteViolations(snapshotIds); 054 deleteDependencies(snapshotIds); 055 deleteSnapshots(snapshotIds); 056 } 057 058 protected void deleteDependencies(List<Integer> snapshotIds) { 059 executeQuery("delete dependencies", snapshotIds, "delete from " + DependencyDto.class.getSimpleName() + " d where d.fromSnapshotId in (:ids)"); 060 executeQuery("delete dependencies", snapshotIds, "delete from " + DependencyDto.class.getSimpleName() + " d where d.toSnapshotId in (:ids)"); 061 } 062 063 /** 064 * Delete all measures, including MEASURE_DATA 065 */ 066 protected void deleteMeasuresBySnapshotId(List<Integer> snapshotIds) { 067 executeQuery("delete measures by snapshot id", snapshotIds, "delete from " + MeasureData.class.getSimpleName() + " m where m.snapshotId in (:ids)"); 068 executeQuery("delete measures by snapshot id", snapshotIds, "delete from " + MeasureModel.class.getSimpleName() + " m where m.snapshotId in (:ids)"); 069 } 070 071 /** 072 * Delete all measures, including MEASURE_DATA 073 */ 074 protected void deleteMeasuresById(List<Integer> measureIds) { 075 executeQuery("delete measures by id", measureIds, "delete from " + MeasureData.class.getSimpleName() + " m where m.measure.id in (:ids)"); 076 executeQuery("delete measures by id", measureIds, "delete from " + MeasureModel.class.getSimpleName() + " m where m.id in (:ids)"); 077 } 078 079 /** 080 * Delete SNAPSHOT_SOURCES table 081 */ 082 protected void deleteSources(List<Integer> snapshotIds) { 083 executeQuery("delete sources", snapshotIds, "delete from " + SnapshotSource.class.getSimpleName() + " e where e.snapshotId in (:ids)"); 084 } 085 086 /** 087 * Delete violations (RULE_FAILURES table) 088 */ 089 protected void deleteViolations(List<Integer> snapshotIds) { 090 executeQuery("delete violations", snapshotIds, "delete from " + RuleFailureModel.class.getSimpleName() + " e where e.snapshotId in (:ids)"); 091 } 092 093 /** 094 * Delete SNAPSHOTS table 095 */ 096 protected void deleteSnapshots(List<Integer> snapshotIds) { 097 executeQuery("delete snapshots", snapshotIds, "delete from " + Snapshot.class.getSimpleName() + " s where s.id in (:ids)"); 098 } 099 100 /** 101 * Paginate execution of SQL requests to avoid exceeding size of rollback segment 102 */ 103 private void executeQuery(String name, List<Integer> ids, String hql) { 104 if (ids == null || ids.isEmpty()) { 105 return; 106 } 107 108 TimeProfiler profiler = new TimeProfiler().setLevelToDebug().start("Execute " + name); 109 110 int page = 1; 111 int index = 0; 112 while (index < ids.size()) { 113 TimeProfiler pageProfiler = new TimeProfiler().setLevelToDebug().start("Execute " + name + " " + page + " page"); 114 Query query = session.createQuery(hql); 115 List<Integer> paginedSids = ids.subList(index, Math.min(ids.size(), index + sqlInPageSize)); 116 query.setParameter("ids", paginedSids); 117 query.executeUpdate(); 118 index += sqlInPageSize; 119 page++; 120 session.commit(); 121 pageProfiler.stop(); 122 } 123 124 profiler.stop(); 125 } 126 127 protected void executeQuery(List<Integer> ids, String hql) { 128 executeQuery("delete for " + getClass().getSimpleName(), ids, hql); 129 } 130 131 protected List<Integer> selectIds(Query query) { 132 profiler.start("Select IDs for " + getClass().getSimpleName()); 133 List<Integer> result = query.getResultList(); 134 profiler.stop(); 135 return result; 136 } 137 }