]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-3437 Use MyBatis instead of Hibernate to improve batch insert
authorDavid Gageot <david@gageot.net>
Mon, 9 Jul 2012 09:36:01 +0000 (11:36 +0200)
committerDavid Gageot <david@gageot.net>
Mon, 9 Jul 2012 11:48:07 +0000 (13:48 +0200)
16 files changed:
sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java
sonar-batch/src/test/java/org/sonar/batch/index/MeasurePersisterTest.java
sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/data.xml [new file with mode: 0644]
sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/empty.xml [new file with mode: 0644]
sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/shared.xml [deleted file]
sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/shouldAddDelayedMeasureSeveralTimes-result.xml [new file with mode: 0644]
sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/shouldDelaySaving-result.xml
sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/shouldInsertMeasure-result.xml
sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/shouldInsertRuleMeasure-result.xml [new file with mode: 0644]
sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/shouldNotDelaySavingWithDatabaseOnlyMeasure-result.xml [deleted file]
sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java
sonar-core/src/test/java/org/sonar/core/persistence/H2Database.java
sonar-plugin-api/src/main/java/org/sonar/api/database/model/MeasureDto.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/database/model/MeasureModelMapper.java [new file with mode: 0644]
sonar-plugin-api/src/main/resources/org/sonar/api/database/model/MeasureModelMapper-oracle.xml [new file with mode: 0644]
sonar-plugin-api/src/main/resources/org/sonar/api/database/model/MeasureModelMapper.xml [new file with mode: 0644]

index 3b967d89e38267381f47969695b9441ae0b6fddf..c270e2fd07105b589c9b54a5f5222d2e45c0da3c 100644 (file)
  */
 package org.sonar.batch.index;
 
+import org.sonar.api.database.model.MeasureDto;
+
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.LinkedHashMultimap;
 import com.google.common.collect.SetMultimap;
 import org.apache.commons.lang.math.NumberUtils;
+import org.apache.ibatis.session.SqlSession;
 import org.slf4j.LoggerFactory;
-import org.sonar.api.database.DatabaseSession;
 import org.sonar.api.database.model.MeasureModel;
+import org.sonar.api.database.model.MeasureModelMapper;
 import org.sonar.api.database.model.Snapshot;
 import org.sonar.api.measures.Measure;
 import org.sonar.api.measures.Metric;
@@ -35,20 +38,21 @@ import org.sonar.api.resources.ResourceUtils;
 import org.sonar.api.rules.Rule;
 import org.sonar.api.rules.RuleFinder;
 import org.sonar.api.utils.SonarException;
+import org.sonar.core.persistence.MyBatis;
 
 import java.util.Collection;
 import java.util.Map;
 
 public final class MeasurePersister {
-  private final DatabaseSession session;
+  private final MyBatis mybatis;
   private final ResourcePersister resourcePersister;
   private final RuleFinder ruleFinder;
   private final MemoryOptimizer memoryOptimizer;
   private final SetMultimap<Resource, Measure> unsavedMeasuresByResource = LinkedHashMultimap.create();
   private boolean delayedMode = false;
 
-  public MeasurePersister(DatabaseSession session, ResourcePersister resourcePersister, RuleFinder ruleFinder, MemoryOptimizer memoryOptimizer) {
-    this.session = session;
+  public MeasurePersister(MyBatis mybatis, ResourcePersister resourcePersister, RuleFinder ruleFinder, MemoryOptimizer memoryOptimizer) {
+    this.mybatis = mybatis;
     this.resourcePersister = resourcePersister;
     this.ruleFinder = ruleFinder;
     this.memoryOptimizer = memoryOptimizer;
@@ -64,17 +68,13 @@ public final class MeasurePersister {
       return;
     }
 
-    if (measure.getId() != null) { // update
-      MeasureModel model = session.reattach(MeasureModel.class, measure.getId());
-      model = mergeModel(measure, model);
-
-      model.save(session);
-      memoryOptimizer.evictDataMeasure(measure, model);
-    } else if (shouldPersistMeasure(resource, measure)) { // insert
-      Snapshot snapshot = resourcePersister.getSnapshotOrFail(resource);
-      MeasureModel model = createModel(measure).setSnapshotId(snapshot.getId());
-
-      model.save(session);
+    MeasureModel model = null;
+    if (measure.getId() != null) {
+      model = update(measure);
+    } else if (shouldPersistMeasure(resource, measure)) {
+      model = insert(measure, resourcePersister.getSnapshotOrFail(resource));
+    }
+    if (model != null) {
       memoryOptimizer.evictDataMeasure(measure, model);
     }
   }
@@ -114,57 +114,91 @@ public final class MeasurePersister {
   public void dump() {
     LoggerFactory.getLogger(getClass()).debug("{} measures to dump", unsavedMeasuresByResource.size());
 
-    Map<Resource, Collection<Measure>> map = unsavedMeasuresByResource.asMap();
-    for (Map.Entry<Resource, Collection<Measure>> entry : map.entrySet()) {
-      Resource resource = entry.getKey();
-      Snapshot snapshot = resourcePersister.getSnapshot(entry.getKey());
-      for (Measure measure : entry.getValue()) {
-        if (shouldPersistMeasure(resource, measure)) {
-          MeasureModel model = createModel(measure).setSnapshotId(snapshot.getId());
-          model.save(session);
+    SqlSession session = mybatis.openSession();
+    try {
+      MeasureModelMapper mapper = session.getMapper(MeasureModelMapper.class);
+
+      Map<Resource, Collection<Measure>> map = unsavedMeasuresByResource.asMap();
+      for (Map.Entry<Resource, Collection<Measure>> entry : map.entrySet()) {
+        Resource resource = entry.getKey();
+        Snapshot snapshot = resourcePersister.getSnapshot(entry.getKey());
+        for (Measure measure : entry.getValue()) {
+          if (shouldPersistMeasure(resource, measure)) {
+            mapper.insert(new MeasureDto(model(measure).setSnapshotId(snapshot.getId())));
+          }
         }
       }
+      session.commit();
+    } finally {
+      MyBatis.closeQuietly(session);
     }
 
-    session.commit();
     unsavedMeasuresByResource.clear();
   }
 
-  private MeasureModel createModel(Measure measure) {
-    return mergeModel(measure, new MeasureModel());
-  }
-
-  private MeasureModel mergeModel(Measure measure, MeasureModel merge) {
-    merge.setMetricId(measure.getMetric().getId()); // we assume that the index has updated the metric
-    merge.setDescription(measure.getDescription());
-    merge.setData(measure.getData());
-    merge.setAlertStatus(measure.getAlertStatus());
-    merge.setAlertText(measure.getAlertText());
-    merge.setTendency(measure.getTendency());
-    merge.setVariationValue1(measure.getVariation1());
-    merge.setVariationValue2(measure.getVariation2());
-    merge.setVariationValue3(measure.getVariation3());
-    merge.setVariationValue4(measure.getVariation4());
-    merge.setVariationValue5(measure.getVariation5());
-    merge.setUrl(measure.getUrl());
-    merge.setCharacteristic(measure.getCharacteristic());
-    merge.setPersonId(measure.getPersonId());
+  private MeasureModel model(Measure measure) {
+    MeasureModel model = new MeasureModel();
+    model.setMetricId(measure.getMetric().getId()); // we assume that the index has updated the metric
+    model.setDescription(measure.getDescription());
+    model.setData(measure.getData());
+    model.setAlertStatus(measure.getAlertStatus());
+    model.setAlertText(measure.getAlertText());
+    model.setTendency(measure.getTendency());
+    model.setVariationValue1(measure.getVariation1());
+    model.setVariationValue2(measure.getVariation2());
+    model.setVariationValue3(measure.getVariation3());
+    model.setVariationValue4(measure.getVariation4());
+    model.setVariationValue5(measure.getVariation5());
+    model.setUrl(measure.getUrl());
+    model.setCharacteristic(measure.getCharacteristic());
+    model.setPersonId(measure.getPersonId());
     if (measure.getValue() != null) {
-      merge.setValue(measure.getValue().doubleValue());
+      model.setValue(measure.getValue().doubleValue());
     } else {
-      merge.setValue(null);
+      model.setValue(null);
     }
     if (measure instanceof RuleMeasure) {
       RuleMeasure ruleMeasure = (RuleMeasure) measure;
-      merge.setRulePriority(ruleMeasure.getSeverity());
+      model.setRulePriority(ruleMeasure.getSeverity());
       if (ruleMeasure.getRule() != null) {
         Rule ruleWithId = ruleFinder.findByKey(ruleMeasure.getRule().getRepositoryKey(), ruleMeasure.getRule().getKey());
         if (ruleWithId == null) {
           throw new SonarException("Can not save a measure with unknown rule " + ruleMeasure);
         }
-        merge.setRuleId(ruleWithId.getId());
+        model.setRuleId(ruleWithId.getId());
       }
     }
-    return merge;
+    return model;
+  }
+
+  private MeasureModel insert(Measure measure, Snapshot snapshot) {
+    MeasureModel model = model(measure).setSnapshotId(snapshot.getId());
+
+    SqlSession session = mybatis.openSession();
+    try {
+      MeasureModelMapper mapper = session.getMapper(MeasureModelMapper.class);
+      mapper.insert(new MeasureDto(model));
+      session.commit();
+    } finally {
+      MyBatis.closeQuietly(session);
+    }
+
+    return model;
+  }
+
+  private MeasureModel update(Measure measure) {
+    MeasureModel model = model(measure);
+    model.setId(measure.getId());
+
+    SqlSession session = mybatis.openSession();
+    try {
+      MeasureModelMapper mapper = session.getMapper(MeasureModelMapper.class);
+      mapper.update(new MeasureDto(model));
+      session.commit();
+    } finally {
+      MyBatis.closeQuietly(session);
+    }
+
+    return model;
   }
 }
index 4d13104ba426c7eba6747042e9c446a70ff5e734..3bb6707129a8beb150ecbf970b7e0b8141e034a0 100644 (file)
@@ -27,12 +27,14 @@ import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.measures.Measure;
 import org.sonar.api.measures.Metric;
 import org.sonar.api.measures.PersistenceMode;
+import org.sonar.api.measures.RuleMeasure;
 import org.sonar.api.resources.JavaFile;
 import org.sonar.api.resources.JavaPackage;
 import org.sonar.api.resources.Project;
-import org.sonar.jpa.test.AbstractDbUnitTestCase;
-
-import java.util.List;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleFinder;
+import org.sonar.api.rules.RulePriority;
+import org.sonar.core.persistence.AbstractDaoTestCase;
 
 import static org.fest.assertions.Assertions.assertThat;
 import static org.mockito.Matchers.any;
@@ -41,7 +43,7 @@ import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-public class MeasurePersisterTest extends AbstractDbUnitTestCase {
+public class MeasurePersisterTest extends AbstractDaoTestCase {
 
   public static final int PROJECT_SNAPSHOT_ID = 3001;
   public static final int PACKAGE_SNAPSHOT_ID = 3002;
@@ -49,6 +51,7 @@ public class MeasurePersisterTest extends AbstractDbUnitTestCase {
   public static final int COVERAGE_METRIC_ID = 2;
 
   private MeasurePersister measurePersister;
+  private RuleFinder ruleFinder = mock(RuleFinder.class);
   private ResourcePersister resourcePersister = mock(ResourcePersister.class);
   private MemoryOptimizer memoryOptimizer = mock(MemoryOptimizer.class);
   private Project project = new Project("foo");
@@ -63,30 +66,36 @@ public class MeasurePersisterTest extends AbstractDbUnitTestCase {
     when(resourcePersister.getSnapshot(project)).thenReturn(projectSnapshot);
     when(resourcePersister.getSnapshot(aPackage)).thenReturn(packageSnapshot);
 
-    measurePersister = new MeasurePersister(getSession(), resourcePersister, null, memoryOptimizer);
+    measurePersister = new MeasurePersister(getMyBatis(), resourcePersister, ruleFinder, memoryOptimizer);
   }
 
   @Test
   public void shouldInsertMeasure() {
-    setupData("shared");
+    setupData("empty");
 
     Measure measure = new Measure(ncloc()).setValue(1234.0);
     measurePersister.saveMeasure(project, measure);
 
     checkTables("shouldInsertMeasure", "project_measures");
+    verify(memoryOptimizer).evictDataMeasure(eq(measure), any(MeasureModel.class));
   }
 
   @Test
-  public void shouldRegisterPersistedMeasureToMemoryOptimizer() {
-    Measure measure = new Measure(ncloc()).setValue(1234.0);
+  public void shouldInsertRuleMeasure() {
+    setupData("empty");
+
+    Rule rule = Rule.create("pmd", "key");
+    when(ruleFinder.findByKey("pmd", "key")).thenReturn(rule);
+
+    Measure measure = new RuleMeasure(ncloc(), rule, RulePriority.MAJOR, 1).setValue(1234.0);
     measurePersister.saveMeasure(project, measure);
 
-    verify(memoryOptimizer).evictDataMeasure(eq(measure), any(MeasureModel.class));
+    checkTables("shouldInsertRuleMeasure", "project_measures");
   }
 
   @Test
   public void shouldUpdateMeasure() {
-    setupData("shared");
+    setupData("data");
 
     Measure measure = new Measure(coverage()).setValue(12.5).setId(1L);
     measurePersister.saveMeasure(project, measure);
@@ -96,8 +105,11 @@ public class MeasurePersisterTest extends AbstractDbUnitTestCase {
 
   @Test
   public void shouldAddDelayedMeasureSeveralTimes() {
-    Measure measure = new Measure(ncloc()).setValue(200.0);
+    setupData("empty");
+
+    Measure measure = new Measure(ncloc());
 
+    measure.setValue(200.0);
     measurePersister.setDelayedMode(true);
     measurePersister.saveMeasure(project, measure);
 
@@ -105,19 +117,18 @@ public class MeasurePersisterTest extends AbstractDbUnitTestCase {
     measurePersister.saveMeasure(project, measure);
     measurePersister.dump();
 
-    List<MeasureModel> coverageMeasures = getSession().getResults(MeasureModel.class, "snapshotId", PROJECT_SNAPSHOT_ID, "metricId", 1);
-    assertThat(coverageMeasures).onProperty("value").containsExactly(300.0);
+    checkTables("shouldAddDelayedMeasureSeveralTimes", "project_measures");
   }
 
   @Test
   public void shouldDelaySaving() {
-    setupData("shared");
+    setupData("empty");
 
     measurePersister.setDelayedMode(true);
     measurePersister.saveMeasure(project, new Measure(ncloc()).setValue(1234.0));
     measurePersister.saveMeasure(aPackage, new Measure(ncloc()).setValue(50.0));
 
-    assertThat(getSession().getResults(MeasureModel.class, "metricId", 1)).isEmpty();
+    assertEmptyTables("project_measures");
 
     measurePersister.dump();
     checkTables("shouldDelaySaving", "project_measures");
@@ -125,42 +136,40 @@ public class MeasurePersisterTest extends AbstractDbUnitTestCase {
 
   @Test
   public void shouldNotDelaySavingWithDatabaseOnlyMeasure() {
-    setupData("shared");
+    setupData("empty");
 
     measurePersister.setDelayedMode(true);
-    measurePersister.saveMeasure(project, new Measure(ncloc()).setValue(1234.0).setPersistenceMode(PersistenceMode.DATABASE)); // database
-    measurePersister.saveMeasure(aPackage, new Measure(ncloc()).setValue(50.0)); // database + memory
+    measurePersister.saveMeasure(project, new Measure(ncloc()).setValue(1234.0).setPersistenceMode(PersistenceMode.DATABASE));
+    measurePersister.saveMeasure(aPackage, new Measure(ncloc()).setValue(50.0));
 
-    checkTables("shouldNotDelaySavingWithDatabaseOnlyMeasure", "project_measures");
+    checkTables("shouldInsertMeasure", "project_measures");
   }
 
   @Test
   public void shouldNotSaveBestValues() {
-    Measure measure = new Measure(coverage()).setValue(0.0);
-    assertThat(MeasurePersister.shouldPersistMeasure(aFile, measure)).isTrue();
-
-    measure = new Measure(coverage()).setValue(75.8);
-    assertThat(MeasurePersister.shouldPersistMeasure(aFile, measure)).isTrue();
-
-    measure = new Measure(coverage()).setValue(100.0);
-    assertThat(MeasurePersister.shouldPersistMeasure(aFile, measure)).isFalse();
+    assertThat(MeasurePersister.shouldPersistMeasure(aFile, new Measure(coverage()).setValue(0.0))).isTrue();
+    assertThat(MeasurePersister.shouldPersistMeasure(aFile, new Measure(coverage()).setValue(75.8))).isTrue();
+    assertThat(MeasurePersister.shouldPersistMeasure(aFile, new Measure(coverage()).setValue(100.0))).isFalse();
   }
 
   @Test
   public void shouldNotSaveBestValueMeasuresInDelayedMode() {
+    setupData("empty");
+
     measurePersister.setDelayedMode(true);
     measurePersister.saveMeasure(aFile, new Measure(coverage()).setValue(100.0));
 
-    assertThat(getSession().getResults(MeasureModel.class, "metricId", COVERAGE_METRIC_ID, "snapshotId", FILE_SNAPSHOT_ID)).isEmpty();
+    assertEmptyTables("project_measures");
 
     measurePersister.dump();
 
-    assertThat(getSession().getResults(MeasureModel.class, "metricId", COVERAGE_METRIC_ID, "snapshotId", FILE_SNAPSHOT_ID)).isEmpty();
+    assertEmptyTables("project_measures");
   }
 
   @Test
   public void shouldNotSaveMemoryOnlyMeasures() {
     Measure measure = new Measure("ncloc").setPersistenceMode(PersistenceMode.MEMORY);
+
     assertThat(MeasurePersister.shouldPersistMeasure(aPackage, measure)).isFalse();
   }
 
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/data.xml b/sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/data.xml
new file mode 100644 (file)
index 0000000..f8900a3
--- /dev/null
@@ -0,0 +1,9 @@
+<dataset>
+
+  <project_measures id="1" VALUE="60" METRIC_ID="2" SNAPSHOT_ID="3001" alert_text="[null]" RULES_CATEGORY_ID="[null]"
+                    RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+                    alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]"
+                    person_id="[null]"
+                    variation_value_1="[null]" variation_value_2="[null]" variation_value_3="[null]" variation_value_4="[null]" variation_value_5="[null]"/>
+
+</dataset>
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/empty.xml b/sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/empty.xml
new file mode 100644 (file)
index 0000000..fb0854f
--- /dev/null
@@ -0,0 +1,2 @@
+<dataset>
+</dataset>
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/shared.xml b/sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/shared.xml
deleted file mode 100644 (file)
index f8900a3..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<dataset>
-
-  <project_measures id="1" VALUE="60" METRIC_ID="2" SNAPSHOT_ID="3001" alert_text="[null]" RULES_CATEGORY_ID="[null]"
-                    RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
-                    alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]"
-                    person_id="[null]"
-                    variation_value_1="[null]" variation_value_2="[null]" variation_value_3="[null]" variation_value_4="[null]" variation_value_5="[null]"/>
-
-</dataset>
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/shouldAddDelayedMeasureSeveralTimes-result.xml b/sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/shouldAddDelayedMeasureSeveralTimes-result.xml
new file mode 100644 (file)
index 0000000..a9d6926
--- /dev/null
@@ -0,0 +1,9 @@
+<dataset>
+
+  <project_measures id="1" VALUE="300.0" METRIC_ID="1" SNAPSHOT_ID="3001" alert_text="[null]" RULES_CATEGORY_ID="[null]"
+                    RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+                    alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]"
+                    person_id="[null]"
+                    variation_value_1="[null]" variation_value_2="[null]" variation_value_3="[null]" variation_value_4="[null]" variation_value_5="[null]"/>
+
+</dataset>
index f0ac513c87d966c06b2d14efa18933172745cca5..3aecc2ea40ed637e526e0cf1f596c75e6b466df1 100644 (file)
@@ -1,19 +1,13 @@
 <dataset>
 
-  <project_measures id="1" VALUE="60" METRIC_ID="2" SNAPSHOT_ID="3001" alert_text="[null]" RULES_CATEGORY_ID="[null]"
-                    RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
-                    alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]"
-                    person_id="[null]"
-                    variation_value_1="[null]" variation_value_2="[null]" variation_value_3="[null]" variation_value_4="[null]" variation_value_5="[null]"/>
-
-  <project_measures id="2" VALUE="1234.0" METRIC_ID="1" SNAPSHOT_ID="3001" alert_text="[null]"
+  <project_measures id="1" VALUE="1234.0" METRIC_ID="1" SNAPSHOT_ID="3001" alert_text="[null]"
                     RULES_CATEGORY_ID="[null]"
                     RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
                     alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]"
                     person_id="[null]"
                     variation_value_1="[null]" variation_value_2="[null]" variation_value_3="[null]" variation_value_4="[null]" variation_value_5="[null]"/>
 
-  <project_measures id="3" VALUE="50.0" METRIC_ID="1" SNAPSHOT_ID="3002" alert_text="[null]" RULES_CATEGORY_ID="[null]"
+  <project_measures id="2" VALUE="50.0" METRIC_ID="1" SNAPSHOT_ID="3002" alert_text="[null]" RULES_CATEGORY_ID="[null]"
                     RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
                     alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]"
                     person_id="[null]"
index 1cd6e6d10c4add9d8cb035fc548cb3ccc983b989..f7475f591e862161e0d9ed17a9f86507255c830e 100644 (file)
@@ -1,12 +1,6 @@
 <dataset>
 
-  <project_measures id="1" VALUE="60" METRIC_ID="2" SNAPSHOT_ID="3001" alert_text="[null]" RULES_CATEGORY_ID="[null]"
-                    RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
-                    alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]"
-                    person_id="[null]"
-                    variation_value_1="[null]" variation_value_2="[null]" variation_value_3="[null]" variation_value_4="[null]" variation_value_5="[null]"/>
-
-  <project_measures id="2" VALUE="1234.0" METRIC_ID="1" SNAPSHOT_ID="3001" alert_text="[null]" RULES_CATEGORY_ID="[null]"
+  <project_measures id="1" VALUE="1234.0" METRIC_ID="1" SNAPSHOT_ID="3001" alert_text="[null]" RULES_CATEGORY_ID="[null]"
                     RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
                     alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]"
                     person_id="[null]"
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/shouldInsertRuleMeasure-result.xml b/sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/shouldInsertRuleMeasure-result.xml
new file mode 100644 (file)
index 0000000..5472c06
--- /dev/null
@@ -0,0 +1,9 @@
+<dataset>
+
+  <project_measures id="1" VALUE="1234.0" METRIC_ID="1" SNAPSHOT_ID="3001" alert_text="[null]" RULES_CATEGORY_ID="[null]"
+                    RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+                    alert_status="[null]" description="[null]" rule_priority="2" characteristic_id="[null]" url="[null]"
+                    person_id="[null]"
+                    variation_value_1="[null]" variation_value_2="[null]" variation_value_3="[null]" variation_value_4="[null]" variation_value_5="[null]"/>
+
+</dataset>
diff --git a/sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/shouldNotDelaySavingWithDatabaseOnlyMeasure-result.xml b/sonar-batch/src/test/resources/org/sonar/batch/index/MeasurePersisterTest/shouldNotDelaySavingWithDatabaseOnlyMeasure-result.xml
deleted file mode 100644 (file)
index 1cd6e6d..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-<dataset>
-
-  <project_measures id="1" VALUE="60" METRIC_ID="2" SNAPSHOT_ID="3001" alert_text="[null]" RULES_CATEGORY_ID="[null]"
-                    RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
-                    alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]"
-                    person_id="[null]"
-                    variation_value_1="[null]" variation_value_2="[null]" variation_value_3="[null]" variation_value_4="[null]" variation_value_5="[null]"/>
-
-  <project_measures id="2" VALUE="1234.0" METRIC_ID="1" SNAPSHOT_ID="3001" alert_text="[null]" RULES_CATEGORY_ID="[null]"
-                    RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
-                    alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]"
-                    person_id="[null]"
-                    variation_value_1="[null]" variation_value_2="[null]" variation_value_3="[null]" variation_value_4="[null]" variation_value_5="[null]"/>
-
-</dataset>
index d051fe6942269bb40588a2d6949007f591386d6c..fc2c07a182129fe50edfba87cdb7ab3bcea448a1 100644 (file)
  */
 package org.sonar.core.persistence;
 
+import org.sonar.api.database.model.MeasureDto;
+
+import org.sonar.api.database.model.MeasureModel;
+
+import org.sonar.api.database.model.MeasureModelMapper;
+
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang.StringUtils;
 import org.apache.ibatis.builder.xml.XMLMapperBuilder;
@@ -98,6 +104,7 @@ public class MyBatis implements BatchComponent, ServerComponent {
     loadAlias(conf, "UserRole", UserRoleDto.class);
     loadAlias(conf, "Widget", WidgetDto.class);
     loadAlias(conf, "WidgetProperty", WidgetPropertyDto.class);
+    loadAlias(conf, "MeasureDto", MeasureDto.class);
 
     loadMapper(conf, ActiveDashboardMapper.class);
     loadMapper(conf, AuthorMapper.class);
@@ -123,6 +130,7 @@ public class MyBatis implements BatchComponent, ServerComponent {
     loadMapper(conf, UserMapper.class);
     loadMapper(conf, WidgetMapper.class);
     loadMapper(conf, WidgetPropertyMapper.class);
+    loadMapper(conf, MeasureModelMapper.class);
 
     sessionFactory = new SqlSessionFactoryBuilder().build(conf);
     return this;
index bc893b4bd75760d93f8aa02cf5ce661881f44deb..55db7c2ed942928be05c59faa2cca5a26c069cd4 100644 (file)
@@ -57,6 +57,7 @@ public class H2Database implements Database {
       properties.put("driverClassName", "org.h2.Driver");
       properties.put("username", "sonar");
       properties.put("password", "sonar");
+      // properties.put("url", "jdbc:h2:mem:sonar2;TRACE_LEVEL_SYSTEM_OUT=2");
       properties.put("url", "jdbc:h2:mem:sonar2");
 
       // limit to 2 because of Hibernate and MyBatis
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/database/model/MeasureDto.java b/sonar-plugin-api/src/main/java/org/sonar/api/database/model/MeasureDto.java
new file mode 100644 (file)
index 0000000..a0cb386
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.api.database.model;
+
+import java.util.Date;
+
+public class MeasureDto {
+  private final  Long id;
+  private final  Double value;
+  private final  String textValue;
+  private final  Integer tendency;
+  private final  Integer metricId;
+  private final  Integer snapshotId;
+  private final  Integer projectId;
+  private final  String description;
+  private final  Date measureDate;
+  private final  Integer ruleId;
+  private final Integer rulePriority;
+  private final String alertStatus;
+  private final  String alertText;
+  private final  Double variationValue1;
+  private final  Double variationValue2;
+  private final  Double variationValue3;
+  private final  Double variationValue4;
+  private final  Double variationValue5;
+  private final  String url;
+  private final  Integer characteristicId;
+  private final  Integer personId;
+
+  // private List<MeasureData> measureData = new ArrayList<MeasureData>();
+
+  public MeasureDto(MeasureModel model) {
+    id = model.getId();
+    value = model.getValue();
+    textValue = model.getTextValue();
+    tendency = model.getTendency();
+    metricId = model.getMetricId();
+    snapshotId = model.getSnapshotId();
+    projectId = model.getProjectId();
+    description = model.getDescription();
+    measureDate = model.getMeasureDate();
+    ruleId = model.getRuleId();
+    rulePriority = (null == model.getRulePriority()) ? null : model.getRulePriority().ordinal();
+    alertStatus = (null == model.getAlertStatus()) ? null : model.getAlertStatus().name();
+    alertText = model.getAlertText();
+    variationValue1 = model.getVariationValue1();
+    variationValue2 = model.getVariationValue2();
+    variationValue3 = model.getVariationValue3();
+    variationValue4 = model.getVariationValue4();
+    variationValue5 = model.getVariationValue5();
+    url = model.getUrl();
+    characteristicId = (null == model.getCharacteristic()) ? null : model.getCharacteristic().getId();
+    personId = model.getPersonId();
+  }
+
+  public Long getId() {
+    return id;
+  }
+
+  public Double getValue() {
+    return value;
+  }
+
+  public String getTextValue() {
+    return textValue;
+  }
+
+  public Integer getTendency() {
+    return tendency;
+  }
+
+  public Integer getMetricId() {
+    return metricId;
+  }
+
+  public Integer getSnapshotId() {
+    return snapshotId;
+  }
+
+  public Integer getProjectId() {
+    return projectId;
+  }
+
+  public String getDescription() {
+    return description;
+  }
+
+  public Date getMeasureDate() {
+    return measureDate;
+  }
+
+  public Integer getRuleId() {
+    return ruleId;
+  }
+
+  public Integer getRulePriority() {
+    return rulePriority;
+  }
+
+  public String getAlertStatus() {
+    return alertStatus;
+  }
+
+  public String getAlertText() {
+    return alertText;
+  }
+
+  public Double getVariationValue1() {
+    return variationValue1;
+  }
+
+  public Double getVariationValue2() {
+    return variationValue2;
+  }
+
+  public Double getVariationValue3() {
+    return variationValue3;
+  }
+
+  public Double getVariationValue4() {
+    return variationValue4;
+  }
+
+  public Double getVariationValue5() {
+    return variationValue5;
+  }
+
+  public String getUrl() {
+    return url;
+  }
+
+  public Integer getCharacteristicId() {
+    return characteristicId;
+  }
+
+  public Integer getPersonId() {
+    return personId;
+  }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/database/model/MeasureModelMapper.java b/sonar-plugin-api/src/main/java/org/sonar/api/database/model/MeasureModelMapper.java
new file mode 100644 (file)
index 0000000..615971f
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.api.database.model;
+
+public interface MeasureModelMapper {
+  void insert(MeasureDto measure);
+
+  void update(MeasureDto measure);
+}
diff --git a/sonar-plugin-api/src/main/resources/org/sonar/api/database/model/MeasureModelMapper-oracle.xml b/sonar-plugin-api/src/main/resources/org/sonar/api/database/model/MeasureModelMapper-oracle.xml
new file mode 100644 (file)
index 0000000..655f4ce
--- /dev/null
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="org.sonar.api.database.model.MeasureModelMapper">
+
+  <insert id="insert" parameterType="MeasureDto" keyColumn="id" useGeneratedKeys="true" keyProperty="id">
+    <selectKey order="BEFORE" resultType="Long" keyProperty="id">
+      select project_measures_seq.NEXTVAL from DUAL
+    </selectKey>
+    INSERT INTO project_measures (id,
+      value, metric_id, snapshot_id, rule_id, text_value, tendency, measure_date,
+      project_id, alert_status, alert_text, url, description, rule_priority, characteristic_id, variation_value_1,
+      variation_value_2, variation_value_3, variation_value_4, variation_value_5, person_id)
+    VALUES (#{id},
+      #{value}, #{metricId}, #{snapshotId}, #{ruleId}, #{textValue, jdbcType=VARCHAR}, #{tendency},
+      #{measureDate, jdbcType=TIMESTAMP}, #{projectId}, #{alertStatus, jdbcType=VARCHAR}, #{alertText, jdbcType=VARCHAR},
+      #{url, jdbcType=VARCHAR}, #{description, jdbcType=VARCHAR}, #{rulePriority}, #{characteristicId}, #{variationValue1},
+      #{variationValue2}, #{variationValue3}, #{variationValue4}, #{variationValue5}, #{personId}
+    )
+  </insert>
+  
+
+  <update id="update" parameterType="MeasureDto">
+    UPDATE project_measures
+    SET
+      value = #{value},
+      metric_id = #{metricId},
+      rule_id = #{ruleId},
+      text_value = #{textValue, jdbcType=VARCHAR},
+      tendency = #{tendency},
+      alert_status = #{alertStatus, jdbcType=VARCHAR},
+      alert_text = #{alertText, jdbcType=VARCHAR},
+      url = #{url, jdbcType=VARCHAR},
+      description = #{description, jdbcType=VARCHAR},
+      rule_priority = #{rulePriority},
+      characteristic_id = #{characteristicId},
+      variation_value_1 = #{variationValue1},
+      variation_value_2 = #{variationValue2},
+      variation_value_3 = #{variationValue3},
+      variation_value_4 = #{variationValue4},
+      variation_value_5 = #{variationValue5},
+      person_id = #{personId}
+    WHERE id = #{id}
+  </update>
+
+</mapper>
diff --git a/sonar-plugin-api/src/main/resources/org/sonar/api/database/model/MeasureModelMapper.xml b/sonar-plugin-api/src/main/resources/org/sonar/api/database/model/MeasureModelMapper.xml
new file mode 100644 (file)
index 0000000..594674a
--- /dev/null
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="org.sonar.api.database.model.MeasureModelMapper">
+
+  <insert id="insert" parameterType="MeasureDto" useGeneratedKeys="true" keyProperty="id">
+    INSERT INTO project_measures (
+      value, metric_id, snapshot_id, rule_id, text_value, tendency, measure_date,
+      project_id, alert_status, alert_text, url, description, rule_priority, characteristic_id, variation_value_1,
+      variation_value_2, variation_value_3, variation_value_4, variation_value_5, person_id)
+    VALUES (
+      #{value}, #{metricId}, #{snapshotId}, #{ruleId}, #{textValue, jdbcType=VARCHAR}, #{tendency},
+      #{measureDate, jdbcType=TIMESTAMP}, #{projectId}, #{alertStatus, jdbcType=VARCHAR}, #{alertText, jdbcType=VARCHAR},
+      #{url, jdbcType=VARCHAR}, #{description, jdbcType=VARCHAR}, #{rulePriority}, #{characteristicId}, #{variationValue1},
+      #{variationValue2}, #{variationValue3}, #{variationValue4}, #{variationValue5}, #{personId}
+    )
+  </insert>
+  
+
+  <update id="update" parameterType="MeasureDto">
+    UPDATE project_measures
+    SET
+      value = #{value},
+      metric_id = #{metricId},
+      rule_id = #{ruleId},
+      text_value = #{textValue, jdbcType=VARCHAR},
+      tendency = #{tendency},
+      alert_status = #{alertStatus, jdbcType=VARCHAR},
+      alert_text = #{alertText, jdbcType=VARCHAR},
+      url = #{url, jdbcType=VARCHAR},
+      description = #{description, jdbcType=VARCHAR},
+      rule_priority = #{rulePriority},
+      characteristic_id = #{characteristicId},
+      variation_value_1 = #{variationValue1},
+      variation_value_2 = #{variationValue2},
+      variation_value_3 = #{variationValue3},
+      variation_value_4 = #{variationValue4},
+      variation_value_5 = #{variationValue5},
+      person_id = #{personId}
+    WHERE id = #{id}
+  </update>
+
+</mapper>