Merge "Get assignee from ChangeIndex"
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DeleteDraftPatchSetIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DeleteDraftPatchSetIT.java
index 31e52f7..2474d68 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DeleteDraftPatchSetIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/DeleteDraftPatchSetIT.java
@@ -33,7 +33,7 @@
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.config.AllUsersName;
@@ -152,8 +152,8 @@
     for (ChangeMessage m : cd.messages()) {
       assertThat(m.getPatchSetId()).named(m.toString()).isNotEqualTo(delPsId);
     }
-    for (PatchLineComment c : cd.publishedComments()) {
-      assertThat(c.getPatchSetId()).named(c.toString()).isNotEqualTo(delPsId);
+    for (Comment c : cd.publishedComments()) {
+      assertThat(c.key.patchSetId).named(c.toString()).isNotEqualTo(delPsId.get());
     }
   }
 
@@ -187,8 +187,8 @@
     for (ChangeMessage m : cd.messages()) {
       assertThat(m.getPatchSetId()).named(m.toString()).isNotEqualTo(delPsId);
     }
-    for (PatchLineComment c : cd.publishedComments()) {
-      assertThat(c.getPatchSetId()).named(c.toString()).isNotEqualTo(delPsId);
+    for (Comment c : cd.publishedComments()) {
+      assertThat(c.key.patchSetId).named(c.toString()).isNotEqualTo(delPsId.get());
     }
   }
 
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java
index ced0141..a30674f 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/notedb/ChangeRebuilderIT.java
@@ -46,7 +46,7 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.reviewdb.server.ReviewDbUtil;
 import com.google.gerrit.server.ChangeUtil;
-import com.google.gerrit.server.PatchLineCommentsUtil;
+import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.Sequences;
 import com.google.gerrit.server.change.PostReview;
 import com.google.gerrit.server.change.Rebuild;
@@ -109,7 +109,7 @@
   private Provider<ReviewDb> dbProvider;
 
   @Inject
-  private PatchLineCommentsUtil plcUtil;
+  private CommentsUtil commentsUtil;
 
   @Inject
   private Provider<PostReview> postReview;
@@ -390,7 +390,7 @@
 
     // Check that the bundles are equal.
     ChangeBundle actual = ChangeBundle.fromNotes(
-        plcUtil, notesFactory.create(dbProvider.get(), project, id));
+        commentsUtil, notesFactory.create(dbProvider.get(), project, id));
     ChangeBundle expected = bundleReader.fromReviewDb(getUnwrappedDb(), id);
     assertThat(actual.differencesFrom(expected)).isEmpty();
   }
@@ -441,7 +441,7 @@
 
     // Check that the bundles are equal.
     ChangeNotes notes = notesFactory.create(dbProvider.get(), project, id);
-    ChangeBundle actual = ChangeBundle.fromNotes(plcUtil, notes);
+    ChangeBundle actual = ChangeBundle.fromNotes(commentsUtil, notes);
     ChangeBundle expected = bundleReader.fromReviewDb(getUnwrappedDb(), id);
     assertThat(actual.differencesFrom(expected)).isEmpty();
     assertThat(
@@ -475,7 +475,7 @@
 
     // Check that the bundles are equal.
     ChangeBundle actual = ChangeBundle.fromNotes(
-        plcUtil, notesFactory.create(dbProvider.get(), project, id));
+        commentsUtil, notesFactory.create(dbProvider.get(), project, id));
     ChangeBundle expected = bundleReader.fromReviewDb(getUnwrappedDb(), id);
     assertThat(actual.differencesFrom(expected)).isEmpty();
   }
@@ -505,7 +505,7 @@
     // Not up to date, but the actual returned state matches anyway.
     assertChangeUpToDate(false, id);
     assertThat(getMetaRef(project, changeMetaRef(id))).isEqualTo(oldMetaId);
-    ChangeBundle actual = ChangeBundle.fromNotes(plcUtil, notes);
+    ChangeBundle actual = ChangeBundle.fromNotes(commentsUtil, notes);
     ChangeBundle expected = bundleReader.fromReviewDb(getUnwrappedDb(), id);
     assertThat(actual.differencesFrom(expected)).isEmpty();
     assertChangeUpToDate(false, id);
@@ -547,7 +547,7 @@
 
     // Not up to date, but the actual returned state matches anyway.
     assertDraftsUpToDate(false, id, user);
-    ChangeBundle actual = ChangeBundle.fromNotes(plcUtil, notes);
+    ChangeBundle actual = ChangeBundle.fromNotes(commentsUtil, notes);
     ChangeBundle expected = bundleReader.fromReviewDb(getUnwrappedDb(), id);
     assertThat(actual.differencesFrom(expected)).isEmpty();
 
@@ -602,7 +602,7 @@
     // Not up to date, but the actual returned state matches anyway.
     assertChangeUpToDate(true, id);
     assertDraftsUpToDate(false, id, user);
-    ChangeBundle actual = ChangeBundle.fromNotes(plcUtil, notes);
+    ChangeBundle actual = ChangeBundle.fromNotes(commentsUtil, notes);
     ChangeBundle expected = bundleReader.fromReviewDb(getUnwrappedDb(), id);
     assertThat(actual.differencesFrom(expected)).isEmpty();
 
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/CommentDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/CommentDetail.java
index 1b98b09..ff5402f 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/CommentDetail.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/CommentDetail.java
@@ -14,7 +14,8 @@
 
 package com.google.gerrit.common.data;
 
-import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.PatchSet;
 
 import java.util.ArrayList;
@@ -24,14 +25,14 @@
 import java.util.Map;
 
 public class CommentDetail {
-  protected List<PatchLineComment> a;
-  protected List<PatchLineComment> b;
+  protected List<Comment> a;
+  protected List<Comment> b;
   protected AccountInfoCache accounts;
 
   private transient PatchSet.Id idA;
   private transient PatchSet.Id idB;
-  private transient Map<Integer, List<PatchLineComment>> forA;
-  private transient Map<Integer, List<PatchLineComment>> forB;
+  private transient Map<Integer, List<Comment>> forA;
+  private transient Map<Integer, List<Comment>> forB;
 
   public CommentDetail(PatchSet.Id idA, PatchSet.Id idB) {
     this.a = new ArrayList<>();
@@ -43,9 +44,9 @@
   protected CommentDetail() {
   }
 
-  public boolean include(final PatchLineComment p) {
-    final PatchSet.Id psId = p.getKey().getParentKey().getParentKey();
-    switch (p.getSide()) {
+  public boolean include(Change.Id changeId, Comment p) {
+    PatchSet.Id psId = new PatchSet.Id(changeId, p.key.patchSetId);
+    switch (p.side) {
       case 0:
         if (idA == null && idB.equals(psId)) {
           a.add(p);
@@ -76,11 +77,11 @@
     return accounts;
   }
 
-  public List<PatchLineComment> getCommentsA() {
+  public List<Comment> getCommentsA() {
     return a;
   }
 
-  public List<PatchLineComment> getCommentsB() {
+  public List<Comment> getCommentsB() {
     return b;
   }
 
@@ -88,24 +89,23 @@
     return a.isEmpty() && b.isEmpty();
   }
 
-  public List<PatchLineComment> getForA(final int lineNbr) {
+  public List<Comment> getForA(int lineNbr) {
     if (forA == null) {
       forA = index(a);
     }
     return get(forA, lineNbr);
   }
 
-  public List<PatchLineComment> getForB(final int lineNbr) {
+  public List<Comment> getForB(int lineNbr) {
     if (forB == null) {
       forB = index(b);
     }
     return get(forB, lineNbr);
   }
 
-  private static List<PatchLineComment> get(
-      final Map<Integer, List<PatchLineComment>> m, final int i) {
-    final List<PatchLineComment> r = m.get(i);
-    return r != null ? orderComments(r) : Collections.<PatchLineComment> emptyList();
+  private static List<Comment> get(Map<Integer, List<Comment>> m, int i) {
+    List<Comment> r = m.get(i);
+    return r != null ? orderComments(r) : Collections.<Comment> emptyList();
   }
 
   /**
@@ -116,21 +116,21 @@
    * @param comments The list of comments for a given line.
    * @return The comments sorted as they should appear in the UI
    */
-  private static List<PatchLineComment> orderComments(List<PatchLineComment> comments) {
+  private static List<Comment> orderComments(List<Comment> comments) {
     // Map of comments keyed by their parent. The values are lists of comments since it is
     // possible for several comments to have the same parent (this can happen if two reviewers
     // click Reply on the same comment at the same time). Such comments will be displayed under
     // their correct parent in chronological order.
-    Map<String, List<PatchLineComment>> parentMap = new HashMap<>();
+    Map<String, List<Comment>> parentMap = new HashMap<>();
 
     // It's possible to have more than one root comment if two reviewers create a comment on the
     // same line at the same time
-    List<PatchLineComment> rootComments = new ArrayList<>();
+    List<Comment> rootComments = new ArrayList<>();
 
     // Store all the comments in parentMap, keyed by their parent
-    for (PatchLineComment c : comments) {
-      String parentUuid = c.getParentUuid();
-      List<PatchLineComment> l = parentMap.get(parentUuid);
+    for (Comment c : comments) {
+      String parentUuid = c.parentUuid;
+      List<Comment> l = parentMap.get(parentUuid);
       if (l == null) {
         l = new ArrayList<>();
         parentMap.put(parentUuid, l);
@@ -143,7 +143,7 @@
 
     // Add the comments in the list, starting with the head and then going through all the
     // comments that have it as a parent, and so on
-    List<PatchLineComment> result = new ArrayList<>();
+    List<Comment> result = new ArrayList<>();
     addChildren(parentMap, rootComments, result);
 
     return result;
@@ -152,24 +152,23 @@
   /**
    * Add the comments to {@code outResult}, depth first
    */
-  private static void addChildren(Map<String, List<PatchLineComment>> parentMap,
-      List<PatchLineComment> children, List<PatchLineComment> outResult) {
+  private static void addChildren(Map<String, List<Comment>> parentMap,
+      List<Comment> children, List<Comment> outResult) {
     if (children != null) {
-      for (PatchLineComment c : children) {
+      for (Comment c : children) {
         outResult.add(c);
-        addChildren(parentMap, parentMap.get(c.getKey().get()), outResult);
+        addChildren(parentMap, parentMap.get(c.key.uuid), outResult);
       }
     }
   }
 
-  private Map<Integer, List<PatchLineComment>> index(
-      List<PatchLineComment> in) {
-    HashMap<Integer, List<PatchLineComment>> r = new HashMap<>();
-    for (final PatchLineComment p : in) {
-      List<PatchLineComment> l = r.get(p.getLine());
+  private Map<Integer, List<Comment>> index(List<Comment> in) {
+    HashMap<Integer, List<Comment>> r = new HashMap<>();
+    for (Comment p : in) {
+      List<Comment> l = r.get(p.lineNbr);
       if (l == null) {
         l = new ArrayList<>();
-        r.put(p.getLine(), l);
+        r.put(p.lineNbr, l);
       }
       l.add(p);
     }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
index b181341..632ddd8 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeApi.java
@@ -34,7 +34,7 @@
 public class ChangeApi {
   /** Abandon the change, ending its review. */
   public static void abandon(int id, String msg, AsyncCallback<ChangeInfo> cb) {
-    Input input = Input.create();
+    MessageInput input = MessageInput.create();
     input.message(emptyToNull(msg));
     call(id, "abandon").post(input, cb);
   }
@@ -64,14 +64,14 @@
 
   /** Restore a previously abandoned change to be open again. */
   public static void restore(int id, String msg, AsyncCallback<ChangeInfo> cb) {
-    Input input = Input.create();
+    MessageInput input = MessageInput.create();
     input.message(emptyToNull(msg));
     call(id, "restore").post(input, cb);
   }
 
   /** Create a new change that reverts the delta caused by this change. */
   public static void revert(int id, String msg, AsyncCallback<ChangeInfo> cb) {
-    Input input = Input.create();
+    MessageInput input = MessageInput.create();
     input.message(emptyToNull(msg));
     call(id, "revert").post(input, cb);
   }
@@ -81,7 +81,7 @@
     RestApi call = call(id, "topic");
     topic = emptyToNull(topic);
     if (topic != null) {
-      Input input = Input.create();
+      TopicInput input = TopicInput.create();
       input.topic(topic);
       call.put(input, NativeString.unwrap(cb));
     } else {
@@ -244,15 +244,25 @@
     call(id, commit, "rebase").post(rebaseInput, cb);
   }
 
-  private static class Input extends JavaScriptObject {
-    final native void topic(String t) /*-{ if(t)this.topic=t; }-*/;
+  private static class MessageInput extends JavaScriptObject {
     final native void message(String m) /*-{ if(m)this.message=m; }-*/;
 
-    static Input create() {
-      return (Input) createObject();
+    static MessageInput create() {
+      return (MessageInput) createObject();
     }
 
-    protected Input() {
+    protected MessageInput() {
+    }
+  }
+
+  private static class TopicInput extends JavaScriptObject {
+    final native void topic(String t) /*-{ if(t)this.topic=t; }-*/;
+
+    static TopicInput create() {
+      return (TopicInput) createObject();
+    }
+
+    protected TopicInput() {
     }
   }
 
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Comment.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Comment.java
new file mode 100644
index 0000000..f37b6bd
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/Comment.java
@@ -0,0 +1,263 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.reviewdb.client;
+
+import java.sql.Timestamp;
+import java.util.Objects;
+
+/**
+ * This class represents inline comments in NoteDb. This means it determines the
+ * JSON format for inline comments in the revision notes that NoteDb uses to
+ * persist inline comments.
+ * <p>
+ * Changing fields in this class changes the storage format of inline comments
+ * in NoteDb and may require a corresponding data migration (adding new optional
+ * fields is generally okay).
+ * <p>
+ * {@link PatchLineComment} also represents inline comments, but in ReviewDb.
+ * There are a few notable differences:
+ * <ul>
+ * <li>PatchLineComment knows the comment status (published or draft). For
+ * comments in NoteDb the status is determined by the branch in which they are
+ * stored (published comments are stored in the change meta ref; draft comments
+ * are store in refs/draft-comments branches in All-Users). Hence Comment
+ * doesn't need to contain the status, but the status is implicitly known by
+ * where the comments are read from.
+ * <li>PatchLineComment knows the change ID. For comments in NoteDb, the change
+ * ID is determined by the branch in which they are stored (the ref name
+ * contains the change ID). Hence Comment doesn't need to contain the change ID,
+ * but the change ID is implicitly known by where the comments are read from.
+ * </ul>
+ * <p>
+ * For all utility classes and middle layer functionality using Comment over
+ * PatchLineComment is preferred, as PatchLineComment will go away together with
+ * ReviewDb. This means Comment should be used everywhere and only for storing
+ * inline comment in ReviewDb a conversion to PatchLineComment is done.
+ * Converting Comments to PatchLineComments and vice verse is done by
+ * CommentsUtil#toPatchLineComments(Change.Id, PatchLineComment.Status, Iterable)
+ * and CommentsUtil#toComments(String, Iterable).
+ */
+public class Comment {
+  public static class Key {
+    public String uuid;
+    public String filename;
+    public int patchSetId;
+
+    public Key(Key k) {
+      this(k.uuid, k.filename, k.patchSetId);
+    }
+
+    public Key(String uuid, String filename, int patchSetId) {
+      this.uuid = uuid;
+      this.filename = filename;
+      this.patchSetId = patchSetId;
+    }
+
+    @Override
+    public String toString() {
+      return new StringBuilder()
+          .append("Comment.Key{")
+          .append("uuid=").append(uuid).append(',')
+          .append("filename=").append(filename).append(',')
+          .append("patchSetId=").append(patchSetId)
+          .append('}')
+          .toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (o instanceof Key) {
+        Key k = (Key) o;
+        return Objects.equals(uuid, k.uuid)
+            && Objects.equals(filename, k.filename)
+            && Objects.equals(patchSetId, k.patchSetId);
+      }
+      return false;
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(uuid, filename, patchSetId);
+    }
+  }
+
+  public static class Identity {
+    int id;
+
+    public Identity(Account.Id id) {
+      this.id = id.get();
+    }
+
+    public Account.Id getId() {
+      return new Account.Id(id);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (o instanceof Identity) {
+        return Objects.equals(id, ((Identity) o).id);
+      }
+      return false;
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(id);
+    }
+
+    @Override
+    public String toString() {
+      return new StringBuilder()
+          .append("Comment.Identity{")
+          .append("id=").append(id)
+          .append('}')
+          .toString();
+    }
+  }
+
+  public static class Range {
+    public int startLine;
+    public int startChar;
+    public int endLine;
+    public int endChar;
+
+    public Range(Range r) {
+      this(r.startLine, r.startChar, r.endLine, r.endChar);
+    }
+
+    public Range(com.google.gerrit.extensions.client.Comment.Range r) {
+      this(r.startLine, r.startCharacter, r.endLine, r.endCharacter);
+    }
+
+    public Range(int startLine, int startChar, int endLine, int endChar) {
+      this.startLine = startLine;
+      this.startChar = startChar;
+      this.endLine = endLine;
+      this.endChar = endChar;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (o instanceof Range) {
+        Range r = (Range) o;
+        return Objects.equals(startLine, r.startLine)
+            && Objects.equals(startChar, r.startChar)
+            && Objects.equals(endLine, r.endLine)
+            && Objects.equals(endChar, r.endChar);
+      }
+      return false;
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(startLine, startChar, endLine, endChar);
+    }
+
+    @Override
+    public String toString() {
+      return new StringBuilder()
+          .append("Comment.Range{")
+          .append("startLine=").append(startLine).append(',')
+          .append("startChar=").append(startChar).append(',')
+          .append("endLine=").append(endLine).append(',')
+          .append("endChar=").append(endChar)
+          .append('}')
+          .toString();
+    }
+  }
+
+  public Key key;
+  public int lineNbr;
+  public Identity author;
+  public Timestamp writtenOn;
+  public short side;
+  public String message;
+  public String parentUuid;
+  public Range range;
+  public String tag;
+  public String revId;
+  public String serverId;
+
+  public Comment(Comment c) {
+    this(new Key(c.key), c.author.getId(), new Timestamp(c.writtenOn.getTime()),
+        c.side, c.message, c.serverId);
+    this.lineNbr = c.lineNbr;
+    this.range = c.range != null ? new Range(c.range) : null;
+    this.tag = c.tag;
+    this.revId = c.revId;
+  }
+
+  public Comment(Key key, Account.Id author, Timestamp writtenOn,
+      short side, String message, String serverId) {
+    this.key = key;
+    this.author = new Comment.Identity(author);
+    this.writtenOn = writtenOn;
+    this.side = side;
+    this.message = message;
+    this.serverId = serverId;
+  }
+
+  public void setLineNbrAndRange(Integer lineNbr,
+      com.google.gerrit.extensions.client.Comment.Range range) {
+    this.lineNbr = lineNbr != null
+        ? lineNbr
+        : range != null
+            ? range.endLine
+            : 0;
+    if (range != null) {
+      this.range = new Comment.Range(range);
+    }
+  }
+
+  public void setRange(CommentRange range) {
+    this.range = range != null ? range.asCommentRange() : null;
+  }
+
+  public void setRevId(RevId revId) {
+    this.revId = revId != null ? revId.get() : null;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (o instanceof Comment) {
+      return Objects.equals(key, ((Comment) o).key);
+    }
+    return false;
+  }
+
+  @Override
+  public int hashCode() {
+    return key.hashCode();
+  }
+
+  @Override
+  public String toString() {
+    return new StringBuilder()
+        .append("Comment{")
+        .append("key=").append(key).append(',')
+        .append("lineNbr=").append(lineNbr).append(',')
+        .append("author=").append(author.getId().get()).append(',')
+        .append("writtenOn=").append(writtenOn.toString()).append(',')
+        .append("side=").append(side).append(',')
+        .append("message=").append(Objects.toString(message, "")).append(',')
+        .append("parentUuid=")
+            .append(Objects.toString(parentUuid, "")).append(',')
+        .append("range=").append(Objects.toString(range, "")).append(',')
+        .append("revId=").append(revId != null ? revId : "")
+        .append("tag=").append(Objects.toString(tag, ""))
+        .append('}')
+        .toString();
+  }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/CommentRange.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/CommentRange.java
index 5a98d94..0cc3e58 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/CommentRange.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/CommentRange.java
@@ -72,6 +72,10 @@
     endCharacter = ec;
   }
 
+  public Comment.Range asCommentRange() {
+    return new Comment.Range(startLine, startCharacter, endLine, endCharacter);
+  }
+
   @Override
   public boolean equals(Object obj) {
     if (obj instanceof CommentRange) {
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchLineComment.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchLineComment.java
index 16b2d61..5a3cc16 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchLineComment.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchLineComment.java
@@ -21,11 +21,24 @@
 import java.sql.Timestamp;
 import java.util.Objects;
 
-/** A comment left by a user on a specific line of a {@link Patch}. */
+/**
+ * A comment left by a user on a specific line of a {@link Patch}.
+ *
+ * This class represents an inline comment in ReviewDb. It should only be used
+ * for writing/reading inline comments to/from ReviewDb. For all other purposes
+ * inline comments should be represented by {@link Comment}.
+ *
+ * @see Comment
+ */
 public final class PatchLineComment {
   public static class Key extends StringKey<Patch.Key> {
     private static final long serialVersionUID = 1L;
 
+    public static Key from(Change.Id changeId, Comment.Key key) {
+      return new Key(new Patch.Key(new PatchSet.Id(changeId, key.patchSetId),
+          key.filename), key.uuid);
+    }
+
     @Column(id = 1, name = Column.NONE)
     protected Patch.Key patchKey;
 
@@ -55,6 +68,12 @@
     public void set(String newValue) {
       uuid = newValue;
     }
+
+    public Comment.Key asCommentKey() {
+      return new Comment.Key(get(),
+          getParentKey().getFileName(),
+          getParentKey().getParentKey().get());
+    }
   }
 
   public static final char STATUS_DRAFT = 'd';
@@ -85,6 +104,28 @@
     }
   }
 
+  public static PatchLineComment from(Change.Id changeId,
+      PatchLineComment.Status status, Comment c) {
+    PatchLineComment.Key key = new PatchLineComment.Key(
+        new Patch.Key(new PatchSet.Id(changeId, c.key.patchSetId),
+            c.key.filename),
+        c.key.uuid);
+
+    PatchLineComment plc = new PatchLineComment(key, c.lineNbr,
+        c.author.getId(), c.parentUuid, c.writtenOn);
+    plc.setSide(c.side);
+    plc.setMessage(c.message);
+    if (c.range != null) {
+      Comment.Range r = c.range;
+      plc.setRange(
+          new CommentRange(r.startLine, r.startChar, r.endLine, r.endChar));
+    }
+    plc.setTag(c.tag);
+    plc.setRevId(new RevId(c.revId));
+    plc.setStatus(status);
+    return plc;
+  }
+
   @Column(id = 1, name = Column.NONE)
   protected Key key;
 
@@ -260,6 +301,17 @@
     return tag;
   }
 
+  public Comment asComment(String serverId) {
+    Comment c = new Comment(key.asCommentKey(), author, writtenOn, side,
+        message, serverId);
+    c.setRevId(revId);
+    c.setRange(range);
+    c.lineNbr = lineNbr;
+    c.parentUuid = parentUuid;
+    c.tag = tag;
+    return c;
+  }
+
   @Override
   public boolean equals(Object o) {
     if (o instanceof PatchLineComment) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/PatchLineCommentsUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/CommentsUtil.java
similarity index 64%
rename from gerrit-server/src/main/java/com/google/gerrit/server/PatchLineCommentsUtil.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/CommentsUtil.java
index 68065cb..f15ff66 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/PatchLineCommentsUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/CommentsUtil.java
@@ -28,14 +28,15 @@
 import com.google.gerrit.extensions.common.CommentInfo;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.Patch;
 import com.google.gerrit.reviewdb.client.PatchLineComment;
 import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.client.RevId;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.config.GerritServerId;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.notedb.ChangeUpdate;
@@ -63,25 +64,23 @@
 import java.util.stream.StreamSupport;
 
 /**
- * Utility functions to manipulate PatchLineComments.
+ * Utility functions to manipulate Comments.
  * <p>
- * These methods either query for and update PatchLineComments in the NoteDb or
+ * These methods either query for and update Comments in the NoteDb or
  * ReviewDb, depending on the state of the NotesMigration.
  */
 @Singleton
-public class PatchLineCommentsUtil {
-  public static final Ordering<PatchLineComment> PLC_ORDER =
-      new Ordering<PatchLineComment>() {
+public class CommentsUtil {
+  public static final Ordering<Comment> COMMENT_ORDER =
+      new Ordering<Comment>() {
     @Override
-    public int compare(PatchLineComment c1, PatchLineComment c2) {
-      String filename1 = c1.getKey().getParentKey().get();
-      String filename2 = c2.getKey().getParentKey().get();
+    public int compare(Comment c1, Comment c2) {
       return ComparisonChain.start()
-          .compare(filename1, filename2)
-          .compare(getCommentPsId(c1).get(), getCommentPsId(c2).get())
-          .compare(c1.getSide(), c2.getSide())
-          .compare(c1.getLine(), c2.getLine())
-          .compare(c1.getWrittenOn(), c2.getWrittenOn())
+          .compare(c1.key.filename, c2.key.filename)
+          .compare(c1.key.patchSetId, c2.key.patchSetId)
+          .compare(c1.side, c2.side)
+          .compare(c1.lineNbr, c2.lineNbr)
+          .compare(c1.writtenOn, c2.writtenOn)
           .result();
     }
   };
@@ -104,8 +103,9 @@
         }
       };
 
-  public static PatchSet.Id getCommentPsId(PatchLineComment plc) {
-    return plc.getKey().getParentKey().getParentKey();
+  public static PatchSet.Id getCommentPsId(Change.Id changeId,
+      Comment comment) {
+    return new PatchSet.Id(changeId, comment.key.patchSetId);
   }
 
   private static final Ordering<Comparable<?>> NULLS_FIRST =
@@ -114,55 +114,61 @@
   private final GitRepositoryManager repoManager;
   private final AllUsersName allUsers;
   private final NotesMigration migration;
+  private final String serverId;
 
   @Inject
-  PatchLineCommentsUtil(GitRepositoryManager repoManager,
+  CommentsUtil(GitRepositoryManager repoManager,
       AllUsersName allUsers,
-      NotesMigration migration) {
+      NotesMigration migration,
+      @GerritServerId String serverId) {
     this.repoManager = repoManager;
     this.allUsers = allUsers;
     this.migration = migration;
+    this.serverId = serverId;
   }
 
-  public Optional<PatchLineComment> get(ReviewDb db, ChangeNotes notes,
-      PatchLineComment.Key key) throws OrmException {
+  public Optional<Comment> get(ReviewDb db, ChangeNotes notes,
+      Comment.Key key) throws OrmException {
     if (!migration.readChanges()) {
-      return Optional.fromNullable(db.patchComments().get(key));
+      PatchLineComment plc = db.patchComments()
+          .get(PatchLineComment.Key.from(notes.getChangeId(), key));
+      Comment c = plc != null ? plc.asComment(serverId) : null;
+      return Optional.fromNullable(c);
     }
-    for (PatchLineComment c : publishedByChange(db, notes)) {
-      if (key.equals(c.getKey())) {
+    for (Comment c : publishedByChange(db, notes)) {
+      if (key.equals(c.key)) {
         return Optional.of(c);
       }
     }
-    for (PatchLineComment c : draftByChange(db, notes)) {
-      if (key.equals(c.getKey())) {
+    for (Comment c : draftByChange(db, notes)) {
+      if (key.equals(c.key)) {
         return Optional.of(c);
       }
     }
     return Optional.absent();
   }
 
-  public List<PatchLineComment> publishedByChange(ReviewDb db,
-      ChangeNotes notes) throws OrmException {
+  public List<Comment> publishedByChange(ReviewDb db, ChangeNotes notes)
+      throws OrmException {
     if (!migration.readChanges()) {
       return sort(byCommentStatus(
           db.patchComments().byChange(notes.getChangeId()), Status.PUBLISHED));
     }
 
     notes.load();
-    List<PatchLineComment> comments = new ArrayList<>();
+    List<Comment> comments = new ArrayList<>();
     comments.addAll(notes.getComments().values());
     return sort(comments);
   }
 
-  public List<PatchLineComment> draftByChange(ReviewDb db,
-      ChangeNotes notes) throws OrmException {
+  public List<Comment> draftByChange(ReviewDb db, ChangeNotes notes)
+      throws OrmException {
     if (!migration.readChanges()) {
       return sort(byCommentStatus(
           db.patchComments().byChange(notes.getChangeId()), Status.DRAFT));
     }
 
-    List<PatchLineComment> comments = new ArrayList<>();
+    List<Comment> comments = new ArrayList<>();
     for (Ref ref : getDraftRefs(notes.getChangeId())) {
       Account.Id account = Account.Id.fromRefSuffix(ref.getName());
       if (account != null) {
@@ -172,19 +178,19 @@
     return sort(comments);
   }
 
-  private static List<PatchLineComment> byCommentStatus(
-      ResultSet<PatchLineComment> comments,
+  private List<Comment> byCommentStatus(ResultSet<PatchLineComment> comments,
       final PatchLineComment.Status status) {
-    return Lists.newArrayList(
-      Iterables.filter(comments, c -> c.getStatus() == status));
+    return toComments(serverId, Lists.newArrayList(
+      Iterables.filter(comments, c -> c.getStatus() == status)));
   }
 
-  public List<PatchLineComment> byPatchSet(ReviewDb db,
+  public List<Comment> byPatchSet(ReviewDb db,
       ChangeNotes notes, PatchSet.Id psId) throws OrmException {
     if (!migration.readChanges()) {
-      return sort(db.patchComments().byPatchSet(psId).toList());
+      return sort(toComments(serverId,
+          db.patchComments().byPatchSet(psId).toList()));
     }
-    List<PatchLineComment> comments = new ArrayList<>();
+    List<Comment> comments = new ArrayList<>();
     comments.addAll(publishedByPatchSet(db, notes, psId));
 
     for (Ref ref : getDraftRefs(notes.getChangeId())) {
@@ -196,20 +202,20 @@
     return sort(comments);
   }
 
-  public List<PatchLineComment> publishedByChangeFile(ReviewDb db,
-      ChangeNotes notes, Change.Id changeId, String file) throws OrmException {
+  public List<Comment> publishedByChangeFile(ReviewDb db, ChangeNotes notes,
+      Change.Id changeId, String file) throws OrmException {
     if (!migration.readChanges()) {
-      return sort(
-          db.patchComments().publishedByChangeFile(changeId, file).toList());
+      return sort(toComments(serverId,
+          db.patchComments().publishedByChangeFile(changeId, file).toList()));
     }
     return commentsOnFile(notes.load().getComments().values(), file);
   }
 
-  public List<PatchLineComment> publishedByPatchSet(ReviewDb db,
+  public List<Comment> publishedByPatchSet(ReviewDb db,
       ChangeNotes notes, PatchSet.Id psId) throws OrmException {
     if (!migration.readChanges()) {
-      return removeCommentsOnAncestorOfCommitMessage(sort(
-          db.patchComments().publishedByPatchSet(psId).toList()));
+      return removeCommentsOnAncestorOfCommitMessage(sort(toComments(serverId,
+          db.patchComments().publishedByPatchSet(psId).toList())));
     }
     return removeCommentsOnAncestorOfCommitMessage(
         commentsOnPatchSet(notes.load().getComments().values(), psId));
@@ -223,50 +229,47 @@
    * done. From that time there may still be comments on the auto-merge commit
    * message and those we want to filter out.
    */
-  private List<PatchLineComment> removeCommentsOnAncestorOfCommitMessage(
-      List<PatchLineComment> list) {
+  private List<Comment> removeCommentsOnAncestorOfCommitMessage(
+      List<Comment> list) {
     return list.stream()
-        .filter(c -> c.getSide() != 0
-            || !Patch.COMMIT_MSG.equals(c.getKey().getParentKey().get()))
+        .filter(c -> c.side != 0 || !Patch.COMMIT_MSG.equals(c.key.filename))
         .collect(toList());
   }
 
-  public List<PatchLineComment> draftByPatchSetAuthor(ReviewDb db,
-      PatchSet.Id psId, Account.Id author, ChangeNotes notes)
-      throws OrmException {
+  public List<Comment> draftByPatchSetAuthor(ReviewDb db, PatchSet.Id psId,
+      Account.Id author, ChangeNotes notes) throws OrmException {
     if (!migration.readChanges()) {
-      return sort(
-          db.patchComments().draftByPatchSetAuthor(psId, author).toList());
+      return sort(toComments(serverId,
+          db.patchComments().draftByPatchSetAuthor(psId, author).toList()));
     }
-    return commentsOnPatchSet(
-        notes.load().getDraftComments(author).values(), psId);
+    return commentsOnPatchSet(notes.load().getDraftComments(author).values(), psId);
   }
 
-  public List<PatchLineComment> draftByChangeFileAuthor(ReviewDb db,
+  public List<Comment> draftByChangeFileAuthor(ReviewDb db,
       ChangeNotes notes, String file, Account.Id author)
       throws OrmException {
     if (!migration.readChanges()) {
-      return sort(
+      return sort(toComments(serverId,
           db.patchComments()
             .draftByChangeFileAuthor(notes.getChangeId(), file, author)
-            .toList());
+            .toList()));
     }
-    return commentsOnFile(
-        notes.load().getDraftComments(author).values(), file);
+    return commentsOnFile(notes.load().getDraftComments(author).values(), file);
   }
 
-  public List<PatchLineComment> draftByChangeAuthor(ReviewDb db,
+  public List<Comment> draftByChangeAuthor(ReviewDb db,
       ChangeNotes notes, Account.Id author)
       throws OrmException {
     if (!migration.readChanges()) {
-      final Change.Id matchId = notes.getChangeId();
-      return StreamSupport.stream(
-              db.patchComments().draftByAuthor(author).spliterator(), false)
-          .filter(c -> c.getPatchSetId().getParentKey().equals(matchId))
-          .sorted(PLC_ORDER)
+      return StreamSupport
+          .stream(db.patchComments().draftByAuthor(author).spliterator(), false)
+          .filter(c -> c.getPatchSetId().getParentKey()
+              .equals(notes.getChangeId()))
+          .map(plc -> plc.asComment(serverId))
+          .sorted(COMMENT_ORDER)
           .collect(toList());
     }
-    List<PatchLineComment> comments = new ArrayList<>();
+    List<Comment> comments = new ArrayList<>();
     comments.addAll(notes.getDraftComments(author).values());
     return sort(comments);
   }
@@ -297,19 +300,22 @@
   }
 
   public void putComments(ReviewDb db, ChangeUpdate update,
-      Iterable<PatchLineComment> comments) throws OrmException {
-    for (PatchLineComment c : comments) {
-      update.putComment(c);
+      PatchLineComment.Status status, Iterable<Comment> comments)
+          throws OrmException {
+    for (Comment c : comments) {
+      update.putComment(status, c);
     }
-    db.patchComments().upsert(comments);
+    db.patchComments()
+        .upsert(toPatchLineComments(update.getId(), status, comments));
   }
 
   public void deleteComments(ReviewDb db, ChangeUpdate update,
-      Iterable<PatchLineComment> comments) throws OrmException {
-    for (PatchLineComment c : comments) {
+      Iterable<Comment> comments) throws OrmException {
+    for (Comment c : comments) {
       update.deleteComment(c);
     }
-    db.patchComments().delete(comments);
+    db.patchComments().delete(toPatchLineComments(update.getId(),
+        PatchLineComment.Status.DRAFT, comments));
   }
 
   public void deleteAllDraftsFromAllUsers(Change.Id changeId)
@@ -334,12 +340,11 @@
     }
   }
 
-  private static List<PatchLineComment> commentsOnFile(
-      Collection<PatchLineComment> allComments,
+  private static List<Comment> commentsOnFile(Collection<Comment> allComments,
       String file) {
-    List<PatchLineComment> result = new ArrayList<>(allComments.size());
-    for (PatchLineComment c : allComments) {
-      String currentFilename = c.getKey().getParentKey().getFileName();
+    List<Comment> result = new ArrayList<>(allComments.size());
+    for (Comment c : allComments) {
+      String currentFilename = c.key.filename;
       if (currentFilename.equals(file)) {
         result.add(c);
       }
@@ -347,34 +352,32 @@
     return sort(result);
   }
 
-  private static List<PatchLineComment> commentsOnPatchSet(
-      Collection<PatchLineComment> allComments,
+  private static List<Comment> commentsOnPatchSet(
+      Collection<Comment> allComments,
       PatchSet.Id psId) {
-    List<PatchLineComment> result = new ArrayList<>(allComments.size());
-    for (PatchLineComment c : allComments) {
-      if (getCommentPsId(c).equals(psId)) {
+    List<Comment> result = new ArrayList<>(allComments.size());
+    for (Comment c : allComments) {
+      if (c.key.patchSetId == psId.get()) {
         result.add(c);
       }
     }
     return sort(result);
   }
 
-  public static void setCommentRevId(PatchLineComment c,
+  public static void setCommentRevId(Comment c,
       PatchListCache cache, Change change, PatchSet ps) throws OrmException {
-    checkArgument(c.getPatchSetId().equals(ps.getId()),
+    checkArgument(c.key.patchSetId == ps.getId().get(),
         "cannot set RevId for patch set %s on comment %s", ps.getId(), c);
-    if (c.getRevId() == null) {
+    if (c.revId == null) {
       try {
-        if (Side.fromShort(c.getSide()) == Side.PARENT) {
-          if (c.getSide() < 0) {
-            c.setRevId(new RevId(ObjectId.toString(
-                cache.getOldId(change, ps, -c.getSide()))));
+        if (Side.fromShort(c.side) == Side.PARENT) {
+          if (c.side < 0) {
+            c.revId = ObjectId.toString(cache.getOldId(change, ps, -c.side));
           } else {
-            c.setRevId(new RevId(ObjectId.toString(
-                cache.getOldId(change, ps, null))));
+            c.revId = ObjectId.toString(cache.getOldId(change, ps, null));
           }
         } else {
-          c.setRevId(ps.getRevision());
+          c.revId = ps.getRevision().get();
         }
       } catch (PatchListNotAvailableException e) {
         throw new OrmException(e);
@@ -397,8 +400,21 @@
         RefNames.refsDraftCommentsPrefix(changeId)).values();
   }
 
-  private static List<PatchLineComment> sort(List<PatchLineComment> comments) {
-    Collections.sort(comments, PLC_ORDER);
+  private static List<Comment> sort(List<Comment> comments) {
+    Collections.sort(comments, COMMENT_ORDER);
     return comments;
   }
+
+  public static Iterable<PatchLineComment> toPatchLineComments(
+      Change.Id changeId, PatchLineComment.Status status,
+      Iterable<Comment> comments) {
+    return FluentIterable.from(comments)
+        .transform(c -> PatchLineComment.from(changeId, status, c));
+  }
+
+  public static List<Comment> toComments(final String serverId,
+      Iterable<PatchLineComment> comments) {
+    return COMMENT_ORDER.sortedCopy(FluentIterable.from(comments)
+        .transform(plc -> plc.asComment(serverId)));
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentJson.java
index 2948fa6..1e48717 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentJson.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.change;
 
-import static com.google.gerrit.server.PatchLineCommentsUtil.COMMENT_INFO_ORDER;
+import static com.google.gerrit.server.CommentsUtil.COMMENT_INFO_ORDER;
 
 import com.google.common.base.Strings;
 import com.google.common.collect.FluentIterable;
@@ -22,8 +22,7 @@
 import com.google.gerrit.extensions.client.Side;
 import com.google.gerrit.extensions.common.CommentInfo;
 import com.google.gerrit.extensions.restapi.Url;
-import com.google.gerrit.reviewdb.client.CommentRange;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.server.account.AccountLoader;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -56,7 +55,7 @@
     return this;
   }
 
-  CommentInfo format(PatchLineComment c) throws OrmException {
+  CommentInfo format(Comment c) throws OrmException {
     AccountLoader loader = null;
     if (fillAccounts) {
       loader = accountLoaderFactory.create(true);
@@ -68,14 +67,14 @@
     return commentInfo;
   }
 
-  Map<String, List<CommentInfo>> format(Iterable<PatchLineComment> l)
+  Map<String, List<CommentInfo>> format(Iterable<Comment> l)
       throws OrmException {
     Map<String, List<CommentInfo>> out = new TreeMap<>();
     AccountLoader accountLoader = fillAccounts
         ? accountLoaderFactory.create(true)
         : null;
 
-    for (PatchLineComment c : l) {
+    for (Comment c : l) {
       CommentInfo o = toCommentInfo(c, accountLoader);
       List<CommentInfo> list = out.get(o.path);
       if (list == null) {
@@ -97,7 +96,7 @@
     return out;
   }
 
-  List<CommentInfo> formatAsList(Iterable<PatchLineComment> l)
+  List<CommentInfo> formatAsList(Iterable<Comment> l)
       throws OrmException {
     AccountLoader accountLoader = fillAccounts
         ? accountLoaderFactory.create(true)
@@ -114,41 +113,41 @@
     return out;
   }
 
-  private CommentInfo toCommentInfo(PatchLineComment c, AccountLoader loader) {
+  private CommentInfo toCommentInfo(Comment c, AccountLoader loader) {
     CommentInfo r = new CommentInfo();
     if (fillPatchSet) {
-      r.patchSet = c.getKey().getParentKey().getParentKey().get();
+      r.patchSet = c.key.patchSetId;
     }
-    r.id = Url.encode(c.getKey().get());
-    r.path = c.getKey().getParentKey().getFileName();
-    if (c.getSide() <= 0) {
+    r.id = Url.encode(c.key.uuid);
+    r.path = c.key.filename;
+    if (c.side <= 0) {
       r.side = Side.PARENT;
-      if (c.getSide() < 0) {
-        r.parent = -c.getSide();
+      if (c.side < 0) {
+        r.parent = -c.side;
       }
     }
-    if (c.getLine() > 0) {
-      r.line = c.getLine();
+    if (c.lineNbr > 0) {
+      r.line = c.lineNbr;
     }
-    r.inReplyTo = Url.encode(c.getParentUuid());
-    r.message = Strings.emptyToNull(c.getMessage());
-    r.updated = c.getWrittenOn();
-    r.range = toRange(c.getRange());
-    r.tag = c.getTag();
+    r.inReplyTo = Url.encode(c.parentUuid);
+    r.message = Strings.emptyToNull(c.message);
+    r.updated = c.writtenOn;
+    r.range = toRange(c.range);
+    r.tag = c.tag;
     if (loader != null) {
-      r.author = loader.get(c.getAuthor());
+      r.author = loader.get(c.author.getId());
     }
     return r;
   }
 
-  private Range toRange(CommentRange commentRange) {
+  private Range toRange(Comment.Range commentRange) {
     Range range = null;
     if (commentRange != null) {
       range = new Range();
-      range.startLine = commentRange.getStartLine();
-      range.startCharacter = commentRange.getStartCharacter();
-      range.endLine = commentRange.getEndLine();
-      range.endCharacter = commentRange.getEndCharacter();
+      range.startLine = commentRange.startLine;
+      range.startCharacter = commentRange.startChar;
+      range.endLine = commentRange.endLine;
+      range.endCharacter = commentRange.endChar;
     }
     return range;
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentResource.java
index c535e9e..40c8515 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentResource.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentResource.java
@@ -17,7 +17,7 @@
 import com.google.gerrit.extensions.restapi.RestResource;
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.inject.TypeLiteral;
 
@@ -26,9 +26,9 @@
       new TypeLiteral<RestView<CommentResource>>() {};
 
   private final RevisionResource rev;
-  private final PatchLineComment comment;
+  private final Comment comment;
 
-  public CommentResource(RevisionResource rev, PatchLineComment c) {
+  public CommentResource(RevisionResource rev, Comment c) {
     this.rev = rev;
     this.comment = c;
   }
@@ -37,15 +37,15 @@
     return rev.getPatchSet();
   }
 
-  PatchLineComment getComment() {
+  Comment getComment() {
     return comment;
   }
 
   String getId() {
-    return comment.getKey().get();
+    return comment.key.uuid;
   }
 
   Account.Id getAuthorId() {
-    return comment.getAuthor();
+    return comment.author.getId();
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Comments.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Comments.java
index 8f78f0e..6ce7dda 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Comments.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Comments.java
@@ -19,9 +19,9 @@
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.PatchLineCommentsUtil;
+import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -33,16 +33,16 @@
   private final DynamicMap<RestView<CommentResource>> views;
   private final ListRevisionComments list;
   private final Provider<ReviewDb> dbProvider;
-  private final PatchLineCommentsUtil plcUtil;
+  private final CommentsUtil commentsUtil;
 
   @Inject
   Comments(DynamicMap<RestView<CommentResource>> views,
       ListRevisionComments list, Provider<ReviewDb> dbProvider,
-      PatchLineCommentsUtil plcUtil) {
+      CommentsUtil commentsUtil) {
     this.views = views;
     this.list = list;
     this.dbProvider = dbProvider;
-    this.plcUtil = plcUtil;
+    this.commentsUtil = commentsUtil;
   }
 
   @Override
@@ -61,9 +61,9 @@
     String uuid = id.get();
     ChangeNotes notes = rev.getNotes();
 
-    for (PatchLineComment c : plcUtil.publishedByPatchSet(dbProvider.get(),
+    for (Comment c : commentsUtil.publishedByPatchSet(dbProvider.get(),
         notes, rev.getPatchSet().getId())) {
-      if (uuid.equals(c.getKey().get())) {
+      if (uuid.equals(c.key.uuid)) {
         return new CommentResource(rev, c);
       }
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraftComment.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraftComment.java
index 1af87b3..37af37f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraftComment.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraftComment.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.change;
 
-import static com.google.gerrit.server.PatchLineCommentsUtil.setCommentRevId;
+import static com.google.gerrit.server.CommentsUtil.setCommentRevId;
 
 import com.google.common.base.Strings;
 import com.google.gerrit.common.TimeUtil;
@@ -26,13 +26,14 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.Url;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.Comment;
+import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ChangeUtil;
-import com.google.gerrit.server.PatchLineCommentsUtil;
+import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.PatchSetUtil;
+import com.google.gerrit.server.config.GerritServerId;
 import com.google.gerrit.server.git.BatchUpdate;
 import com.google.gerrit.server.git.BatchUpdate.ChangeContext;
 import com.google.gerrit.server.git.UpdateException;
@@ -49,23 +50,26 @@
   private final Provider<ReviewDb> db;
   private final BatchUpdate.Factory updateFactory;
   private final Provider<CommentJson> commentJson;
-  private final PatchLineCommentsUtil plcUtil;
+  private final CommentsUtil commentsUtil;
   private final PatchSetUtil psUtil;
   private final PatchListCache patchListCache;
+  private final String serverId;
 
   @Inject
   CreateDraftComment(Provider<ReviewDb> db,
       BatchUpdate.Factory updateFactory,
       Provider<CommentJson> commentJson,
-      PatchLineCommentsUtil plcUtil,
+      CommentsUtil commentsUtil,
       PatchSetUtil psUtil,
-      PatchListCache patchListCache) {
+      PatchListCache patchListCache,
+      @GerritServerId String serverId) {
     this.db = db;
     this.updateFactory = updateFactory;
     this.commentJson = commentJson;
-    this.plcUtil = plcUtil;
+    this.commentsUtil = commentsUtil;
     this.psUtil = psUtil;
     this.patchListCache = patchListCache;
+    this.serverId = serverId;
   }
 
   @Override
@@ -95,7 +99,7 @@
     private final PatchSet.Id psId;
     private final DraftInput in;
 
-    private PatchLineComment comment;
+    private Comment comment;
 
     private Op(PatchSet.Id psId, DraftInput in) {
       this.psId = psId;
@@ -109,23 +113,22 @@
       if (ps == null) {
         throw new ResourceNotFoundException("patch set not found: " + psId);
       }
-      int line = in.line != null
-          ? in.line
-          : in.range != null ? in.range.endLine : 0;
-      comment = new PatchLineComment(
-          new PatchLineComment.Key(
-              new Patch.Key(ps.getId(), in.path),
-              ChangeUtil.messageUUID(ctx.getDb())),
-          line, ctx.getAccountId(), Url.decode(in.inReplyTo),
-          ctx.getWhen());
-      comment.setSide(in.side());
-      comment.setMessage(in.message.trim());
-      comment.setRange(in.range);
-      comment.setTag(in.tag);
+      comment = new Comment(
+          new Comment.Key(ChangeUtil.messageUUID(ctx.getDb()), in.path,
+              ps.getPatchSetId()),
+          ctx.getAccountId(),
+          ctx.getWhen(),
+          in.side(),
+          in.message.trim(),
+          serverId);
+      comment.parentUuid = Url.decode(in.inReplyTo);
+      comment.setLineNbrAndRange(in.line, in.range);
+      comment.tag = in.tag;
       setCommentRevId(
           comment, patchListCache, ctx.getChange(), ps);
-      plcUtil.putComments(
-          ctx.getDb(), ctx.getUpdate(psId), Collections.singleton(comment));
+
+      commentsUtil.putComments(ctx.getDb(), ctx.getUpdate(psId), Status.DRAFT,
+          Collections.singleton(comment));
       ctx.bumpLastUpdatedOn(false);
       return true;
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftComment.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftComment.java
index 56dbfa7..9a4c02d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftComment.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraftComment.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.change;
 
-import static com.google.gerrit.server.PatchLineCommentsUtil.setCommentRevId;
+import static com.google.gerrit.server.CommentsUtil.setCommentRevId;
 
 import com.google.common.base.Optional;
 import com.google.gerrit.common.TimeUtil;
@@ -23,10 +23,10 @@
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.PatchLineCommentsUtil;
+import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.change.DeleteDraftComment.Input;
 import com.google.gerrit.server.git.BatchUpdate;
@@ -47,19 +47,19 @@
   }
 
   private final Provider<ReviewDb> db;
-  private final PatchLineCommentsUtil plcUtil;
+  private final CommentsUtil commentsUtil;
   private final PatchSetUtil psUtil;
   private final BatchUpdate.Factory updateFactory;
   private final PatchListCache patchListCache;
 
   @Inject
   DeleteDraftComment(Provider<ReviewDb> db,
-      PatchLineCommentsUtil plcUtil,
+      CommentsUtil commentsUtil,
       PatchSetUtil psUtil,
       BatchUpdate.Factory updateFactory,
       PatchListCache patchListCache) {
     this.db = db;
-    this.plcUtil = plcUtil;
+    this.commentsUtil = commentsUtil;
     this.psUtil = psUtil;
     this.updateFactory = updateFactory;
     this.patchListCache = patchListCache;
@@ -71,7 +71,7 @@
     try (BatchUpdate bu = updateFactory.create(
         db.get(), rsrc.getChange().getProject(), rsrc.getControl().getUser(),
         TimeUtil.nowTs())) {
-      Op op = new Op(rsrc.getComment().getKey());
+      Op op = new Op(rsrc.getComment().key);
       bu.addOp(rsrc.getChange().getId(), op);
       bu.execute();
     }
@@ -79,28 +79,29 @@
   }
 
   private class Op extends BatchUpdate.Op {
-    private final PatchLineComment.Key key;
+    private final Comment.Key key;
 
-    private Op(PatchLineComment.Key key) {
+    private Op(Comment.Key key) {
       this.key = key;
     }
 
     @Override
     public boolean updateChange(ChangeContext ctx)
         throws ResourceNotFoundException, OrmException {
-      Optional<PatchLineComment> maybeComment =
-          plcUtil.get(ctx.getDb(), ctx.getNotes(), key);
+      Optional<Comment> maybeComment =
+          commentsUtil.get(ctx.getDb(), ctx.getNotes(), key);
       if (!maybeComment.isPresent()) {
         return false; // Nothing to do.
       }
-      PatchSet.Id psId = key.getParentKey().getParentKey();
+      PatchSet.Id psId =
+          new PatchSet.Id(ctx.getChange().getId(), key.patchSetId);
       PatchSet ps = psUtil.get(ctx.getDb(), ctx.getNotes(), psId);
       if (ps == null) {
         throw new ResourceNotFoundException("patch set not found: " + psId);
       }
-      PatchLineComment c = maybeComment.get();
+      Comment c = maybeComment.get();
       setCommentRevId(c, patchListCache, ctx.getChange(), ps);
-      plcUtil.deleteComments(
+      commentsUtil.deleteComments(
           ctx.getDb(), ctx.getUpdate(psId), Collections.singleton(c));
       ctx.bumpLastUpdatedOn(false);
       return true;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DraftCommentResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DraftCommentResource.java
index 3dc0c78..781216c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DraftCommentResource.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DraftCommentResource.java
@@ -18,7 +18,7 @@
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.server.project.ChangeControl;
 import com.google.inject.TypeLiteral;
@@ -28,9 +28,9 @@
       new TypeLiteral<RestView<DraftCommentResource>>() {};
 
   private final RevisionResource rev;
-  private final PatchLineComment comment;
+  private final Comment comment;
 
-  public DraftCommentResource(RevisionResource rev, PatchLineComment c) {
+  public DraftCommentResource(RevisionResource rev, Comment c) {
     this.rev = rev;
     this.comment = c;
   }
@@ -47,12 +47,12 @@
     return rev.getPatchSet();
   }
 
-  PatchLineComment getComment() {
+  Comment getComment() {
     return comment;
   }
 
   String getId() {
-    return comment.getKey().get();
+    return comment.key.uuid;
   }
 
   Account.Id getAuthorId() {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DraftComments.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DraftComments.java
index acb50ac..1729f93 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DraftComments.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DraftComments.java
@@ -20,10 +20,10 @@
 import com.google.gerrit.extensions.restapi.IdString;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.PatchLineCommentsUtil;
+import com.google.gerrit.server.CommentsUtil;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -35,19 +35,19 @@
   private final Provider<CurrentUser> user;
   private final ListRevisionDrafts list;
   private final Provider<ReviewDb> dbProvider;
-  private final PatchLineCommentsUtil plcUtil;
+  private final CommentsUtil commentsUtil;
 
   @Inject
   DraftComments(DynamicMap<RestView<DraftCommentResource>> views,
       Provider<CurrentUser> user,
       ListRevisionDrafts list,
       Provider<ReviewDb> dbProvider,
-      PatchLineCommentsUtil plcUtil) {
+      CommentsUtil commentsUtil) {
     this.views = views;
     this.user = user;
     this.list = list;
     this.dbProvider = dbProvider;
-    this.plcUtil = plcUtil;
+    this.commentsUtil = commentsUtil;
   }
 
   @Override
@@ -66,9 +66,9 @@
       throws ResourceNotFoundException, OrmException, AuthException {
     checkIdentifiedUser();
     String uuid = id.get();
-    for (PatchLineComment c : plcUtil.draftByPatchSetAuthor(dbProvider.get(),
+    for (Comment c : commentsUtil.draftByPatchSetAuthor(dbProvider.get(),
         rev.getPatchSet().getId(), rev.getAccountId(), rev.getNotes())) {
-      if (uuid.equals(c.getKey().get())) {
+      if (uuid.equals(c.key.uuid)) {
         return new DraftCommentResource(rev, c);
       }
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/EmailReviewComments.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/EmailReviewComments.java
index 390f416..f887be4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/EmailReviewComments.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/EmailReviewComments.java
@@ -14,11 +14,11 @@
 
 package com.google.gerrit.server.change;
 
-import static com.google.gerrit.server.PatchLineCommentsUtil.PLC_ORDER;
+import static com.google.gerrit.server.CommentsUtil.COMMENT_ORDER;
 
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CurrentUser;
@@ -52,7 +52,7 @@
         PatchSet patchSet,
         IdentifiedUser user,
         ChangeMessage message,
-        List<PatchLineComment> comments);
+        List<Comment> comments);
   }
 
   private final ExecutorService sendEmailsExecutor;
@@ -66,7 +66,7 @@
   private final PatchSet patchSet;
   private final IdentifiedUser user;
   private final ChangeMessage message;
-  private List<PatchLineComment> comments;
+  private final List<Comment> comments;
   private ReviewDb db;
 
   @Inject
@@ -81,7 +81,7 @@
       @Assisted PatchSet patchSet,
       @Assisted IdentifiedUser user,
       @Assisted ChangeMessage message,
-      @Assisted List<PatchLineComment> comments) {
+      @Assisted List<Comment> comments) {
     this.sendEmailsExecutor = executor;
     this.patchSetInfoFactory = patchSetInfoFactory;
     this.commentSenderFactory = commentSenderFactory;
@@ -92,7 +92,7 @@
     this.patchSet = patchSet;
     this.user = user;
     this.message = message;
-    this.comments = PLC_ORDER.sortedCopy(comments);
+    this.comments = COMMENT_ORDER.sortedCopy(comments);
   }
 
   void sendAsync() {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListChangeComments.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListChangeComments.java
index 97befa0..40fa7c8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListChangeComments.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListChangeComments.java
@@ -18,7 +18,7 @@
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.PatchLineCommentsUtil;
+import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -33,17 +33,17 @@
   private final Provider<ReviewDb> db;
   private final ChangeData.Factory changeDataFactory;
   private final Provider<CommentJson> commentJson;
-  private final PatchLineCommentsUtil plcUtil;
+  private final CommentsUtil commentsUtil;
 
   @Inject
   ListChangeComments(Provider<ReviewDb> db,
       ChangeData.Factory changeDataFactory,
       Provider<CommentJson> commentJson,
-      PatchLineCommentsUtil plcUtil) {
+      CommentsUtil commentsUtil) {
     this.db = db;
     this.changeDataFactory = changeDataFactory;
     this.commentJson = commentJson;
-    this.plcUtil = plcUtil;
+    this.commentsUtil = commentsUtil;
   }
 
   @Override
@@ -53,6 +53,6 @@
     return commentJson.get()
         .setFillAccounts(true)
         .setFillPatchSet(true)
-        .format(plcUtil.publishedByChange(db.get(), cd.notes()));
+        .format(commentsUtil.publishedByChange(db.get(), cd.notes()));
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListChangeDrafts.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListChangeDrafts.java
index 561a040..dbbd35d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListChangeDrafts.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListChangeDrafts.java
@@ -17,9 +17,9 @@
 import com.google.gerrit.extensions.common.CommentInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.PatchLineCommentsUtil;
+import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -34,17 +34,17 @@
   private final Provider<ReviewDb> db;
   private final ChangeData.Factory changeDataFactory;
   private final Provider<CommentJson> commentJson;
-  private final PatchLineCommentsUtil plcUtil;
+  private final CommentsUtil commentsUtil;
 
   @Inject
   ListChangeDrafts(Provider<ReviewDb> db,
       ChangeData.Factory changeDataFactory,
       Provider<CommentJson> commentJson,
-      PatchLineCommentsUtil plcUtil) {
+      CommentsUtil commentsUtil) {
     this.db = db;
     this.changeDataFactory = changeDataFactory;
     this.commentJson = commentJson;
-    this.plcUtil = plcUtil;
+    this.commentsUtil = commentsUtil;
   }
 
   @Override
@@ -54,7 +54,7 @@
       throw new AuthException("Authentication required");
     }
     ChangeData cd = changeDataFactory.create(db.get(), rsrc.getControl());
-    List<PatchLineComment> drafts = plcUtil.draftByChangeAuthor(
+    List<Comment> drafts = commentsUtil.draftByChangeAuthor(
         db.get(), cd.notes(), rsrc.getControl().getUser().getAccountId());
     return commentJson.get()
         .setFillAccounts(false)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListRevisionComments.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListRevisionComments.java
index 2392781..8524b8e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListRevisionComments.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListRevisionComments.java
@@ -14,9 +14,9 @@
 
 package com.google.gerrit.server.change;
 
-import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.PatchLineCommentsUtil;
+import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -28,8 +28,8 @@
   @Inject
   ListRevisionComments(Provider<ReviewDb> db,
       Provider<CommentJson> commentJson,
-      PatchLineCommentsUtil plcUtil) {
-    super(db, commentJson, plcUtil);
+      CommentsUtil commentsUtil) {
+    super(db, commentJson, commentsUtil);
   }
 
   @Override
@@ -38,9 +38,10 @@
   }
 
   @Override
-  protected Iterable<PatchLineComment> listComments(RevisionResource rsrc)
+  protected Iterable<Comment> listComments(RevisionResource rsrc)
       throws OrmException {
     ChangeNotes notes = rsrc.getNotes();
-    return plcUtil.publishedByPatchSet(db.get(), notes, rsrc.getPatchSet().getId());
+    return commentsUtil.publishedByPatchSet(db.get(), notes,
+        rsrc.getPatchSet().getId());
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListRevisionDrafts.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListRevisionDrafts.java
index ef12b2a..2c45219 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListRevisionDrafts.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListRevisionDrafts.java
@@ -16,9 +16,9 @@
 
 import com.google.gerrit.extensions.common.CommentInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.PatchLineCommentsUtil;
+import com.google.gerrit.server.CommentsUtil;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -31,20 +31,20 @@
 public class ListRevisionDrafts implements RestReadView<RevisionResource> {
   protected final Provider<ReviewDb> db;
   protected final Provider<CommentJson> commentJson;
-  protected final PatchLineCommentsUtil plcUtil;
+  protected final CommentsUtil commentsUtil;
 
   @Inject
   ListRevisionDrafts(Provider<ReviewDb> db,
       Provider<CommentJson> commentJson,
-      PatchLineCommentsUtil plcUtil) {
+      CommentsUtil commentsUtil) {
     this.db = db;
     this.commentJson = commentJson;
-    this.plcUtil = plcUtil;
+    this.commentsUtil = commentsUtil;
   }
 
-  protected Iterable<PatchLineComment> listComments(RevisionResource rsrc)
+  protected Iterable<Comment> listComments(RevisionResource rsrc)
       throws OrmException {
-    return plcUtil.draftByPatchSetAuthor(db.get(), rsrc.getPatchSet().getId(),
+    return commentsUtil.draftByPatchSetAuthor(db.get(), rsrc.getPatchSet().getId(),
         rsrc.getAccountId(), rsrc.getNotes());
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
index d6819ba..1a41f7d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
@@ -16,7 +16,7 @@
 
 import static com.google.common.base.MoreObjects.firstNonNull;
 import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.gerrit.server.PatchLineCommentsUtil.setCommentRevId;
+import static com.google.gerrit.server.CommentsUtil.setCommentRevId;
 import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
@@ -54,20 +54,21 @@
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.extensions.restapi.Url;
 import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.CommentRange;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.ChangeMessagesUtil;
 import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.PatchLineCommentsUtil;
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.ReviewerSet;
 import com.google.gerrit.server.account.AccountsCollection;
+import com.google.gerrit.server.config.GerritServerId;
 import com.google.gerrit.server.extensions.events.CommentAdded;
 import com.google.gerrit.server.git.BatchUpdate;
 import com.google.gerrit.server.git.BatchUpdate.ChangeContext;
@@ -109,13 +110,14 @@
   private final ChangeData.Factory changeDataFactory;
   private final ApprovalsUtil approvalsUtil;
   private final ChangeMessagesUtil cmUtil;
-  private final PatchLineCommentsUtil plcUtil;
+  private final CommentsUtil commentsUtil;
   private final PatchSetUtil psUtil;
   private final PatchListCache patchListCache;
   private final AccountsCollection accounts;
   private final EmailReviewComments.Factory email;
   private final CommentAdded commentAdded;
   private final PostReviewers postReviewers;
+  private final String serverId;
 
   @Inject
   PostReview(Provider<ReviewDb> db,
@@ -124,18 +126,19 @@
       ChangeData.Factory changeDataFactory,
       ApprovalsUtil approvalsUtil,
       ChangeMessagesUtil cmUtil,
-      PatchLineCommentsUtil plcUtil,
+      CommentsUtil commentsUtil,
       PatchSetUtil psUtil,
       PatchListCache patchListCache,
       AccountsCollection accounts,
       EmailReviewComments.Factory email,
       CommentAdded commentAdded,
-      PostReviewers postReviewers) {
+      PostReviewers postReviewers,
+      @GerritServerId String serverId) {
     this.db = db;
     this.batchUpdateFactory = batchUpdateFactory;
     this.changes = changes;
     this.changeDataFactory = changeDataFactory;
-    this.plcUtil = plcUtil;
+    this.commentsUtil = commentsUtil;
     this.psUtil = psUtil;
     this.patchListCache = patchListCache;
     this.approvalsUtil = approvalsUtil;
@@ -144,6 +147,7 @@
     this.email = email;
     this.commentAdded = commentAdded;
     this.postReviewers = postReviewers;
+    this.serverId = serverId;
   }
 
   @Override
@@ -371,25 +375,27 @@
    */
   @AutoValue
   abstract static class CommentSetEntry {
-    private static CommentSetEntry create(Patch.Key key,
-        Integer line, Side side, HashCode message, CommentRange range) {
-      return new AutoValue_PostReview_CommentSetEntry(key, line, side, message,
-          range);
+    private static CommentSetEntry create(String filename, int patchSetId,
+        Integer line, Side side, HashCode message, Comment.Range range) {
+      return new AutoValue_PostReview_CommentSetEntry(filename, patchSetId,
+          line, side, message, range);
     }
 
-    public static CommentSetEntry create(PatchLineComment comment) {
-      return create(comment.getKey().getParentKey(),
-          comment.getLine(),
-          Side.fromShort(comment.getSide()),
-          Hashing.sha1().hashString(comment.getMessage(), UTF_8),
-          comment.getRange());
+    public static CommentSetEntry create(Comment comment) {
+      return create(comment.key.filename,
+          comment.key.patchSetId,
+          comment.lineNbr,
+          Side.fromShort(comment.side),
+          Hashing.sha1().hashString(comment.message, UTF_8),
+          comment.range);
     }
 
-    abstract Patch.Key key();
+    abstract String filename();
+    abstract int patchSetId();
     @Nullable abstract Integer line();
     abstract Side side();
     abstract HashCode message();
-    @Nullable abstract CommentRange range();
+    @Nullable abstract Comment.Range range();
   }
 
   private class Op extends BatchUpdate.Op {
@@ -401,7 +407,7 @@
     private ChangeNotes notes;
     private PatchSet ps;
     private ChangeMessage message;
-    private List<PatchLineComment> comments = new ArrayList<>();
+    private List<Comment> comments = new ArrayList<>();
     private List<String> labelDelta = new ArrayList<>();
     private Map<String, Short> approvals = new HashMap<>();
     private Map<String, Short> oldApprovals = new HashMap<>();
@@ -451,7 +457,7 @@
         map = Collections.emptyMap();
       }
 
-      Map<String, PatchLineComment> drafts = Collections.emptyMap();
+      Map<String, Comment> drafts = Collections.emptyMap();
       if (!map.isEmpty() || in.drafts != DraftHandling.KEEP) {
         if (in.drafts == DraftHandling.PUBLISH_ALL_REVISIONS) {
           drafts = changeDrafts(ctx);
@@ -460,8 +466,8 @@
         }
       }
 
-      List<PatchLineComment> del = new ArrayList<>();
-      List<PatchLineComment> ups = new ArrayList<>();
+      List<Comment> toDel = new ArrayList<>();
+      List<Comment> toPublish = new ArrayList<>();
 
       Set<CommentSetEntry> existingIds = in.omitDuplicateComments
           ? readExistingComments(ctx)
@@ -471,37 +477,33 @@
         String path = ent.getKey();
         for (CommentInput c : ent.getValue()) {
           String parent = Url.decode(c.inReplyTo);
-          PatchLineComment e = drafts.remove(Url.decode(c.id));
+          Comment e = drafts.remove(Url.decode(c.id));
           if (e == null) {
-            e = new PatchLineComment(
-                new PatchLineComment.Key(new Patch.Key(psId, path), null),
-                c.line != null ? c.line : 0,
+            e = new Comment(
+                new Comment.Key(ChangeUtil.messageUUID(ctx.getDb()), path,
+                    psId.get()),
                 user.getAccountId(),
-                parent, ctx.getWhen());
-          } else if (parent != null) {
-            e.setParentUuid(parent);
+                ctx.getWhen(),
+                c.side(),
+                c.message,
+                serverId);
+          } else {
+            e.writtenOn = ctx.getWhen();
+            e.side = c.side();
+            e.message = c.message;
           }
-          e.setStatus(PatchLineComment.Status.PUBLISHED);
-          e.setWrittenOn(ctx.getWhen());
-          e.setSide(c.side());
+
+          if (parent != null) {
+            e.parentUuid = parent;
+          }
           setCommentRevId(e, patchListCache, ctx.getChange(), ps);
-          e.setMessage(c.message);
-          e.setTag(in.tag);
-          if (c.range != null) {
-            e.setRange(new CommentRange(
-                c.range.startLine,
-                c.range.startCharacter,
-                c.range.endLine,
-                c.range.endCharacter));
-            e.setLine(c.range.endLine);
-          }
+          e.setLineNbrAndRange(c.line, c.range);
+          e.tag = in.tag;
+
           if (existingIds.contains(CommentSetEntry.create(e))) {
             continue;
           }
-          if (e.getKey().get() == null) {
-            e.getKey().set(ChangeUtil.messageUUID(ctx.getDb()));
-          }
-          ups.add(e);
+          toPublish.add(e);
         }
       }
 
@@ -510,51 +512,51 @@
         default:
           break;
         case DELETE:
-          del.addAll(drafts.values());
+          toDel.addAll(drafts.values());
           break;
         case PUBLISH:
-          for (PatchLineComment e : drafts.values()) {
-            ups.add(publishComment(ctx, e, ps));
+          for (Comment e : drafts.values()) {
+            toPublish.add(publishComment(ctx, e, ps));
           }
           break;
         case PUBLISH_ALL_REVISIONS:
-          publishAllRevisions(ctx, drafts, ups);
+          publishAllRevisions(ctx, drafts, toPublish);
           break;
       }
       ChangeUpdate u = ctx.getUpdate(psId);
-      plcUtil.deleteComments(ctx.getDb(), u, del);
-      plcUtil.putComments(ctx.getDb(), u, ups);
-      comments.addAll(ups);
-      return !del.isEmpty() || !ups.isEmpty();
+      commentsUtil.deleteComments(ctx.getDb(), u, toDel);
+      commentsUtil.putComments(ctx.getDb(), u, Status.PUBLISHED, toPublish);
+      comments.addAll(toPublish);
+      return !toDel.isEmpty() || !toPublish.isEmpty();
     }
 
     private Set<CommentSetEntry> readExistingComments(ChangeContext ctx)
         throws OrmException {
       Set<CommentSetEntry> r = new HashSet<>();
-      for (PatchLineComment c : plcUtil.publishedByChange(ctx.getDb(),
+      for (Comment c : commentsUtil.publishedByChange(ctx.getDb(),
             ctx.getNotes())) {
         r.add(CommentSetEntry.create(c));
       }
       return r;
     }
 
-    private Map<String, PatchLineComment> changeDrafts(ChangeContext ctx)
+    private Map<String, Comment> changeDrafts(ChangeContext ctx)
         throws OrmException {
-      Map<String, PatchLineComment> drafts = new HashMap<>();
-      for (PatchLineComment c : plcUtil.draftByChangeAuthor(
+      Map<String, Comment> drafts = new HashMap<>();
+      for (Comment c : commentsUtil.draftByChangeAuthor(
           ctx.getDb(), ctx.getNotes(), user.getAccountId())) {
-        c.setTag(in.tag);
-        drafts.put(c.getKey().get(), c);
+        c.tag = in.tag;
+        drafts.put(c.key.uuid, c);
       }
       return drafts;
     }
 
-    private Map<String, PatchLineComment> patchSetDrafts(ChangeContext ctx)
+    private Map<String, Comment> patchSetDrafts(ChangeContext ctx)
         throws OrmException {
-      Map<String, PatchLineComment> drafts = new HashMap<>();
-      for (PatchLineComment c : plcUtil.draftByPatchSetAuthor(ctx.getDb(),
+      Map<String, Comment> drafts = new HashMap<>();
+      for (Comment c : commentsUtil.draftByPatchSetAuthor(ctx.getDb(),
           psId, user.getAccountId(), ctx.getNotes())) {
-        drafts.put(c.getKey().get(), c);
+        drafts.put(c.key.uuid, c);
       }
       return drafts;
     }
@@ -568,21 +570,20 @@
       return labels;
     }
 
-    private PatchLineComment publishComment(ChangeContext ctx,
-        PatchLineComment c, PatchSet ps) throws OrmException {
-      c.setStatus(PatchLineComment.Status.PUBLISHED);
-      c.setWrittenOn(ctx.getWhen());
-      c.setTag(in.tag);
+    private Comment publishComment(ChangeContext ctx,
+        Comment c, PatchSet ps) throws OrmException {
+      c.writtenOn = ctx.getWhen();
+      c.tag = in.tag;
       setCommentRevId(c, patchListCache, ctx.getChange(), checkNotNull(ps));
       return c;
     }
 
     private void publishAllRevisions(ChangeContext ctx,
-        Map<String, PatchLineComment> drafts, List<PatchLineComment> ups)
+        Map<String, Comment> drafts, List<Comment> ups)
         throws OrmException {
       boolean needOtherPatchSets = false;
-      for (PatchLineComment c : drafts.values()) {
-        if (!c.getPatchSetId().equals(psId)) {
+      for (Comment c : drafts.values()) {
+        if (c.key.patchSetId != psId.get()) {
           needOtherPatchSets = true;
           break;
         }
@@ -590,8 +591,9 @@
       Map<PatchSet.Id, PatchSet> patchSets = needOtherPatchSets
           ? psUtil.byChangeAsMap(ctx.getDb(), ctx.getNotes())
           : ImmutableMap.of(psId, ps);
-      for (PatchLineComment e : drafts.values()) {
-        ups.add(publishComment(ctx, e, patchSets.get(e.getPatchSetId())));
+      for (Comment e : drafts.values()) {
+        ups.add(publishComment(ctx, e, patchSets
+            .get(new PatchSet.Id(ctx.getChange().getId(), e.key.patchSetId))));
       }
     }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraftComment.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraftComment.java
index e817d93..feb17b3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraftComment.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraftComment.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.change;
 
-import static com.google.gerrit.server.PatchLineCommentsUtil.setCommentRevId;
+import static com.google.gerrit.server.CommentsUtil.setCommentRevId;
 
 import com.google.common.base.Optional;
 import com.google.gerrit.common.TimeUtil;
@@ -26,11 +26,11 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.extensions.restapi.Url;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.Comment;
+import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.PatchLineCommentsUtil;
+import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.git.BatchUpdate;
 import com.google.gerrit.server.git.BatchUpdate.ChangeContext;
@@ -50,7 +50,7 @@
 
   private final Provider<ReviewDb> db;
   private final DeleteDraftComment delete;
-  private final PatchLineCommentsUtil plcUtil;
+  private final CommentsUtil commentsUtil;
   private final PatchSetUtil psUtil;
   private final BatchUpdate.Factory updateFactory;
   private final Provider<CommentJson> commentJson;
@@ -59,14 +59,14 @@
   @Inject
   PutDraftComment(Provider<ReviewDb> db,
       DeleteDraftComment delete,
-      PatchLineCommentsUtil plcUtil,
+      CommentsUtil commentsUtil,
       PatchSetUtil psUtil,
       BatchUpdate.Factory updateFactory,
       Provider<CommentJson> commentJson,
       PatchListCache patchListCache) {
     this.db = db;
     this.delete = delete;
-    this.plcUtil = plcUtil;
+    this.commentsUtil = commentsUtil;
     this.psUtil = psUtil;
     this.updateFactory = updateFactory;
     this.commentJson = commentJson;
@@ -89,7 +89,7 @@
     try (BatchUpdate bu = updateFactory.create(
         db.get(), rsrc.getChange().getProject(), rsrc.getControl().getUser(),
         TimeUtil.nowTs())) {
-      Op op = new Op(rsrc.getComment().getKey(), in);
+      Op op = new Op(rsrc.getComment().key, in);
       bu.addOp(rsrc.getChange().getId(), op);
       bu.execute();
       return Response.ok(
@@ -98,12 +98,12 @@
   }
 
   private class Op extends BatchUpdate.Op {
-    private final PatchLineComment.Key key;
+    private final Comment.Key key;
     private final DraftInput in;
 
-    private PatchLineComment comment;
+    private Comment comment;
 
-    private Op(PatchLineComment.Key key, DraftInput in) {
+    private Op(Comment.Key key, DraftInput in) {
       this.key = key;
       this.in = in;
     }
@@ -111,17 +111,18 @@
     @Override
     public boolean updateChange(ChangeContext ctx)
         throws ResourceNotFoundException, OrmException {
-      Optional<PatchLineComment> maybeComment =
-          plcUtil.get(ctx.getDb(), ctx.getNotes(), key);
+      Optional<Comment> maybeComment =
+          commentsUtil.get(ctx.getDb(), ctx.getNotes(), key);
       if (!maybeComment.isPresent()) {
         // Disappeared out from under us. Can't easily fall back to insert,
         // because the input might be missing required fields. Just give up.
         throw new ResourceNotFoundException("comment not found: " + key);
       }
-      PatchLineComment origComment = maybeComment.get();
-      comment = new PatchLineComment(origComment);
+      Comment origComment = maybeComment.get();
+      comment = new Comment(origComment);
 
-      PatchSet.Id psId = comment.getKey().getParentKey().getParentKey();
+      PatchSet.Id psId =
+          new PatchSet.Id(ctx.getChange().getId(), origComment.key.patchSetId);
       ChangeUpdate update = ctx.getUpdate(psId);
 
       PatchSet ps = psUtil.get(ctx.getDb(), ctx.getNotes(), psId);
@@ -129,53 +130,35 @@
         throw new ResourceNotFoundException("patch set not found: " + psId);
       }
       if (in.path != null
-          && !in.path.equals(comment.getKey().getParentKey().getFileName())) {
+          && !in.path.equals(origComment.key.filename)) {
         // Updating the path alters the primary key, which isn't possible.
         // Delete then recreate the comment instead of an update.
 
-        plcUtil.deleteComments(
+        commentsUtil.deleteComments(
             ctx.getDb(), update, Collections.singleton(origComment));
-        comment = new PatchLineComment(
-            new PatchLineComment.Key(
-                new Patch.Key(psId, in.path),
-                comment.getKey().get()),
-            comment.getLine(),
-            ctx.getAccountId(),
-            comment.getParentUuid(), ctx.getWhen());
-        comment.setTag(origComment.getTag());
-        setCommentRevId(comment, patchListCache, ctx.getChange(), ps);
-        plcUtil.putComments(ctx.getDb(), update,
-            Collections.singleton(update(comment, in, ctx.getWhen())));
-      } else {
-        if (comment.getRevId() == null) {
-          setCommentRevId(
-              comment, patchListCache, ctx.getChange(), ps);
-        }
-        plcUtil.putComments(ctx.getDb(), update,
-            Collections.singleton(update(comment, in, ctx.getWhen())));
+        comment.key.filename = in.path;
       }
+      setCommentRevId(comment, patchListCache, ctx.getChange(), ps);
+      commentsUtil.putComments(ctx.getDb(), update, Status.DRAFT,
+          Collections.singleton(update(comment, in, ctx.getWhen())));
       ctx.bumpLastUpdatedOn(false);
       return true;
     }
   }
 
-  private static PatchLineComment update(PatchLineComment e, DraftInput in,
-      Timestamp when) {
+  private static Comment update(Comment e, DraftInput in, Timestamp when) {
     if (in.side != null) {
-      e.setSide(in.side());
+      e.side = in.side();
     }
     if (in.inReplyTo != null) {
-      e.setParentUuid(Url.decode(in.inReplyTo));
+      e.parentUuid = Url.decode(in.inReplyTo);
     }
-    e.setMessage(in.message.trim());
-    if (in.range != null || in.line != null) {
-      e.setRange(in.range);
-      e.setLine(in.range != null ? in.range.endLine : in.line);
-    }
-    e.setWrittenOn(when);
+    e.setLineNbrAndRange(in.line, in.range);
+    e.message = in.message.trim();
+    e.writtenOn = when;
     if (in.tag != null) {
       // TODO(dborowitz): Can we support changing tags via PUT?
-      e.setTag(in.tag);
+      e.tag = in.tag;
     }
     return e;
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
index 44b5979..a1440e8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
@@ -27,8 +27,8 @@
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.ChangeMessage;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.reviewdb.client.UserIdentity;
@@ -398,10 +398,10 @@
   }
 
   public void addPatchSetComments(PatchSetAttribute patchSetAttribute,
-      Collection<PatchLineComment> patchLineComments) {
-    for (PatchLineComment comment : patchLineComments) {
-      if (comment.getKey().getParentKey().getParentKey().get()
-          == Integer.parseInt(patchSetAttribute.number)) {
+      Collection<Comment> comments) {
+    for (Comment comment : comments) {
+      if (comment.key.patchSetId ==
+          Integer.parseInt(patchSetAttribute.number)) {
         if (patchSetAttribute.comments == null) {
           patchSetAttribute.comments = new ArrayList<>();
         }
@@ -637,12 +637,12 @@
     return a;
   }
 
-  public PatchSetCommentAttribute asPatchSetLineAttribute(PatchLineComment c) {
+  public PatchSetCommentAttribute asPatchSetLineAttribute(Comment c) {
     PatchSetCommentAttribute a = new PatchSetCommentAttribute();
-    a.reviewer = asAccountAttribute(c.getAuthor());
-    a.file = c.getKey().getParentKey().get();
-    a.line = c.getLine();
-    a.message = c.getMessage();
+    a.reviewer = asAccountAttribute(c.author.getId());
+    a.file = c.key.filename;
+    a.line = c.lineNbr;
+    a.message = c.message;
     return a;
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/AllChangesIndexer.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/AllChangesIndexer.java
index d659215..22416ac 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/AllChangesIndexer.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/AllChangesIndexer.java
@@ -222,6 +222,10 @@
         try (Repository repo = repoManager.openRepository(project);
             ReviewDb db = schemaFactory.open()) {
           Map<String, Ref> refs = repo.getRefDatabase().getRefs(ALL);
+          // TODO(dborowitz): Pre-loading all notes is almost certainly a
+          // terrible idea for performance. If we can get rid of walking by
+          // commit (see note below), then all we need to discover here is the
+          // change IDs.
           for (ChangeNotes cn : notesFactory.scan(repo, db, project)) {
             Ref r = refs.get(cn.getChange().currentPatchSetId().toRefName());
             if (r != null) {
@@ -290,6 +294,9 @@
           }
         }
 
+        // TODO(dborowitz): This is basically pointless; it computes
+        // currentFilePaths faster than going through PatchListCache, but we
+        // still need to go through PatchListCache for changedLines.
         RevCommit bCommit;
         while ((bCommit = walk.next()) != null && !byId.isEmpty()) {
           if (byId.containsKey(bCommit)) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeField.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeField.java
index 0955a5a..8de8092 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeField.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeField.java
@@ -31,7 +31,7 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.server.ReviewerSet;
@@ -569,8 +569,8 @@
         public Iterable<String> get(ChangeData input, FillArgs args)
             throws OrmException {
           Set<String> r = new HashSet<>();
-          for (PatchLineComment c : input.publishedComments()) {
-            r.add(c.getMessage());
+          for (Comment c : input.publishedComments()) {
+            r.add(c.message);
           }
           for (ChangeMessage m : input.messages()) {
             r.add(m.getMessage());
@@ -647,8 +647,8 @@
               r.add(m.getAuthor().get());
             }
           }
-          for (PatchLineComment c : input.publishedComments()) {
-            r.add(c.getAuthor().get());
+          for (Comment c : input.publishedComments()) {
+            r.add(c.author.getId().get());
           }
           return r;
         }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java
index c4b6869..84091ad 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java
@@ -14,8 +14,6 @@
 
 package com.google.gerrit.server.mail;
 
-import static com.google.gerrit.server.PatchLineCommentsUtil.getCommentPsId;
-
 import com.google.common.base.Optional;
 import com.google.common.base.Strings;
 import com.google.common.collect.Ordering;
@@ -23,12 +21,10 @@
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.CommentRange;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
-import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.PatchLineCommentsUtil;
+import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.patch.PatchFile;
 import com.google.gerrit.server.patch.PatchList;
 import com.google.gerrit.server.patch.PatchListNotAvailableException;
@@ -56,27 +52,26 @@
     CommentSender create(Project.NameKey project, Change.Id id);
   }
 
-  private List<PatchLineComment> inlineComments = Collections.emptyList();
-  private final PatchLineCommentsUtil plcUtil;
+  private List<Comment> inlineComments = Collections.emptyList();
+  private final CommentsUtil commentsUtil;
 
   @Inject
   public CommentSender(EmailArguments ea,
-      PatchLineCommentsUtil plcUtil,
+      CommentsUtil commentsUtil,
       @Assisted Project.NameKey project,
       @Assisted Change.Id id) throws OrmException {
     super(ea, "comment", newChangeData(ea, project, id));
-    this.plcUtil = plcUtil;
+    this.commentsUtil = commentsUtil;
   }
 
-  public void setPatchLineComments(final List<PatchLineComment> plc)
+  public void setPatchLineComments(List<Comment> plc)
       throws OrmException {
     inlineComments = plc;
 
     Set<String> paths = new HashSet<>();
-    for (PatchLineComment c : plc) {
-      Patch.Key p = c.getKey().getParentKey();
-      if (!Patch.isMagic(p.getFileName())) {
-        paths.add(p.getFileName());
+    for (Comment c : plc) {
+      if (!Patch.isMagic(c.key.filename)) {
+        paths.add(c.key.filename);
       }
     }
     changeData.setCurrentFilePaths(Ordering.natural().sortedCopy(paths));
@@ -125,32 +120,34 @@
         }
       }
 
-      Patch.Key currentFileKey = null;
+      String currentFileName = null;
+      int currentPatchSetId = -1;
       PatchFile currentFileData = null;
-      for (final PatchLineComment c : inlineComments) {
-        final Patch.Key pk = c.getKey().getParentKey();
-
-        if (!pk.equals(currentFileKey)) {
-          String link = makeLink(pk);
+      for (Comment c : inlineComments) {
+        if (!c.key.filename.equals(currentFileName)
+            || c.key.patchSetId != currentPatchSetId) {
+          String link = makeLink(change.getId(), c.key);
           if (link != null) {
             cmts.append(link).append('\n');
           }
-          if (Patch.COMMIT_MSG.equals(pk.get())) {
+          if (Patch.COMMIT_MSG.equals(c.key.filename)) {
             cmts.append("Commit Message:\n\n");
-          } else if (Patch.MERGE_LIST.equals(pk.get())) {
+          } else if (Patch.MERGE_LIST.equals(c.key.filename)) {
             cmts.append("Merge List:\n\n");
           } else {
-            cmts.append("File ").append(pk.get()).append(":\n\n");
+            cmts.append("File ").append(c.key.filename).append(":\n\n");
           }
-          currentFileKey = pk;
+          currentFileName = c.key.filename;
+          currentPatchSetId = c.key.patchSetId;
 
           if (patchList != null) {
             try {
-              currentFileData = new PatchFile(repo, patchList, pk.get());
+              currentFileData =
+                  new PatchFile(repo, patchList, c.key.filename);
             } catch (IOException e) {
               log.warn(String.format(
                   "Cannot load %s from %s in %s",
-                  pk.getFileName(),
+                  c.key.filename,
                   patchList.getNewId().name(),
                   projectState.getProject().getName()), e);
               currentFileData = null;
@@ -168,26 +165,26 @@
   }
 
   private void appendComment(StringBuilder out, int contextLines,
-      PatchFile currentFileData, PatchLineComment comment) {
-    short side = comment.getSide();
-    CommentRange range = comment.getRange();
+      PatchFile currentFileData, Comment comment) {
+    short side = comment.side;
+    Comment.Range range = comment.range;
     if (range != null) {
-      String prefix = "PS" + getCommentPsId(comment).get()
-        + ", Line " + range.getStartLine() + ": ";
-      for (int n = range.getStartLine(); n <= range.getEndLine(); n++) {
-        out.append(n == range.getStartLine()
+      String prefix = "PS" + comment.key.patchSetId
+        + ", Line " + range.startLine + ": ";
+      for (int n = range.startLine; n <= range.endLine; n++) {
+        out.append(n == range.startLine
             ? prefix
             : Strings.padStart(": ", prefix.length(), ' '));
         try {
           String s = currentFileData.getLine(side, n);
-          if (n == range.getStartLine() && n == range.getEndLine()) {
+          if (n == range.startLine && n == range.endLine) {
             s = s.substring(
-                Math.min(range.getStartCharacter(), s.length()),
-                Math.min(range.getEndCharacter(), s.length()));
-          } else if (n == range.getStartLine()) {
-            s = s.substring(Math.min(range.getStartCharacter(), s.length()));
-          } else if (n == range.getEndLine()) {
-            s = s.substring(0, Math.min(range.getEndCharacter(), s.length()));
+                Math.min(range.startChar, s.length()),
+                Math.min(range.endChar, s.length()));
+          } else if (n == range.startLine) {
+            s = s.substring(Math.min(range.startChar, s.length()));
+          } else if (n == range.endLine) {
+            s = s.substring(0, Math.min(range.endChar, s.length()));
           }
           out.append(s);
         } catch (Throwable e) {
@@ -196,9 +193,9 @@
         out.append('\n');
       }
       appendQuotedParent(out, comment);
-      out.append(comment.getMessage().trim()).append('\n');
+      out.append(comment.message.trim()).append('\n');
     } else {
-      int lineNbr = comment.getLine();
+      int lineNbr = comment.lineNbr;
       int maxLines;
       try {
         maxLines = currentFileData.getLineCount(side);
@@ -213,7 +210,7 @@
         appendFileLine(out, currentFileData, side, line);
       }
       appendQuotedParent(out, comment);
-      out.append(comment.getMessage().trim()).append('\n');
+      out.append(comment.message.trim()).append('\n');
 
       for (int line = lineNbr + 1; line < stopLine; ++line) {
         appendFileLine(out, currentFileData, side, line);
@@ -233,21 +230,20 @@
     cmts.append("\n");
   }
 
-  private void appendQuotedParent(StringBuilder out, PatchLineComment child) {
-    if (child.getParentUuid() != null) {
-      Optional<PatchLineComment> parent;
-      PatchLineComment.Key key = new PatchLineComment.Key(
-          child.getKey().getParentKey(),
-          child.getParentUuid());
+  private void appendQuotedParent(StringBuilder out, Comment child) {
+    if (child.parentUuid != null) {
+      Optional<Comment> parent;
+      Comment.Key key = new Comment.Key(child.parentUuid, child.key.filename,
+          child.key.patchSetId);
       try {
-        parent = plcUtil.get(args.db.get(), changeData.notes(), key);
+        parent = commentsUtil.get(args.db.get(), changeData.notes(), key);
       } catch (OrmException e) {
         log.warn("Could not find the parent of this comment: "
             + child.toString());
         parent = Optional.absent();
       }
       if (parent.isPresent()) {
-        String msg = parent.get().getMessage().trim();
+        String msg = parent.get().message.trim();
         if (msg.length() > 75) {
           msg = msg.substring(0, 75);
         }
@@ -261,19 +257,17 @@
   }
 
   // Makes a link back to the given patch set and file.
-  private String makeLink(Patch.Key patch) {
+  private String makeLink(Change.Id changeId, Comment.Key key) {
     String url = getGerritUrl();
     if (url == null) {
       return null;
     }
 
-    PatchSet.Id ps = patch.getParentKey();
-    Change.Id c = ps.getParentKey();
     return new StringBuilder()
       .append(url)
-      .append("#/c/").append(c)
-      .append('/').append(ps.get())
-      .append('/').append(KeyUtil.encode(patch.get()))
+      .append("#/c/").append(changeId)
+      .append('/').append(key.patchSetId)
+      .append('/').append(KeyUtil.encode(key.filename))
       .toString();
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeBundle.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeBundle.java
index fd931f5..fd17883 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeBundle.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeBundle.java
@@ -50,7 +50,7 @@
 import com.google.gerrit.reviewdb.client.PatchLineComment;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.server.PatchLineCommentsUtil;
+import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.ReviewerSet;
 import com.google.gerrit.server.notedb.rebuild.ChangeRebuilderImpl;
 import com.google.gwtorm.client.Column;
@@ -83,16 +83,18 @@
     REVIEW_DB, NOTE_DB;
   }
 
-  public static ChangeBundle fromNotes(PatchLineCommentsUtil plcUtil,
+  public static ChangeBundle fromNotes(CommentsUtil commentsUtil,
       ChangeNotes notes) throws OrmException {
     return new ChangeBundle(
         notes.getChange(),
         notes.getChangeMessages(),
         notes.getPatchSets().values(),
         notes.getApprovals().values(),
-        Iterables.concat(
-            plcUtil.draftByChange(null, notes),
-            plcUtil.publishedByChange(null, notes)),
+        Iterables.concat(CommentsUtil.toPatchLineComments(notes.getChangeId(),
+            PatchLineComment.Status.DRAFT, commentsUtil.draftByChange(null, notes)),
+            CommentsUtil.toPatchLineComments(notes.getChangeId(),
+                PatchLineComment.Status.PUBLISHED,
+                commentsUtil.publishedByChange(null, notes))),
         notes.getReviewers(),
         Source.NOTE_DB);
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java
index ae6c4cf..6d546cd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java
@@ -22,6 +22,7 @@
 import com.google.common.collect.Sets;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.PatchLineComment;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
@@ -69,17 +70,17 @@
 
   @AutoValue
   abstract static class Key {
-    abstract RevId revId();
-    abstract PatchLineComment.Key key();
+    abstract String revId();
+    abstract Comment.Key key();
   }
 
-  private static Key key(PatchLineComment c) {
-    return new AutoValue_ChangeDraftUpdate_Key(c.getRevId(), c.getKey());
+  private static Key key(Comment c) {
+    return new AutoValue_ChangeDraftUpdate_Key(c.revId, c.key);
   }
 
   private final AllUsersName draftsProject;
 
-  private List<PatchLineComment> put = new ArrayList<>();
+  private List<Comment> put = new ArrayList<>();
   private Set<Key> delete = new HashSet<>();
 
   @AssistedInject
@@ -114,24 +115,22 @@
     this.draftsProject = allUsers;
   }
 
-  public void putComment(PatchLineComment c) {
+  public void putComment(Comment c) {
     verifyComment(c);
-    checkArgument(c.getStatus() == PatchLineComment.Status.DRAFT,
-        "Cannot insert a published comment into a ChangeDraftUpdate");
     put.add(c);
   }
 
-  public void deleteComment(PatchLineComment c) {
+  public void deleteComment(Comment c) {
     verifyComment(c);
     delete.add(key(c));
   }
 
-  public void deleteComment(RevId revId, PatchLineComment.Key key) {
+  public void deleteComment(String revId, Comment.Key key) {
     delete.add(new AutoValue_ChangeDraftUpdate_Key(revId, key));
   }
 
-  private void verifyComment(PatchLineComment comment) {
-    checkArgument(comment.getAuthor().equals(accountId),
+  private void verifyComment(Comment comment) {
+    checkArgument(comment.author.getId().equals(accountId),
         "The author for the following comment does not match the author of"
         + " this ChangeDraftUpdate (%s): %s", accountId, comment);
   }
@@ -144,13 +143,13 @@
         Sets.newHashSetWithExpectedSize(rnm.revisionNotes.size());
     RevisionNoteBuilder.Cache cache = new RevisionNoteBuilder.Cache(rnm);
 
-    for (PatchLineComment c : put) {
+    for (Comment c : put) {
       if (!delete.contains(key(c))) {
-        cache.get(c.getRevId()).putComment(c);
+        cache.get(new RevId(c.revId)).putComment(c);
       }
     }
     for (Key k : delete) {
-      cache.get(k.revId()).deleteComment(k.key());
+      cache.get(new RevId(k.revId())).deleteComment(k.key());
     }
 
     Map<RevId, RevisionNoteBuilder> builders = cache.getBuilders();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
index 960962f..078adf1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNoteUtil.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.server.notedb;
 
 import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.gerrit.server.PatchLineCommentsUtil.PLC_ORDER;
+import static com.google.gerrit.server.CommentsUtil.COMMENT_ORDER;
 import static com.google.gerrit.server.notedb.ChangeNotes.parseException;
 import static java.nio.charset.StandardCharsets.UTF_8;
 
@@ -25,13 +25,10 @@
 import com.google.common.primitives.Ints;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.CommentRange;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
-import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.RevId;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.config.AnonymousCowardName;
@@ -58,6 +55,7 @@
 import java.sql.Timestamp;
 import java.text.ParseException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
@@ -164,13 +162,13 @@
     return m == p.value + expected.length;
   }
 
-  public List<PatchLineComment> parseNote(byte[] note, MutableInteger p,
-      Change.Id changeId, Status status) throws ConfigInvalidException {
+  public List<Comment> parseNote(byte[] note, MutableInteger p,
+      Change.Id changeId) throws ConfigInvalidException {
     if (p.value >= note.length) {
       return ImmutableList.of();
     }
-    Set<PatchLineComment.Key> seen = new HashSet<>();
-    List<PatchLineComment> result = new ArrayList<>();
+    Set<Comment.Key> seen = new HashSet<>();
+    List<Comment> result = new ArrayList<>();
     int sizeOfNote = note.length;
     byte[] psb = PATCH_SET.getBytes(UTF_8);
     byte[] bpsb = BASE_PATCH_SET.getBytes(UTF_8);
@@ -201,21 +199,21 @@
             PATCH_SET, BASE_PATCH_SET);
       }
 
-      PatchLineComment c = parseComment(
-          note, p, fileName, psId, revId, isForBase, parentNumber, status);
-      fileName = c.getKey().getParentKey().getFileName();
-      if (!seen.add(c.getKey())) {
+      Comment c = parseComment(
+          note, p, fileName, psId, revId, isForBase, parentNumber);
+      fileName = c.key.filename;
+      if (!seen.add(c.key)) {
         throw parseException(
-            changeId, "multiple comments for %s in note", c.getKey());
+            changeId, "multiple comments for %s in note", c.key);
       }
       result.add(c);
     }
     return result;
   }
 
-  private PatchLineComment parseComment(byte[] note, MutableInteger curr,
+  private Comment parseComment(byte[] note, MutableInteger curr,
       String currentFileName, PatchSet.Id psId, RevId revId, boolean isForBase,
-      Integer parentNumber, Status status) throws ConfigInvalidException {
+      Integer parentNumber) throws ConfigInvalidException {
     Change.Id changeId = psId.getParentKey();
 
     // Check if there is a new file.
@@ -258,27 +256,27 @@
         UTF_8, note, curr.value, curr.value + commentLength);
     checkResult(message, "message contents", changeId);
 
-    PatchLineComment plc = new PatchLineComment(
-        new PatchLineComment.Key(new Patch.Key(psId, currentFileName), uuid),
-        range.getEndLine(), aId, parentUUID, commentTime);
-    plc.setMessage(message);
-    plc.setTag(tag);
-
-    if (isForBase) {
-      plc.setSide((short) (parentNumber == null ? 0 : -parentNumber));
-    } else {
-      plc.setSide((short) 1);
-    }
+    Comment c = new Comment(
+        new Comment.Key(uuid, currentFileName, psId.get()),
+        aId,
+        commentTime,
+        isForBase
+            ? (short) (parentNumber == null ? 0 : -parentNumber)
+            : (short) 1,
+        message,
+        serverId);
+    c.lineNbr = range.getEndLine();
+    c.parentUuid = parentUUID;
+    c.tag = tag;
+    c.setRevId(revId);
 
     if (range.getStartCharacter() != -1) {
-      plc.setRange(range);
+      c.setRange(range);
     }
-    plc.setRevId(revId);
-    plc.setStatus(status);
 
     curr.value = RawParseUtils.nextLF(note, curr.value + commentLength);
     curr.value = RawParseUtils.nextLF(note, curr.value);
-    return plc;
+    return c;
   }
 
   private static String parseStringField(byte[] note, MutableInteger curr,
@@ -492,47 +490,45 @@
    *     side.
    * @param out output stream to write to.
    */
-  void buildNote(Multimap<PatchSet.Id, PatchLineComment> comments,
+  void buildNote(Multimap<Integer, Comment> comments,
       OutputStream out) {
     if (comments.isEmpty()) {
       return;
     }
 
-    List<PatchSet.Id> psIds =
-        ReviewDbUtil.intKeyOrdering().sortedCopy(comments.keySet());
+    List<Integer> psIds = new ArrayList<>(comments.keySet());
+    Collections.sort(psIds);
 
     OutputStreamWriter streamWriter = new OutputStreamWriter(out, UTF_8);
     try (PrintWriter writer = new PrintWriter(streamWriter)) {
-      RevId revId = comments.values().iterator().next().getRevId();
-      appendHeaderField(writer, REVISION, revId.get());
+      String revId = comments.values().iterator().next().revId;
+      appendHeaderField(writer, REVISION, revId);
 
-      for (PatchSet.Id psId : psIds) {
-        List<PatchLineComment> psComments =
-            PLC_ORDER.sortedCopy(comments.get(psId));
-        PatchLineComment first = psComments.get(0);
+      for (int psId : psIds) {
+        List<Comment> psComments = COMMENT_ORDER.sortedCopy(comments.get(psId));
+        Comment first = psComments.get(0);
 
-        short side = first.getSide();
+        short side = first.side;
         appendHeaderField(writer, side <= 0
             ? BASE_PATCH_SET
             : PATCH_SET,
-            Integer.toString(psId.get()));
+            Integer.toString(psId));
         if (side < 0) {
           appendHeaderField(writer, PARENT_NUMBER, Integer.toString(-side));
         }
 
         String currentFilename = null;
 
-        for (PatchLineComment c : psComments) {
-          checkArgument(revId.equals(c.getRevId()),
+        for (Comment c : psComments) {
+          checkArgument(revId.equals(c.revId),
               "All comments being added must have all the same RevId. The "
               + "comment below does not have the same RevId as the others "
               + "(%s).\n%s", revId, c);
-          checkArgument(side == c.getSide(),
+          checkArgument(side == c.side,
               "All comments being added must all have the same side. The "
               + "comment below does not have the same side as the others "
               + "(%s).\n%s", side, c);
-          String commentFilename = QuotedString.GIT_PATH.quote(
-              c.getKey().getParentKey().getFileName());
+          String commentFilename = QuotedString.GIT_PATH.quote(c.key.filename);
 
           if (!commentFilename.equals(currentFilename)) {
             currentFilename = commentFilename;
@@ -547,30 +543,30 @@
     }
   }
 
-  private void appendOneComment(PrintWriter writer, PatchLineComment c) {
+  private void appendOneComment(PrintWriter writer, Comment c) {
     // The CommentRange field for a comment is allowed to be null. If it is
     // null, then in the first line, we simply use the line number field for a
     // comment instead. If it isn't null, we write the comment range itself.
-    CommentRange range = c.getRange();
+    Comment.Range range = c.range;
     if (range != null) {
-      writer.print(range.getStartLine());
+      writer.print(range.startLine);
       writer.print(':');
-      writer.print(range.getStartCharacter());
+      writer.print(range.startChar);
       writer.print('-');
-      writer.print(range.getEndLine());
+      writer.print(range.endLine);
       writer.print(':');
-      writer.print(range.getEndCharacter());
+      writer.print(range.endChar);
     } else {
-      writer.print(c.getLine());
+      writer.print(c.lineNbr);
     }
     writer.print("\n");
 
-    writer.print(formatTime(serverIdent, c.getWrittenOn()));
+    writer.print(formatTime(serverIdent, c.writtenOn));
     writer.print("\n");
 
     PersonIdent ident = newIdent(
-        accountCache.get(c.getAuthor()).getAccount(),
-        c.getWrittenOn(), serverIdent, anonymousCowardName);
+        accountCache.get(c.author.getId()).getAccount(),
+        c.writtenOn, serverIdent, anonymousCowardName);
     StringBuilder name = new StringBuilder();
     PersonIdent.appendSanitized(name, ident.getName());
     name.append(" <");
@@ -578,22 +574,22 @@
     name.append('>');
     appendHeaderField(writer, AUTHOR, name.toString());
 
-    String parent = c.getParentUuid();
+    String parent = c.parentUuid;
     if (parent != null) {
       appendHeaderField(writer, PARENT, parent);
     }
 
-    appendHeaderField(writer, UUID, c.getKey().get());
+    appendHeaderField(writer, UUID, c.key.uuid);
 
-    if (c.getTag() != null) {
-      appendHeaderField(writer, TAG, c.getTag());
+    if (c.tag != null) {
+      appendHeaderField(writer, TAG, c.tag);
     }
 
-    byte[] messageBytes = c.getMessage().getBytes(UTF_8);
+    byte[] messageBytes = c.message.getBytes(UTF_8);
     appendHeaderField(writer, LENGTH,
         Integer.toString(messageBytes.length));
 
-    writer.print(c.getMessage());
+    writer.print(c.message);
     writer.print("\n\n");
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java
index 8c47c7e..ae9bb96 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotes.java
@@ -39,7 +39,7 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.reviewdb.client.Project;
@@ -432,23 +432,23 @@
   }
 
   /** @return inline comments on each revision. */
-  public ImmutableListMultimap<RevId, PatchLineComment> getComments() {
+  public ImmutableListMultimap<RevId, Comment> getComments() {
     return state.publishedComments();
   }
 
-  public ImmutableListMultimap<RevId, PatchLineComment> getDraftComments(
+  public ImmutableListMultimap<RevId, Comment> getDraftComments(
       Account.Id author) throws OrmException {
     loadDraftComments(author);
-    final Multimap<RevId, PatchLineComment> published =
+    final Multimap<RevId, Comment> published =
         state.publishedComments();
     // Filter out any draft comments that also exist in the published map, in
     // case the update to All-Users to delete them during the publish operation
     // failed.
-    Multimap<RevId, PatchLineComment> filtered = Multimaps.filterEntries(
+    Multimap<RevId, Comment> filtered = Multimaps.filterEntries(
         draftCommentNotes.getComments(),
-        (Map.Entry<RevId, PatchLineComment> e) -> {
-            for (PatchLineComment c : published.get(e.getKey())) {
-              if (c.getKey().equals(e.getValue().getKey())) {
+        (Map.Entry<RevId, Comment> e) -> {
+            for (Comment c : published.get(e.getKey())) {
+              if (c.key.equals(e.getValue().key)) {
                 return false;
               }
             }
@@ -479,17 +479,17 @@
     return draftCommentNotes;
   }
 
-  public boolean containsComment(PatchLineComment c) throws OrmException {
+  public boolean containsComment(Comment c) throws OrmException {
     if (containsCommentPublished(c)) {
       return true;
     }
-    loadDraftComments(c.getAuthor());
+    loadDraftComments(c.author.getId());
     return draftCommentNotes.containsComment(c);
   }
 
-  public boolean containsCommentPublished(PatchLineComment c) {
-    for (PatchLineComment l : getComments().values()) {
-      if (c.getKey().equals(l.getKey())) {
+  public boolean containsCommentPublished(Comment c) {
+    for (Comment l : getComments().values()) {
+      if (c.key.equals(l.key)) {
         return true;
       }
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesParser.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
index 8bf8ca1..e3afe63 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
@@ -54,6 +54,7 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.ChangeMessage;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.LabelId;
 import com.google.gerrit.reviewdb.client.PatchLineComment;
 import com.google.gerrit.reviewdb.client.PatchSet;
@@ -124,7 +125,7 @@
   private final List<Account.Id> allPastReviewers;
   private final List<ReviewerStatusUpdate> reviewerUpdates;
   private final List<SubmitRecord> submitRecords;
-  private final Multimap<RevId, PatchLineComment> comments;
+  private final Multimap<RevId, Comment> comments;
   private final TreeMap<PatchSet.Id, PatchSet> patchSets;
   private final Set<PatchSet.Id> deletedPatchSets;
   private final Map<PatchSet.Id, PatchSetState> patchSetStates;
@@ -617,8 +618,8 @@
     Map<RevId, RevisionNote> rns = revisionNoteMap.revisionNotes;
 
     for (Map.Entry<RevId, RevisionNote> e : rns.entrySet()) {
-      for (PatchLineComment plc : e.getValue().comments) {
-        comments.put(e.getKey(), plc);
+      for (Comment c : e.getValue().comments) {
+        comments.put(e.getKey(), c);
       }
     }
 
@@ -861,9 +862,9 @@
         it.remove();
       }
     }
-    for (Iterator<PatchLineComment> it = comments.values().iterator();
+    for (Iterator<Comment> it = comments.values().iterator();
         it.hasNext();) {
-      PatchSet.Id psId = it.next().getKey().getParentKey().getParentKey();
+      PatchSet.Id psId = new PatchSet.Id(id, it.next().key.patchSetId);
       if (!all.contains(psId)) {
         it.remove();
       }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesState.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesState.java
index 4abb8ac..797c9b5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesState.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeNotesState.java
@@ -30,7 +30,7 @@
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.reviewdb.client.RevId;
@@ -69,7 +69,7 @@
         ImmutableList.<SubmitRecord>of(),
         ImmutableList.<ChangeMessage>of(),
         ImmutableListMultimap.<PatchSet.Id, ChangeMessage>of(),
-        ImmutableListMultimap.<RevId, PatchLineComment>of());
+        ImmutableListMultimap.<RevId, Comment>of());
   }
 
   static ChangeNotesState create(
@@ -95,7 +95,7 @@
       List<SubmitRecord> submitRecords,
       List<ChangeMessage> allChangeMessages,
       Multimap<PatchSet.Id, ChangeMessage> changeMessagesByPatchSet,
-      Multimap<RevId, PatchLineComment> publishedComments) {
+      Multimap<RevId, Comment> publishedComments) {
     if (hashtags == null) {
       hashtags = ImmutableSet.of();
     }
@@ -169,7 +169,7 @@
   abstract ImmutableList<ChangeMessage> allChangeMessages();
   abstract ImmutableListMultimap<PatchSet.Id, ChangeMessage>
       changeMessagesByPatchSet();
-  abstract ImmutableListMultimap<RevId, PatchLineComment> publishedComments();
+  abstract ImmutableListMultimap<RevId, Comment> publishedComments();
 
   void copyColumnsTo(Change change) {
     ChangeColumns c = checkNotNull(columns());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java
index 39ca57c..08b617e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/ChangeUpdate.java
@@ -47,6 +47,7 @@
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.PatchLineComment;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RevId;
@@ -113,7 +114,7 @@
 
   private final Table<String, Account.Id, Optional<Short>> approvals;
   private final Map<Account.Id, ReviewerStateInternal> reviewers = new LinkedHashMap<>();
-  private final List<PatchLineComment> comments = new ArrayList<>();
+  private final List<Comment> comments = new ArrayList<>();
 
   private String commitSubject;
   private String subject;
@@ -304,10 +305,10 @@
     this.tag = tag;
   }
 
-  public void putComment(PatchLineComment c) {
+  public void putComment(PatchLineComment.Status status, Comment c) {
     verifyComment(c);
     createDraftUpdateIfNull();
-    if (c.getStatus() == PatchLineComment.Status.DRAFT) {
+    if (status == PatchLineComment.Status.DRAFT) {
       draftUpdate.putComment(c);
     } else {
       comments.add(c);
@@ -319,14 +320,9 @@
     }
   }
 
-  public void deleteComment(PatchLineComment c) {
+  public void deleteComment(Comment c) {
     verifyComment(c);
-    if (c.getStatus() == PatchLineComment.Status.DRAFT) {
-      createDraftUpdateIfNull().deleteComment(c);
-    } else {
-      throw new IllegalArgumentException(
-          "Cannot delete published comment " + c);
-    }
+    createDraftUpdateIfNull().deleteComment(c);
   }
 
   @VisibleForTesting
@@ -344,9 +340,9 @@
     return draftUpdate;
   }
 
-  private void verifyComment(PatchLineComment c) {
-    checkArgument(c.getRevId() != null, "RevId required for comment: %s", c);
-    checkArgument(c.getAuthor().equals(getAccountId()),
+  private void verifyComment(Comment c) {
+    checkArgument(c.revId != null, "RevId required for comment: %s", c);
+    checkArgument(c.author.getId().equals(getAccountId()),
         "The author for the following comment does not match the author of"
         + " this ChangeDraftUpdate (%s): %s", getAccountId(), c);
 
@@ -417,9 +413,9 @@
     RevisionNoteMap rnm = getRevisionNoteMap(rw, curr);
 
     RevisionNoteBuilder.Cache cache = new RevisionNoteBuilder.Cache(rnm);
-    for (PatchLineComment c : comments) {
-      c.setTag(tag);
-      cache.get(c.getRevId()).putComment(c);
+    for (Comment c : comments) {
+      c.tag = tag;
+      cache.get(new RevId(c.revId)).putComment(c);
     }
     if (pushCert != null) {
       checkState(commit != null);
@@ -469,10 +465,10 @@
   private void checkComments(Map<RevId, RevisionNote> existingNotes,
       Map<RevId, RevisionNoteBuilder> toUpdate) throws OrmException {
     // Prohibit various kinds of illegal operations on comments.
-    Set<PatchLineComment.Key> existing = new HashSet<>();
+    Set<Comment.Key> existing = new HashSet<>();
     for (RevisionNote rn : existingNotes.values()) {
-      for (PatchLineComment c : rn.comments) {
-        existing.add(c.getKey());
+      for (Comment c : rn.comments) {
+        existing.add(c.key);
         if (draftUpdate != null) {
           // Take advantage of an existing update on All-Users to prune any
           // published comments from drafts. NoteDbUpdateManager takes care of
@@ -489,14 +485,14 @@
           // separate commit. But note that we don't care much about the commit
           // graph of the draft ref, particularly because the ref is completely
           // deleted when all drafts are gone.
-          draftUpdate.deleteComment(c.getRevId(), c.getKey());
+          draftUpdate.deleteComment(c.revId, c.key);
         }
       }
     }
 
     for (RevisionNoteBuilder b : toUpdate.values()) {
-      for (PatchLineComment c : b.put.values()) {
-        if (existing.contains(c.getKey())) {
+      for (Comment c : b.put.values()) {
+        if (existing.contains(c.key)) {
           throw new OrmException(
               "Cannot update existing published comment: " + c);
         }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/DraftCommentNotes.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
index 933b4ae..34da36d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
@@ -24,6 +24,7 @@
 import com.google.gerrit.metrics.Timer1;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.PatchLineComment;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
@@ -68,7 +69,7 @@
   private final Account.Id author;
   private final NoteDbUpdateManager.Result rebuildResult;
 
-  private ImmutableListMultimap<RevId, PatchLineComment> comments;
+  private ImmutableListMultimap<RevId, Comment> comments;
   private RevisionNoteMap revisionNoteMap;
 
   @AssistedInject
@@ -110,13 +111,13 @@
     return author;
   }
 
-  public ImmutableListMultimap<RevId, PatchLineComment> getComments() {
+  public ImmutableListMultimap<RevId, Comment> getComments() {
     return comments;
   }
 
-  public boolean containsComment(PatchLineComment c) {
-    for (PatchLineComment existing : comments.values()) {
-      if (c.getKey().equals(existing.getKey())) {
+  public boolean containsComment(Comment c) {
+    for (Comment existing : comments.values()) {
+      if (c.key.equals(existing.key)) {
         return true;
       }
     }
@@ -142,10 +143,10 @@
     revisionNoteMap = RevisionNoteMap.parse(
         args.noteUtil, getChangeId(), reader, NoteMap.read(reader, tipCommit),
         PatchLineComment.Status.DRAFT);
-    Multimap<RevId, PatchLineComment> cs = ArrayListMultimap.create();
+    Multimap<RevId, Comment> cs = ArrayListMultimap.create();
     for (RevisionNote rn : revisionNoteMap.revisionNotes.values()) {
-      for (PatchLineComment c : rn.comments) {
-        cs.put(c.getRevId(), c);
+      for (Comment c : rn.comments) {
+        cs.put(new RevId(c.revId), c);
       }
     }
     comments = ImmutableListMultimap.copyOf(cs);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RevisionNote.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RevisionNote.java
index 4c9f702..7d44ffb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RevisionNote.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RevisionNote.java
@@ -20,6 +20,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.primitives.Bytes;
 import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.PatchLineComment;
 
 import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -65,7 +66,7 @@
   }
 
   final byte[] raw;
-  final ImmutableList<PatchLineComment> comments;
+  final ImmutableList<Comment> comments;
   final String pushCert;
 
   RevisionNote(ChangeNoteUtil noteUtil, Change.Id changeId,
@@ -83,7 +84,7 @@
 
     if (isJson(raw, p.value)) {
       RevisionNoteData data = parseJson(noteUtil, p.value);
-      comments = data.exportComments(changeId, status);
+      comments = ImmutableList.copyOf(data.comments);
       if (status == PatchLineComment.Status.PUBLISHED) {
         pushCert = data.pushCert;
       } else {
@@ -98,8 +99,7 @@
     } else {
       pushCert = null;
     }
-    comments = ImmutableList.copyOf(
-        noteUtil.parseNote(raw, p, changeId, status));
+    comments = ImmutableList.copyOf(noteUtil.parseNote(raw, p, changeId));
   }
 
   private static boolean isJson(byte[] raw, int offset) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java
index be0a689..266a891 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RevisionNoteBuilder.java
@@ -15,15 +15,13 @@
 package com.google.gerrit.server.notedb;
 
 import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.gerrit.server.PatchLineCommentsUtil.PLC_ORDER;
+import static com.google.gerrit.server.CommentsUtil.COMMENT_ORDER;
 import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.util.stream.Collectors.toList;
 
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Multimap;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
-import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.RevId;
 
 import java.io.ByteArrayOutputStream;
@@ -63,9 +61,9 @@
   }
 
   final byte[] baseRaw;
-  final List<PatchLineComment> baseComments;
-  final Map<PatchLineComment.Key, PatchLineComment> put;
-  final Set<PatchLineComment.Key> delete;
+  final List<Comment> baseComments;
+  final Map<Comment.Key, Comment> put;
+  final Set<Comment.Key> delete;
 
   private String pushCert;
 
@@ -94,13 +92,13 @@
     return out.toByteArray();
   }
 
-  void putComment(PatchLineComment comment) {
-    checkArgument(!delete.contains(comment.getKey()),
-        "cannot both delete and put %s", comment.getKey());
-    put.put(comment.getKey(), comment);
+  void putComment(Comment comment) {
+    checkArgument(!delete.contains(comment.key),
+        "cannot both delete and put %s", comment.key);
+    put.put(comment.key, comment);
   }
 
-  void deleteComment(PatchLineComment.Key key) {
+  void deleteComment(Comment.Key key) {
     checkArgument(!put.containsKey(key), "cannot both delete and put %s", key);
     delete.add(key);
   }
@@ -109,17 +107,17 @@
     this.pushCert = pushCert;
   }
 
-  private Multimap<PatchSet.Id, PatchLineComment> buildCommentMap() {
-    Multimap<PatchSet.Id, PatchLineComment> all = ArrayListMultimap.create();
+  private Multimap<Integer, Comment> buildCommentMap() {
+    Multimap<Integer, Comment> all = ArrayListMultimap.create();
 
-    for (PatchLineComment c : baseComments) {
-      if (!delete.contains(c.getKey()) && !put.containsKey(c.getKey())) {
-        all.put(c.getPatchSetId(), c);
+    for (Comment c : baseComments) {
+      if (!delete.contains(c.key) && !put.containsKey(c.key)) {
+        all.put(c.key.patchSetId, c);
       }
     }
-    for (PatchLineComment c : put.values()) {
-      if (!delete.contains(c.getKey())) {
-        all.put(c.getPatchSetId(), c);
+    for (Comment c : put.values()) {
+      if (!delete.contains(c.key)) {
+        all.put(c.key.patchSetId, c);
       }
     }
     return all;
@@ -127,16 +125,13 @@
 
   private void buildNoteJson(final ChangeNoteUtil noteUtil, OutputStream out)
       throws IOException {
-    Multimap<PatchSet.Id, PatchLineComment> comments = buildCommentMap();
+    Multimap<Integer, Comment> comments = buildCommentMap();
     if (comments.isEmpty() && pushCert == null) {
       return;
     }
 
     RevisionNoteData data = new RevisionNoteData();
-    data.comments = comments.values().stream()
-        .sorted(PLC_ORDER)
-        .map(plc -> new RevisionNoteData.Comment(plc, noteUtil.getServerId()))
-        .collect(toList());
+    data.comments = COMMENT_ORDER.sortedCopy(comments.values());
     data.pushCert = pushCert;
 
     try (OutputStreamWriter osw = new OutputStreamWriter(out)) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RevisionNoteData.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RevisionNoteData.java
index 73083b7..e0ee934 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RevisionNoteData.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/RevisionNoteData.java
@@ -14,17 +14,8 @@
 
 package com.google.gerrit.server.notedb;
 
-import static java.util.stream.Collectors.toList;
+import com.google.gerrit.reviewdb.client.Comment;
 
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
-import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.RevId;
-
-import java.sql.Timestamp;
 import java.util.List;
 
 /**
@@ -32,110 +23,6 @@
  * <p>It is intended for (de)serialization to JSON only.
  */
 class RevisionNoteData {
-  static class Identity {
-    int id;
-
-    Identity(Account.Id id) {
-      this.id = id.get();
-    }
-
-    Account.Id export() {
-      return new Account.Id(id);
-    }
-  }
-
-  static class CommentKey {
-    String uuid;
-    String filename;
-    int patchSetId;
-
-    CommentKey(PatchLineComment.Key k) {
-      uuid = k.get();
-      filename = k.getParentKey().getFileName();
-      patchSetId = k.getParentKey().getParentKey().get();
-    }
-
-    PatchLineComment.Key export(Change.Id changeId) {
-      return new PatchLineComment.Key(
-          new Patch.Key(
-              new PatchSet.Id(changeId, patchSetId),
-              filename),
-          uuid);
-    }
-  }
-
-  static class CommentRange {
-    int startLine;
-    int startChar;
-    int endLine;
-    int endChar;
-
-    CommentRange(com.google.gerrit.reviewdb.client.CommentRange cr) {
-      startLine = cr.getStartLine();
-      startChar = cr.getStartCharacter();
-      endLine = cr.getEndLine();
-      endChar = cr.getEndCharacter();
-    }
-
-    com.google.gerrit.reviewdb.client.CommentRange export() {
-      return new com.google.gerrit.reviewdb.client.CommentRange(
-          startLine, startChar, endLine, endChar);
-    }
-  }
-
-  static class Comment {
-    CommentKey key;
-    int lineNbr;
-    Identity author;
-    Timestamp writtenOn;
-    short side;
-    String message;
-    String parentUuid;
-    CommentRange range;
-    String tag;
-    String revId;
-    String serverId;
-
-    Comment(PatchLineComment plc, String serverId) {
-      key = new CommentKey(plc.getKey());
-      lineNbr = plc.getLine();
-      author = new Identity(plc.getAuthor());
-      writtenOn = plc.getWrittenOn();
-      side = plc.getSide();
-      message = plc.getMessage();
-      parentUuid = plc.getParentUuid();
-      range = plc.getRange() != null ? new CommentRange(plc.getRange()) : null;
-      tag = plc.getTag();
-      revId = plc.getRevId().get();
-      this.serverId = serverId;
-    }
-
-    PatchLineComment export(Change.Id changeId,
-        PatchLineComment.Status status) {
-      PatchLineComment plc = new PatchLineComment(
-          key.export(changeId), lineNbr, author.export(), parentUuid, writtenOn);
-      plc.setSide(side);
-      plc.setMessage(message);
-      if (range != null) {
-        plc.setRange(range.export());
-      }
-      plc.setTag(tag);
-      plc.setRevId(new RevId(revId));
-      plc.setStatus(status);
-      return plc;
-    }
-  }
-
-
   String pushCert;
   List<Comment> comments;
-
-
-  ImmutableList<PatchLineComment> exportComments(Change.Id changeId,
-      PatchLineComment.Status status) {
-    return ImmutableList.copyOf(
-        comments.stream()
-            .map(c -> c.export(changeId, status))
-            .collect(toList()));
-  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilderImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilderImpl.java
index 7e99957..d4caa6c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilderImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/ChangeRebuilderImpl.java
@@ -39,6 +39,7 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.ChangeMessage;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.PatchLineComment;
 import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
 import com.google.gerrit.reviewdb.client.PatchSet;
@@ -47,10 +48,11 @@
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.reviewdb.server.ReviewDbUtil;
+import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.PatchLineCommentsUtil;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.config.AnonymousCowardName;
+import com.google.gerrit.server.config.GerritServerId;
 import com.google.gerrit.server.git.ChainedReceiveCommands;
 import com.google.gerrit.server.notedb.ChangeBundle;
 import com.google.gerrit.server.notedb.ChangeBundleReader;
@@ -128,6 +130,7 @@
   private final PersonIdent serverIdent;
   private final ProjectCache projectCache;
   private final String anonymousCowardName;
+  private final String serverId;
 
   @Inject
   ChangeRebuilderImpl(SchemaFactory<ReviewDb> schemaFactory,
@@ -141,7 +144,8 @@
       PatchListCache patchListCache,
       @GerritPersonIdent PersonIdent serverIdent,
       @Nullable ProjectCache projectCache,
-      @AnonymousCowardName String anonymousCowardName) {
+      @AnonymousCowardName String anonymousCowardName,
+      @GerritServerId String serverId) {
     super(schemaFactory);
     this.accountCache = accountCache;
     this.bundleReader = bundleReader;
@@ -154,6 +158,7 @@
     this.serverIdent = serverIdent;
     this.projectCache = projectCache;
     this.anonymousCowardName = anonymousCowardName;
+    this.serverId = serverId;
   }
 
   @Override
@@ -324,16 +329,15 @@
           new PatchSetEvent(change, ps, manager.getChangeRepo().rw);
       patchSetEvents.put(ps.getId(), pse);
       events.add(pse);
-      for (PatchLineComment c : getPatchLineComments(bundle, ps)) {
-        if (c.getStatus() == Status.PUBLISHED) {
-          CommentEvent e =
-              new CommentEvent(c, change, ps, patchListCache);
-          events.add(e.addDep(pse));
-        } else {
-          DraftCommentEvent e =
-              new DraftCommentEvent(c, change, ps, patchListCache);
-          draftCommentEvents.put(c.getAuthor(), e);
-        }
+      for (Comment c : getComments(bundle, serverId, Status.PUBLISHED, ps)) {
+        CommentEvent e =
+            new CommentEvent(c, change, ps, patchListCache);
+        events.add(e.addDep(pse));
+      }
+      for (Comment c : getComments(bundle, serverId, Status.DRAFT, ps)) {
+        DraftCommentEvent e =
+            new DraftCommentEvent(c, change, ps, patchListCache);
+        draftCommentEvents.put(c.author.getId(), e);
       }
     }
 
@@ -403,11 +407,12 @@
     return minPsNum;
   }
 
-  private static List<PatchLineComment> getPatchLineComments(ChangeBundle bundle,
-      final PatchSet ps) {
+  private static List<Comment> getComments(ChangeBundle bundle, String serverId,
+      PatchLineComment.Status status, PatchSet ps) {
     return bundle.getPatchLineComments().stream()
-        .filter(c -> c.getPatchSetId().equals(ps.getId()))
-        .sorted(PatchLineCommentsUtil.PLC_ORDER)
+        .filter(c -> c.getPatchSetId().equals(ps.getId())
+            && c.getStatus() == status)
+        .map(plc -> plc.asComment(serverId)).sorted(CommentsUtil.COMMENT_ORDER)
         .collect(toList());
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/CommentEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/CommentEvent.java
index a2f436d..51ade79 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/CommentEvent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/CommentEvent.java
@@ -14,26 +14,27 @@
 
 package com.google.gerrit.server.notedb.rebuild;
 
-import static com.google.gerrit.server.PatchLineCommentsUtil.setCommentRevId;
+import static com.google.gerrit.server.CommentsUtil.setCommentRevId;
 
 import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.PatchLineComment;
 import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.PatchLineCommentsUtil;
+import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.notedb.ChangeUpdate;
 import com.google.gerrit.server.patch.PatchListCache;
 import com.google.gwtorm.server.OrmException;
 
 class CommentEvent extends Event {
-  public final PatchLineComment c;
+  public final Comment c;
   private final Change change;
   private final PatchSet ps;
   private final PatchListCache cache;
 
-  CommentEvent(PatchLineComment c, Change change, PatchSet ps,
+  CommentEvent(Comment c, Change change, PatchSet ps,
       PatchListCache cache) {
-    super(PatchLineCommentsUtil.getCommentPsId(c), c.getAuthor(),
-        c.getWrittenOn(), change.getCreatedOn(), c.getTag());
+    super(CommentsUtil.getCommentPsId(change.getId(), c), c.author.getId(),
+        c.writtenOn, change.getCreatedOn(), c.tag);
     this.c = c;
     this.change = change;
     this.ps = ps;
@@ -48,9 +49,9 @@
   @Override
   void apply(ChangeUpdate update) throws OrmException {
     checkUpdate(update);
-    if (c.getRevId() == null) {
+    if (c.revId == null) {
       setCommentRevId(c, cache, change, ps);
     }
-    update.putComment(c);
+    update.putComment(PatchLineComment.Status.PUBLISHED, c);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/DraftCommentEvent.java b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/DraftCommentEvent.java
index 7d93875..360dfff 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/DraftCommentEvent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/notedb/rebuild/DraftCommentEvent.java
@@ -14,27 +14,27 @@
 
 package com.google.gerrit.server.notedb.rebuild;
 
-import static com.google.gerrit.server.PatchLineCommentsUtil.setCommentRevId;
+import static com.google.gerrit.server.CommentsUtil.setCommentRevId;
 
 import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.server.PatchLineCommentsUtil;
+import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.notedb.ChangeDraftUpdate;
 import com.google.gerrit.server.notedb.ChangeUpdate;
 import com.google.gerrit.server.patch.PatchListCache;
 import com.google.gwtorm.server.OrmException;
 
 class DraftCommentEvent extends Event {
-  public final PatchLineComment c;
+  public final Comment c;
   private final Change change;
   private final PatchSet ps;
   private final PatchListCache cache;
 
-  DraftCommentEvent(PatchLineComment c, Change change, PatchSet ps,
+  DraftCommentEvent(Comment c, Change change, PatchSet ps,
       PatchListCache cache) {
-    super(PatchLineCommentsUtil.getCommentPsId(c), c.getAuthor(),
-        c.getWrittenOn(), change.getCreatedOn(), c.getTag());
+    super(CommentsUtil.getCommentPsId(change.getId(), c), c.author.getId(),
+        c.writtenOn, change.getCreatedOn(), c.tag);
     this.c = c;
     this.change = change;
     this.ps = ps;
@@ -52,7 +52,7 @@
   }
 
   void applyDraft(ChangeDraftUpdate draftUpdate) throws OrmException {
-    if (c.getRevId() == null) {
+    if (c.revId == null) {
       setCommentRevId(c, cache, change, ps);
     }
     draftUpdate.putComment(c);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
index 7eee6a3..246d7a5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
@@ -22,8 +22,8 @@
 import com.google.gerrit.prettify.common.EditList;
 import com.google.gerrit.prettify.common.SparseFileContent;
 import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.mime.FileTypeRegistry;
 import com.google.inject.Inject;
@@ -283,8 +283,8 @@
     int lastLine;
 
     lastLine = -1;
-    for (PatchLineComment plc : comments.getCommentsA()) {
-      final int a = plc.getLine();
+    for (Comment c : comments.getCommentsA()) {
+      final int a = c.lineNbr;
       if (lastLine != a) {
         final int b = mapA2B(a - 1);
         if (0 <= b) {
@@ -295,8 +295,8 @@
     }
 
     lastLine = -1;
-    for (PatchLineComment plc : comments.getCommentsB()) {
-      final int b = plc.getLine();
+    for (Comment c : comments.getCommentsB()) {
+      int b = c.lineNbr;
       if (lastLine != b) {
         final int a = mapB2A(b - 1);
         if (0 <= a) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptFactory.java
index 91f8cf8..142ef54 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchScriptFactory.java
@@ -25,14 +25,14 @@
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.Patch;
 import com.google.gerrit.reviewdb.client.Patch.ChangeType;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.PatchLineCommentsUtil;
+import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.account.AccountInfoCacheFactory;
 import com.google.gerrit.server.edit.ChangeEdit;
@@ -88,7 +88,7 @@
   private final PatchListCache patchListCache;
   private final ReviewDb db;
   private final AccountInfoCacheFactory.Factory aicFactory;
-  private final PatchLineCommentsUtil plcUtil;
+  private final CommentsUtil commentsUtil;
 
   private final String fileName;
   @Nullable
@@ -118,7 +118,7 @@
       PatchListCache patchListCache,
       ReviewDb db,
       AccountInfoCacheFactory.Factory aicFactory,
-      PatchLineCommentsUtil plcUtil,
+      CommentsUtil commentsUtil,
       ChangeEditUtil editReader,
       @Assisted ChangeControl control,
       @Assisted final String fileName,
@@ -132,7 +132,7 @@
     this.db = db;
     this.control = control;
     this.aicFactory = aicFactory;
-    this.plcUtil = plcUtil;
+    this.commentsUtil = commentsUtil;
     this.editReader = editReader;
 
     this.fileName = fileName;
@@ -151,7 +151,7 @@
       PatchListCache patchListCache,
       ReviewDb db,
       AccountInfoCacheFactory.Factory aicFactory,
-      PatchLineCommentsUtil plcUtil,
+      CommentsUtil commentsUtil,
       ChangeEditUtil editReader,
       @Assisted ChangeControl control,
       @Assisted String fileName,
@@ -165,7 +165,7 @@
     this.db = db;
     this.control = control;
     this.aicFactory = aicFactory;
-    this.plcUtil = plcUtil;
+    this.commentsUtil = commentsUtil;
     this.editReader = editReader;
 
     this.fileName = fileName;
@@ -402,13 +402,14 @@
   private void loadPublished(final Map<Patch.Key, Patch> byKey,
       final AccountInfoCacheFactory aic, final String file) throws OrmException {
     ChangeNotes notes = control.getNotes();
-    for (PatchLineComment c : plcUtil.publishedByChangeFile(db, notes, changeId, file)) {
-      if (comments.include(c)) {
-        aic.want(c.getAuthor());
+    for (Comment c : commentsUtil.publishedByChangeFile(db, notes, changeId, file)) {
+      if (comments.include(change.getId(), c)) {
+        aic.want(c.author.getId());
       }
 
-      final Patch.Key pKey = c.getKey().getParentKey();
-      final Patch p = byKey.get(pKey);
+      PatchSet.Id psId = new PatchSet.Id(change.getId(), c.key.patchSetId);
+      Patch.Key pKey = new Patch.Key(psId, c.key.filename);
+      Patch p = byKey.get(pKey);
       if (p != null) {
         p.setCommentCount(p.getCommentCount() + 1);
       }
@@ -418,14 +419,15 @@
   private void loadDrafts(final Map<Patch.Key, Patch> byKey,
       final AccountInfoCacheFactory aic, final Account.Id me, final String file)
       throws OrmException {
-    for (PatchLineComment c :
-        plcUtil.draftByChangeFileAuthor(db, control.getNotes(), file, me)) {
-      if (comments.include(c)) {
+    for (Comment c :
+        commentsUtil.draftByChangeFileAuthor(db, control.getNotes(), file, me)) {
+      if (comments.include(change.getId(), c)) {
         aic.want(me);
       }
 
-      final Patch.Key pKey = c.getKey().getParentKey();
-      final Patch p = byKey.get(pKey);
+      PatchSet.Id psId = new PatchSet.Id(change.getId(), c.key.patchSetId);
+      Patch.Key pKey = new Patch.Key(psId, c.key.filename);
+      Patch p = byKey.get(pKey);
       if (p != null) {
         p.setDraftCount(p.getDraftCount() + 1);
       }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
index 413cbcf..cdaf201 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -37,8 +37,8 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.ChangeMessage;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.reviewdb.client.Project;
@@ -49,7 +49,7 @@
 import com.google.gerrit.server.ChangeMessagesUtil;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.PatchLineCommentsUtil;
+import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.ReviewerSet;
 import com.google.gerrit.server.ReviewerStatusUpdate;
@@ -319,7 +319,7 @@
   private final ChangeNotes.Factory notesFactory;
   private final ApprovalsUtil approvalsUtil;
   private final ChangeMessagesUtil cmUtil;
-  private final PatchLineCommentsUtil plcUtil;
+  private final CommentsUtil commentsUtil;
   private final PatchSetUtil psUtil;
   private final PatchListCache patchListCache;
   private final NotesMigration notesMigration;
@@ -337,7 +337,7 @@
   private List<PatchSetApproval> currentApprovals;
   private Map<Integer, List<String>> files;
   private Map<Integer, Optional<PatchList>> patchLists;
-  private Collection<PatchLineComment> publishedComments;
+  private Collection<Comment> publishedComments;
   private CurrentUser visibleTo;
   private ChangeControl changeControl;
   private List<ChangeMessage> messages;
@@ -368,7 +368,7 @@
       ChangeNotes.Factory notesFactory,
       ApprovalsUtil approvalsUtil,
       ChangeMessagesUtil cmUtil,
-      PatchLineCommentsUtil plcUtil,
+      CommentsUtil commentsUtil,
       PatchSetUtil psUtil,
       PatchListCache patchListCache,
       NotesMigration notesMigration,
@@ -386,7 +386,7 @@
     this.notesFactory = notesFactory;
     this.approvalsUtil = approvalsUtil;
     this.cmUtil = cmUtil;
-    this.plcUtil = plcUtil;
+    this.commentsUtil = commentsUtil;
     this.psUtil = psUtil;
     this.patchListCache = patchListCache;
     this.notesMigration = notesMigration;
@@ -406,7 +406,7 @@
       ChangeNotes.Factory notesFactory,
       ApprovalsUtil approvalsUtil,
       ChangeMessagesUtil cmUtil,
-      PatchLineCommentsUtil plcUtil,
+      CommentsUtil commentsUtil,
       PatchSetUtil psUtil,
       PatchListCache patchListCache,
       NotesMigration notesMigration,
@@ -423,7 +423,7 @@
     this.notesFactory = notesFactory;
     this.approvalsUtil = approvalsUtil;
     this.cmUtil = cmUtil;
-    this.plcUtil = plcUtil;
+    this.commentsUtil = commentsUtil;
     this.psUtil = psUtil;
     this.patchListCache = patchListCache;
     this.notesMigration = notesMigration;
@@ -444,7 +444,7 @@
       ChangeNotes.Factory notesFactory,
       ApprovalsUtil approvalsUtil,
       ChangeMessagesUtil cmUtil,
-      PatchLineCommentsUtil plcUtil,
+      CommentsUtil commentsUtil,
       PatchSetUtil psUtil,
       PatchListCache patchListCache,
       NotesMigration notesMigration,
@@ -461,7 +461,7 @@
     this.notesFactory = notesFactory;
     this.approvalsUtil = approvalsUtil;
     this.cmUtil = cmUtil;
-    this.plcUtil = plcUtil;
+    this.commentsUtil = commentsUtil;
     this.psUtil = psUtil;
     this.patchListCache = patchListCache;
     this.notesMigration = notesMigration;
@@ -483,7 +483,7 @@
       ChangeNotes.Factory notesFactory,
       ApprovalsUtil approvalsUtil,
       ChangeMessagesUtil cmUtil,
-      PatchLineCommentsUtil plcUtil,
+      CommentsUtil commentsUtil,
       PatchSetUtil psUtil,
       PatchListCache patchListCache,
       NotesMigration notesMigration,
@@ -500,7 +500,7 @@
     this.notesFactory = notesFactory;
     this.approvalsUtil = approvalsUtil;
     this.cmUtil = cmUtil;
-    this.plcUtil = plcUtil;
+    this.commentsUtil = commentsUtil;
     this.psUtil = psUtil;
     this.patchListCache = patchListCache;
     this.notesMigration = notesMigration;
@@ -523,7 +523,7 @@
       ChangeNotes.Factory notesFactory,
       ApprovalsUtil approvalsUtil,
       ChangeMessagesUtil cmUtil,
-      PatchLineCommentsUtil plcUtil,
+      CommentsUtil commentsUtil,
       PatchSetUtil psUtil,
       PatchListCache patchListCache,
       NotesMigration notesMigration,
@@ -542,7 +542,7 @@
     this.notesFactory = notesFactory;
     this.approvalsUtil = approvalsUtil;
     this.cmUtil = cmUtil;
-    this.plcUtil = plcUtil;
+    this.commentsUtil = commentsUtil;
     this.psUtil = psUtil;
     this.patchListCache = patchListCache;
     this.notesMigration = notesMigration;
@@ -1002,13 +1002,13 @@
     return reviewerUpdates;
   }
 
-  public Collection<PatchLineComment> publishedComments()
+  public Collection<Comment> publishedComments()
       throws OrmException {
     if (publishedComments == null) {
       if (!lazyLoad) {
         return Collections.emptyList();
       }
-      publishedComments = plcUtil.publishedByChange(db, notes());
+      publishedComments = commentsUtil.publishedByChange(db, notes());
     }
     return publishedComments;
   }
@@ -1124,8 +1124,8 @@
         return Collections.emptySet();
       }
       draftsByUser = new HashSet<>();
-      for (PatchLineComment sc : plcUtil.draftByChange(db, notes)) {
-        draftsByUser.add(sc.getAuthor());
+      for (Comment sc : commentsUtil.draftByChange(db, notes)) {
+        draftsByUser.add(sc.author.getId());
       }
     }
     return draftsByUser;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index d59b66c..aaf7966 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -35,7 +35,7 @@
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.PatchLineCommentsUtil;
+import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.StarredChangesUtil;
 import com.google.gerrit.server.account.AccountCache;
 import com.google.gerrit.server.account.AccountResolver;
@@ -169,7 +169,7 @@
     final ChangeNotes.Factory notesFactory;
     final ChangeData.Factory changeDataFactory;
     final FieldDef.FillArgs fillArgs;
-    final PatchLineCommentsUtil plcUtil;
+    final CommentsUtil commentsUtil;
     final AccountResolver accountResolver;
     final GroupBackend groupBackend;
     final AllProjectsName allProjectsName;
@@ -203,7 +203,7 @@
         ChangeNotes.Factory notesFactory,
         ChangeData.Factory changeDataFactory,
         FieldDef.FillArgs fillArgs,
-        PatchLineCommentsUtil plcUtil,
+        CommentsUtil commentsUtil,
         AccountResolver accountResolver,
         GroupBackend groupBackend,
         AllProjectsName allProjectsName,
@@ -223,7 +223,7 @@
         @GerritServerConfig Config cfg) {
       this(db, queryProvider, rewriter, opFactories, userFactory, self,
           capabilityControlFactory, changeControlGenericFactory, notesFactory,
-          changeDataFactory, fillArgs, plcUtil, accountResolver, groupBackend,
+          changeDataFactory, fillArgs, commentsUtil, accountResolver, groupBackend,
           allProjectsName, allUsersName, patchListCache, repoManager,
           projectCache, listChildProjects, submitDryRun, conflictsCache,
           trackingFooters, indexes != null ? indexes.getSearchIndex() : null,
@@ -243,7 +243,7 @@
         ChangeNotes.Factory notesFactory,
         ChangeData.Factory changeDataFactory,
         FieldDef.FillArgs fillArgs,
-        PatchLineCommentsUtil plcUtil,
+        CommentsUtil commentsUtil,
         AccountResolver accountResolver,
         GroupBackend groupBackend,
         AllProjectsName allProjectsName,
@@ -272,7 +272,7 @@
      this.changeControlGenericFactory = changeControlGenericFactory;
      this.changeDataFactory = changeDataFactory;
      this.fillArgs = fillArgs;
-     this.plcUtil = plcUtil;
+     this.commentsUtil = commentsUtil;
      this.accountResolver = accountResolver;
      this.groupBackend = groupBackend;
      this.allProjectsName = allProjectsName;
@@ -296,7 +296,7 @@
       return new Arguments(db, queryProvider, rewriter, opFactories, userFactory,
           Providers.of(otherUser),
           capabilityControlFactory, changeControlGenericFactory, notesFactory,
-          changeDataFactory, fillArgs, plcUtil, accountResolver, groupBackend,
+          changeDataFactory, fillArgs, commentsUtil, accountResolver, groupBackend,
           allProjectsName, allUsersName, patchListCache, repoManager,
           projectCache, listChildProjects, submitDryRun,
           conflictsCache, trackingFooters, index, indexConfig, listMembers,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommentByPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommentByPredicate.java
index 48d6e05..1cb6333 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommentByPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/CommentByPredicate.java
@@ -16,7 +16,7 @@
 
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.server.index.change.ChangeField;
 import com.google.gwtorm.server.OrmException;
 
@@ -41,8 +41,8 @@
         return true;
       }
     }
-    for (PatchLineComment c : cd.publishedComments()) {
-      if (Objects.equals(c.getAuthor(), id)) {
+    for (Comment c : cd.publishedComments()) {
+      if (Objects.equals(c.author.getId(), id)) {
         return true;
       }
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/HasDraftByLegacyPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/HasDraftByLegacyPredicate.java
index d2f6876..5250bb7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/HasDraftByLegacyPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/HasDraftByLegacyPredicate.java
@@ -41,7 +41,7 @@
 
   @Override
   public boolean match(final ChangeData object) throws OrmException {
-    return !args.plcUtil
+    return !args.commentsUtil
         .draftByChangeAuthor(args.db.get(), object.notes(), accountId)
         .isEmpty();
   }
@@ -49,7 +49,7 @@
   @Override
   public ResultSet<ChangeData> read() throws OrmException {
     Set<Change.Id> ids = new HashSet<>();
-    for (Change.Id changeId : args.plcUtil
+    for (Change.Id changeId : args.commentsUtil
         .changesWithDraftsByAuthor(args.db.get(), accountId)) {
       ids.add(changeId);
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
index 496eff6..5e08ee3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
@@ -307,8 +307,7 @@
             includeApprovals ? d.approvals().asMap() : null,
             includeFiles, d.change(), labelTypes);
         for (PatchSetAttribute attribute : c.patchSets) {
-          eventFactory.addPatchSetComments(
-              attribute, d.publishedComments());
+          eventFactory.addPatchSetComments(attribute, d.publishedComments());
         }
       }
     }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
index 0fe4941..0a98c40 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
@@ -25,12 +25,10 @@
 import com.google.gerrit.metrics.MetricMaker;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.CommentRange;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RevId;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.GerritPersonIdent;
@@ -132,6 +130,10 @@
   @Inject
   protected AbstractChangeNotes.Args args;
 
+  @Inject
+  @GerritServerId
+  private String serverId;
+
   protected Injector injector;
   private String systemTimeZone;
 
@@ -250,30 +252,22 @@
     return label;
   }
 
-  protected PatchLineComment newPublishedComment(PatchSet.Id psId,
-      String filename, String UUID, CommentRange range, int line,
-      IdentifiedUser commenter, String parentUUID, Timestamp t,
-      String message, short side, String commitSHA1) {
-    return newComment(psId, filename, UUID, range, line, commenter,
-        parentUUID, t, message, side, commitSHA1,
-        PatchLineComment.Status.PUBLISHED);
-  }
+  protected Comment newComment(PatchSet.Id psId, String filename, String UUID,
+      CommentRange range, int line, IdentifiedUser commenter, String parentUUID,
+      Timestamp t, String message, short side, String commitSHA1) {
+    Comment c = new Comment(
+        new Comment.Key(UUID, filename, psId.get()),
+        commenter.getAccountId(),
+        t,
+        side,
+        message,
+        serverId);
+    c.lineNbr = line;
+    c.parentUuid = parentUUID;
+    c.revId = commitSHA1;
+    c.setRange(range);
+    return c;
 
-  protected PatchLineComment newComment(PatchSet.Id psId,
-      String filename, String UUID, CommentRange range, int line,
-      IdentifiedUser commenter, String parentUUID, Timestamp t,
-      String message, short side, String commitSHA1,
-      PatchLineComment.Status status) {
-    PatchLineComment comment = new PatchLineComment(
-        new PatchLineComment.Key(
-            new Patch.Key(psId, filename), UUID),
-        line, commenter.getAccountId(), parentUUID, t);
-    comment.setSide(side);
-    comment.setMessage(message);
-    comment.setRange(range);
-    comment.setRevId(new RevId(commitSHA1));
-    comment.setStatus(status);
-    return comment;
   }
 
   protected static Timestamp truncate(Timestamp ts) {
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java
index 5aa3fc4..0b04ee9 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/notedb/ChangeNotesTest.java
@@ -39,8 +39,8 @@
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.ChangeMessage;
+import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.CommentRange;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
 import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
@@ -103,18 +103,19 @@
     Change c = newChange();
     RevCommit commit = tr.commit().message("PS2").create();
     ChangeUpdate update = newUpdate(c, changeOwner);
-    update.putComment(newPublishedComment(c.currentPatchSetId(), "a.txt",
-        "uuid1", new CommentRange(1, 2, 3, 4), 1, changeOwner, null,
-        TimeUtil.nowTs(), "Comment", (short) 1, commit.name()));
+    update.putComment(Status.PUBLISHED,
+        newComment(c.currentPatchSetId(), "a.txt", "uuid1",
+            new CommentRange(1, 2, 3, 4), 1, changeOwner, null,
+            TimeUtil.nowTs(), "Comment", (short) 1, commit.name()));
     update.setTag(tag);
     update.commit();
 
     ChangeNotes notes = newNotes(c);
 
-    ImmutableListMultimap<RevId, PatchLineComment> comments = notes.getComments();
+    ImmutableListMultimap<RevId, Comment> comments = notes.getComments();
     assertThat(comments).hasSize(1);
     assertThat(
-        comments.entries().asList().get(0).getValue().getTag())
+        comments.entries().asList().get(0).getValue().tag)
             .isEqualTo(tag);
   }
 
@@ -159,9 +160,10 @@
 
     RevCommit commit = tr.commit().message("PS2").create();
     update = newUpdate(c, changeOwner);
-    update.putComment(newPublishedComment(c.currentPatchSetId(), "a.txt",
-        "uuid1", new CommentRange(1, 2, 3, 4), 1, changeOwner, null,
-        TimeUtil.nowTs(), "Comment", (short) 1, commit.name()));
+    update.putComment(Status.PUBLISHED,
+        newComment(c.currentPatchSetId(), "a.txt", "uuid1",
+            new CommentRange(1, 2, 3, 4), 1, changeOwner, null,
+            TimeUtil.nowTs(), "Comment", (short) 1, commit.name()));
     update.setChangeMessage("coverage verification");
     update.setTag(coverageTag);
     update.commit();
@@ -180,10 +182,9 @@
     assertThat(approval.getTag()).isEqualTo(integrationTag);
     assertThat(approval.getValue()).isEqualTo(-1);
 
-    ImmutableListMultimap<RevId, PatchLineComment> comments =
-        notes.getComments();
+    ImmutableListMultimap<RevId, Comment> comments = notes.getComments();
     assertThat(comments).hasSize(1);
-    assertThat(comments.entries().asList().get(0).getValue().getTag())
+    assertThat(comments.entries().asList().get(0).getValue().tag)
         .isEqualTo(coverageTag);
 
     ImmutableList<ChangeMessage> messages = notes.getChangeMessages();
@@ -923,9 +924,10 @@
     update.setPatchSetState(PatchSetState.DRAFT);
     update.putApproval("Code-Review", (short) 1);
     update.setChangeMessage("This is a message");
-    update.putComment(newPublishedComment(c.currentPatchSetId(), "a.txt",
-        "uuid1", new CommentRange(1, 2, 3, 4), 1, changeOwner, null,
-        TimeUtil.nowTs(), "Comment", (short) 1, commit.name()));
+    update.putComment(Status.PUBLISHED,
+        newComment(c.currentPatchSetId(), "a.txt", "uuid1",
+            new CommentRange(1, 2, 3, 4), 1, changeOwner, null,
+            TimeUtil.nowTs(), "Comment", (short) 1, commit.name()));
     update.commit();
 
     ChangeNotes notes = newNotes(c);
@@ -1022,9 +1024,9 @@
     update = newUpdate(c, changeOwner);
     update.setPatchSetId(psId2);
     Timestamp ts = TimeUtil.nowTs();
-    update.putComment(newPublishedComment(psId2, "a.txt",
-        "uuid1", new CommentRange(1, 2, 3, 4), 1, changeOwner, null, ts,
-        "Comment", (short) 1, commit.name()));
+    update.putComment(Status.PUBLISHED,
+        newComment(psId2, "a.txt", "uuid1", new CommentRange(1, 2, 3, 4), 1,
+            changeOwner, null, ts, "Comment", (short) 1, commit.name()));
     update.commit();
 
     notes = newNotes(c);
@@ -1102,11 +1104,11 @@
     RevCommit tipCommit;
     try (NoteDbUpdateManager updateManager =
         updateManagerFactory.create(project)) {
-      PatchLineComment comment1 = newPublishedComment(psId, "file1",
+      Comment comment1 = newComment(psId, "file1",
           uuid1, range1, range1.getEndLine(), otherUser, null, time1, message1,
           (short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234");
       update1.setPatchSetId(psId);
-      update1.putComment(comment1);
+      update1.putComment(Status.PUBLISHED, comment1);
       updateManager.add(update1);
 
       ChangeUpdate update2 = newUpdate(c, otherUser);
@@ -1329,11 +1331,11 @@
     PatchSet.Id psId = c.currentPatchSetId();
     RevId revId = new RevId("abcd1234abcd1234abcd1234abcd1234abcd1234");
 
-    PatchLineComment comment = newPublishedComment(psId, "file1",
+    Comment comment = newComment(psId, "file1",
         "uuid", null, 0, otherUser, null,
         TimeUtil.nowTs(), "message", (short) 1, revId.get());
     update.setPatchSetId(psId);
-    update.putComment(comment);
+    update.putComment(Status.PUBLISHED, comment);
     update.commit();
 
     ChangeNotes notes = newNotes(c);
@@ -1349,11 +1351,11 @@
     RevId revId = new RevId("abcd1234abcd1234abcd1234abcd1234abcd1234");
     CommentRange range = new CommentRange(1, 0, 2, 0);
 
-    PatchLineComment comment = newPublishedComment(psId, "file1",
+    Comment comment = newComment(psId, "file1",
         "uuid", range, range.getEndLine(), otherUser, null,
         TimeUtil.nowTs(), "message", (short) 1, revId.get());
     update.setPatchSetId(psId);
-    update.putComment(comment);
+    update.putComment(Status.PUBLISHED, comment);
     update.commit();
 
     ChangeNotes notes = newNotes(c);
@@ -1369,11 +1371,11 @@
     RevId revId = new RevId("abcd1234abcd1234abcd1234abcd1234abcd1234");
     CommentRange range = new CommentRange(0, 0, 0, 0);
 
-    PatchLineComment comment = newPublishedComment(psId, "file",
+    Comment comment = newComment(psId, "file",
         "uuid", range, range.getEndLine(), otherUser, null,
         TimeUtil.nowTs(), "message", (short) 1, revId.get());
     update.setPatchSetId(psId);
-    update.putComment(comment);
+    update.putComment(Status.PUBLISHED, comment);
     update.commit();
 
     ChangeNotes notes = newNotes(c);
@@ -1389,11 +1391,10 @@
     RevId revId = new RevId("abcd1234abcd1234abcd1234abcd1234abcd1234");
     CommentRange range = new CommentRange(1, 2, 3, 4);
 
-    PatchLineComment comment = newPublishedComment(psId, "",
-        "uuid", range, range.getEndLine(), otherUser, null,
-        TimeUtil.nowTs(), "message", (short) 1, revId.get());
+    Comment comment = newComment(psId, "", "uuid", range, range.getEndLine(),
+        otherUser, null, TimeUtil.nowTs(), "message", (short) 1, revId.get());
     update.setPatchSetId(psId);
-    update.putComment(comment);
+    update.putComment(Status.PUBLISHED, comment);
     update.commit();
 
     ChangeNotes notes = newNotes(c);
@@ -1417,29 +1418,29 @@
     Timestamp time3 = TimeUtil.nowTs();
     PatchSet.Id psId = c.currentPatchSetId();
 
-    PatchLineComment comment1 = newPublishedComment(psId, "file1",
-        uuid1, range1, range1.getEndLine(), otherUser, null, time1, message1,
-        (short) 1, "abcd1234abcd1234abcd1234abcd1234abcd1234");
+    Comment comment1 = newComment(psId, "file1", uuid1, range1,
+        range1.getEndLine(), otherUser, null, time1, message1, (short) 1,
+        "abcd1234abcd1234abcd1234abcd1234abcd1234");
     update.setPatchSetId(psId);
-    update.putComment(comment1);
+    update.putComment(Status.PUBLISHED, comment1);
     update.commit();
 
     update = newUpdate(c, otherUser);
     CommentRange range2 = new CommentRange(2, 1, 3, 1);
-    PatchLineComment comment2 = newPublishedComment(psId, "file1",
-        uuid2, range2, range2.getEndLine(), otherUser, null, time2, message2,
-        (short) 1, "abcd1234abcd1234abcd1234abcd1234abcd1234");
+    Comment comment2 = newComment(psId, "file1", uuid2, range2,
+        range2.getEndLine(), otherUser, null, time2, message2, (short) 1,
+        "abcd1234abcd1234abcd1234abcd1234abcd1234");
     update.setPatchSetId(psId);
-    update.putComment(comment2);
+    update.putComment(Status.PUBLISHED, comment2);
     update.commit();
 
     update = newUpdate(c, otherUser);
     CommentRange range3 = new CommentRange(3, 0, 4, 1);
-    PatchLineComment comment3 = newPublishedComment(psId, "file2",
-        uuid3, range3, range3.getEndLine(), otherUser, null, time3, message3,
-        (short) 1, "abcd1234abcd1234abcd1234abcd1234abcd1234");
+    Comment comment3 = newComment(psId, "file2", uuid3, range3,
+        range3.getEndLine(), otherUser, null, time3, message3, (short) 1,
+        "abcd1234abcd1234abcd1234abcd1234abcd1234");
     update.setPatchSetId(psId);
-    update.putComment(comment3);
+    update.putComment(Status.PUBLISHED, comment3);
     update.commit();
 
     ChangeNotes notes = newNotes(c);
@@ -1500,20 +1501,20 @@
     Timestamp time2 = TimeUtil.nowTs();
     PatchSet.Id psId = c.currentPatchSetId();
 
-    PatchLineComment comment1 = newPublishedComment(psId, "file1",
+    Comment comment1 = newComment(psId, "file1",
         uuid1, range1, range1.getEndLine(), otherUser, null, time1, message1,
         (short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234");
     update.setPatchSetId(psId);
-    update.putComment(comment1);
+    update.putComment(Status.PUBLISHED, comment1);
     update.commit();
 
     update = newUpdate(c, otherUser);
     CommentRange range2 = new CommentRange(2, 1, 3, 1);
-    PatchLineComment comment2 = newPublishedComment(psId, "file1",
+    Comment comment2 = newComment(psId, "file1",
         uuid2, range2, range2.getEndLine(), otherUser, null, time2, message2,
         (short) 0, "abcd1234abcd1234abcd1234abcd1234abcd1234");
     update.setPatchSetId(psId);
-    update.putComment(comment2);
+    update.putComment(Status.PUBLISHED, comment2);
     update.commit();
 
     ChangeNotes notes = newNotes(c);
@@ -1570,21 +1571,21 @@
     PatchSet.Id psId1 = c.currentPatchSetId();
     PatchSet.Id psId2 = new PatchSet.Id(c.getId(), psId1.get() + 1);
 
-    PatchLineComment comment1 = newPublishedComment(psId1, "file1",
-        uuid1, range1, range1.getEndLine(), otherUser, null, time, message1,
-        (short) 0, revId.get());
-    PatchLineComment comment2 = newPublishedComment(psId1, "file1",
-        uuid2, range2, range2.getEndLine(), otherUser, null, time, message2,
-        (short) 0, revId.get());
-    PatchLineComment comment3 = newPublishedComment(psId2, "file1",
-        uuid3, range1, range1.getEndLine(), otherUser, null, time, message3,
-        (short) 0, revId.get());
+    Comment comment1 =
+        newComment(psId1, "file1", uuid1, range1, range1.getEndLine(),
+            otherUser, null, time, message1, (short) 0, revId.get());
+    Comment comment2 =
+        newComment(psId1, "file1", uuid2, range2, range2.getEndLine(),
+            otherUser, null, time, message2, (short) 0, revId.get());
+    Comment comment3 =
+        newComment(psId2, "file1", uuid3, range1, range1.getEndLine(),
+            otherUser, null, time, message3, (short) 0, revId.get());
 
     ChangeUpdate update = newUpdate(c, otherUser);
     update.setPatchSetId(psId2);
-    update.putComment(comment3);
-    update.putComment(comment2);
-    update.putComment(comment1);
+    update.putComment(Status.PUBLISHED, comment3);
+    update.putComment(Status.PUBLISHED, comment2);
+    update.putComment(Status.PUBLISHED, comment1);
     update.commit();
 
     ChangeNotes notes = newNotes(c);
@@ -1654,11 +1655,11 @@
     Timestamp time = TimeUtil.nowTs();
     PatchSet.Id psId = c.currentPatchSetId();
 
-    PatchLineComment comment = newPublishedComment(psId, "file1",
-        uuid, range, range.getEndLine(), user, null, time, "comment",
-        (short) 1, "abcd1234abcd1234abcd1234abcd1234abcd1234");
+    Comment comment = newComment(psId, "file1", uuid, range, range.getEndLine(),
+        user, null, time, "comment", (short) 1,
+        "abcd1234abcd1234abcd1234abcd1234abcd1234");
     update.setPatchSetId(psId);
-    update.putComment(comment);
+    update.putComment(Status.PUBLISHED, comment);
     update.commit();
 
     ChangeNotes notes = newNotes(c);
@@ -1690,7 +1691,7 @@
       }
     }
     assertThat(notes.getComments())
-        .isEqualTo(ImmutableMultimap.of(comment.getRevId(), comment));
+        .isEqualTo(ImmutableMultimap.of(new RevId(comment.revId), comment));
   }
 
   @Test
@@ -1708,21 +1709,20 @@
     Timestamp now = TimeUtil.nowTs();
     PatchSet.Id psId = c.currentPatchSetId();
 
-    PatchLineComment commentForBase =
-        newPublishedComment(psId, "filename", uuid1,
-        range, range.getEndLine(), otherUser, null, now, messageForBase,
-        (short) 0, rev1);
+    Comment commentForBase =
+        newComment(psId, "filename", uuid1, range, range.getEndLine(),
+            otherUser, null, now, messageForBase, (short) 0, rev1);
     update.setPatchSetId(psId);
-    update.putComment(commentForBase);
+    update.putComment(Status.PUBLISHED, commentForBase);
     update.commit();
 
     update = newUpdate(c, otherUser);
-    PatchLineComment commentForPS =
-        newPublishedComment(psId, "filename", uuid2,
-        range, range.getEndLine(), otherUser, null, now, messageForPS,
+    Comment commentForPS =
+        newComment(psId, "filename", uuid2, range, range.getEndLine(),
+            otherUser, null, now, messageForPS,
         (short) 1, rev2);
     update.setPatchSetId(psId);
-    update.putComment(commentForPS);
+    update.putComment(Status.PUBLISHED, commentForPS);
     update.commit();
 
     assertThat(newNotes(c).getComments()).containsExactlyEntriesIn(
@@ -1745,19 +1745,19 @@
     ChangeUpdate update = newUpdate(c, otherUser);
     Timestamp timeForComment1 = TimeUtil.nowTs();
     Timestamp timeForComment2 = TimeUtil.nowTs();
-    PatchLineComment comment1 = newPublishedComment(psId, filename,
-        uuid1, range, range.getEndLine(), otherUser, null, timeForComment1,
-        "comment 1", side, rev);
+    Comment comment1 =
+        newComment(psId, filename, uuid1, range, range.getEndLine(), otherUser,
+            null, timeForComment1, "comment 1", side, rev);
     update.setPatchSetId(psId);
-    update.putComment(comment1);
+    update.putComment(Status.PUBLISHED, comment1);
     update.commit();
 
     update = newUpdate(c, otherUser);
-    PatchLineComment comment2 = newPublishedComment(psId, filename,
-        uuid2, range, range.getEndLine(), otherUser, null, timeForComment2,
-        "comment 2", side, rev);
+    Comment comment2 =
+        newComment(psId, filename, uuid2, range, range.getEndLine(), otherUser,
+            null, timeForComment2, "comment 2", side, rev);
     update.setPatchSetId(psId);
-    update.putComment(comment2);
+    update.putComment(Status.PUBLISHED, comment2);
     update.commit();
 
     assertThat(newNotes(c).getComments()).containsExactlyEntriesIn(
@@ -1780,19 +1780,19 @@
 
     ChangeUpdate update = newUpdate(c, otherUser);
     Timestamp now = TimeUtil.nowTs();
-    PatchLineComment comment1 = newPublishedComment(psId, filename1,
+    Comment comment1 = newComment(psId, filename1,
         uuid, range, range.getEndLine(), otherUser, null, now, "comment 1",
         side, rev);
     update.setPatchSetId(psId);
-    update.putComment(comment1);
+    update.putComment(Status.PUBLISHED, comment1);
     update.commit();
 
     update = newUpdate(c, otherUser);
-    PatchLineComment comment2 = newPublishedComment(psId, filename2,
+    Comment comment2 = newComment(psId, filename2,
         uuid, range, range.getEndLine(), otherUser, null, now, "comment 2",
         side, rev);
     update.setPatchSetId(psId);
-    update.putComment(comment2);
+    update.putComment(Status.PUBLISHED, comment2);
     update.commit();
 
     assertThat(newNotes(c).getComments()).containsExactlyEntriesIn(
@@ -1814,11 +1814,10 @@
 
     ChangeUpdate update = newUpdate(c, otherUser);
     Timestamp now = TimeUtil.nowTs();
-    PatchLineComment comment1 = newPublishedComment(ps1, filename,
-        uuid, range, range.getEndLine(), otherUser, null, now, "comment on ps1",
-        side, rev1);
+    Comment comment1 = newComment(ps1, filename, uuid, range,
+        range.getEndLine(), otherUser, null, now, "comment on ps1", side, rev1);
     update.setPatchSetId(ps1);
-    update.putComment(comment1);
+    update.putComment(Status.PUBLISHED, comment1);
     update.commit();
 
     incrementPatchSet(c);
@@ -1826,11 +1825,10 @@
 
     update = newUpdate(c, otherUser);
     now = TimeUtil.nowTs();
-    PatchLineComment comment2 = newPublishedComment(ps2, filename,
-        uuid, range, range.getEndLine(), otherUser, null, now, "comment on ps2",
-        side, rev2);
+    Comment comment2 = newComment(ps2, filename, uuid, range,
+        range.getEndLine(), otherUser, null, now, "comment on ps2", side, rev2);
     update.setPatchSetId(ps2);
-    update.putComment(comment2);
+    update.putComment(Status.PUBLISHED, comment2);
     update.commit();
 
     assertThat(newNotes(c).getComments()).containsExactlyEntriesIn(
@@ -1851,11 +1849,10 @@
 
     ChangeUpdate update = newUpdate(c, otherUser);
     Timestamp now = TimeUtil.nowTs();
-    PatchLineComment comment1 = newComment(ps1, filename, uuid, range,
-        range.getEndLine(), otherUser, null, now, "comment on ps1", side,
-        rev, Status.DRAFT);
+    Comment comment1 = newComment(ps1, filename, uuid, range,
+        range.getEndLine(), otherUser, null, now, "comment on ps1", side, rev);
     update.setPatchSetId(ps1);
-    update.putComment(comment1);
+    update.putComment(Status.DRAFT, comment1);
     update.commit();
 
     ChangeNotes notes = newNotes(c);
@@ -1863,10 +1860,9 @@
         ImmutableMultimap.of(new RevId(rev), comment1));
     assertThat(notes.getComments()).isEmpty();
 
-    comment1.setStatus(Status.PUBLISHED);
     update = newUpdate(c, otherUser);
     update.setPatchSetId(ps1);
-    update.putComment(comment1);
+    update.putComment(Status.PUBLISHED, comment1);
     update.commit();
 
     notes = newNotes(c);
@@ -1892,14 +1888,12 @@
     // Write two drafts on the same side of one patch set.
     ChangeUpdate update = newUpdate(c, otherUser);
     update.setPatchSetId(psId);
-    PatchLineComment comment1 = newComment(psId, filename, uuid1,
-        range1, range1.getEndLine(), otherUser, null, now, "comment on ps1",
-        side, rev, Status.DRAFT);
-    PatchLineComment comment2 = newComment(psId, filename, uuid2,
-        range2, range2.getEndLine(), otherUser, null, now, "other on ps1",
-        side, rev, Status.DRAFT);
-    update.putComment(comment1);
-    update.putComment(comment2);
+    Comment comment1 = newComment(psId, filename, uuid1, range1,
+        range1.getEndLine(), otherUser, null, now, "comment on ps1", side, rev);
+    Comment comment2 = newComment(psId, filename, uuid2, range2,
+        range2.getEndLine(), otherUser, null, now, "other on ps1", side, rev);
+    update.putComment(Status.DRAFT, comment1);
+    update.putComment(Status.DRAFT, comment2);
     update.commit();
 
     ChangeNotes notes = newNotes(c);
@@ -1912,8 +1906,7 @@
     // Publish first draft.
     update = newUpdate(c, otherUser);
     update.setPatchSetId(psId);
-    comment1.setStatus(Status.PUBLISHED);
-    update.putComment(comment1);
+    update.putComment(Status.PUBLISHED, comment1);
     update.commit();
 
     notes = newNotes(c);
@@ -1940,15 +1933,15 @@
     // Write two drafts, one on each side of the patchset.
     ChangeUpdate update = newUpdate(c, otherUser);
     update.setPatchSetId(psId);
-    PatchLineComment baseComment = newComment(psId, filename, uuid1,
-        range1, range1.getEndLine(), otherUser, null, now, "comment on base",
-        (short) 0, rev1, Status.DRAFT);
-    PatchLineComment psComment = newComment(psId, filename, uuid2,
-        range2, range2.getEndLine(), otherUser, null, now, "comment on ps",
-        (short) 1, rev2, Status.DRAFT);
+    Comment baseComment =
+        newComment(psId, filename, uuid1, range1, range1.getEndLine(),
+            otherUser, null, now, "comment on base", (short) 0, rev1);
+    Comment psComment =
+        newComment(psId, filename, uuid2, range2, range2.getEndLine(),
+            otherUser, null, now, "comment on ps", (short) 1, rev2);
 
-    update.putComment(baseComment);
-    update.putComment(psComment);
+    update.putComment(Status.DRAFT, baseComment);
+    update.putComment(Status.DRAFT, psComment);
     update.commit();
 
     ChangeNotes notes = newNotes(c);
@@ -1962,10 +1955,8 @@
     update = newUpdate(c, otherUser);
     update.setPatchSetId(psId);
 
-    baseComment.setStatus(Status.PUBLISHED);
-    psComment.setStatus(Status.PUBLISHED);
-    update.putComment(baseComment);
-    update.putComment(psComment);
+    update.putComment(Status.PUBLISHED, baseComment);
+    update.putComment(Status.PUBLISHED, psComment);
     update.commit();
 
     notes = newNotes(c);
@@ -1989,11 +1980,10 @@
 
     ChangeUpdate update = newUpdate(c, otherUser);
     Timestamp now = TimeUtil.nowTs();
-    PatchLineComment comment = newComment(psId, filename, uuid, range,
-        range.getEndLine(), otherUser, null, now, "comment on ps1", side,
-        rev, Status.DRAFT);
+    Comment comment = newComment(psId, filename, uuid, range,
+        range.getEndLine(), otherUser, null, now, "comment on ps1", side, rev);
     update.setPatchSetId(psId);
-    update.putComment(comment);
+    update.putComment(Status.DRAFT, comment);
     update.commit();
 
     ChangeNotes notes = newNotes(c);
@@ -2028,11 +2018,10 @@
 
     ChangeUpdate update = newUpdate(c, otherUser);
     Timestamp now = TimeUtil.nowTs();
-    PatchLineComment comment1 = newComment(ps1, filename,
-        uuid, range, range.getEndLine(), otherUser, null, now, "comment on ps1",
-        side, rev1, Status.DRAFT);
+    Comment comment1 = newComment(ps1, filename, uuid, range,
+        range.getEndLine(), otherUser, null, now, "comment on ps1", side, rev1);
     update.setPatchSetId(ps1);
-    update.putComment(comment1);
+    update.putComment(Status.DRAFT, comment1);
     update.commit();
 
     incrementPatchSet(c);
@@ -2040,11 +2029,10 @@
 
     update = newUpdate(c, otherUser);
     now = TimeUtil.nowTs();
-    PatchLineComment comment2 = newComment(ps2, filename,
-        uuid, range, range.getEndLine(), otherUser, null, now, "comment on ps2",
-        side, rev2, Status.DRAFT);
+    Comment comment2 = newComment(ps2, filename, uuid, range,
+        range.getEndLine(), otherUser, null, now, "comment on ps2", side, rev2);
     update.setPatchSetId(ps2);
-    update.putComment(comment2);
+    update.putComment(Status.DRAFT, comment2);
     update.commit();
 
     ChangeNotes notes = newNotes(c);
@@ -2076,10 +2064,9 @@
 
     ChangeUpdate update = newUpdate(c, otherUser);
     Timestamp now = TimeUtil.nowTs();
-    PatchLineComment comment = newComment(ps1, filename, uuid, range,
-        range.getEndLine(), otherUser, null, now, "comment on ps1", side,
-        rev, Status.PUBLISHED);
-    update.putComment(comment);
+    Comment comment = newComment(ps1, filename, uuid, range, range.getEndLine(),
+        otherUser, null, now, "comment on ps1", side, rev);
+    update.putComment(Status.PUBLISHED, comment);
     update.commit();
 
     assertThat(repo.exactRef(changeMetaRef(c.getId()))).isNotNull();
@@ -2099,10 +2086,10 @@
 
     ChangeUpdate update = newUpdate(c, otherUser);
     Timestamp now = TimeUtil.nowTs();
-    PatchLineComment draft = newComment(ps1, filename, "uuid1", range,
-        range.getEndLine(), otherUser, null, now, "draft comment on ps1", side,
-        rev, Status.DRAFT);
-    update.putComment(draft);
+    Comment draft =
+        newComment(ps1, filename, "uuid1", range, range.getEndLine(), otherUser,
+            null, now, "draft comment on ps1", side, rev);
+    update.putComment(Status.DRAFT, draft);
     update.commit();
 
     String draftRef = refsDraftComments(c.getId(), otherUser.getAccountId());
@@ -2110,10 +2097,9 @@
     assertThat(old).isNotNull();
 
     update = newUpdate(c, otherUser);
-    PatchLineComment pub = newComment(ps1, filename, "uuid2", range,
-        range.getEndLine(), otherUser, null, now, "comment on ps1", side,
-        rev, Status.PUBLISHED);
-    update.putComment(pub);
+    Comment pub = newComment(ps1, filename, "uuid2", range, range.getEndLine(),
+        otherUser, null, now, "comment on ps1", side, rev);
+    update.putComment(Status.PUBLISHED, pub);
     update.commit();
 
     assertThat(exactRefAllUsers(draftRef)).isEqualTo(old);
@@ -2129,11 +2115,10 @@
     Timestamp now = TimeUtil.nowTs();
     PatchSet.Id psId = c.currentPatchSetId();
 
-    PatchLineComment comment = newPublishedComment(
-        psId, "filename", uuid, null, 0, otherUser, null, now, messageForBase,
-        (short) 0, rev);
+    Comment comment = newComment(psId, "filename", uuid, null, 0, otherUser,
+        null, now, messageForBase, (short) 0, rev);
     update.setPatchSetId(psId);
-    update.putComment(comment);
+    update.putComment(Status.PUBLISHED, comment);
     update.commit();
 
     assertThat(newNotes(c).getComments()).containsExactlyEntriesIn(
@@ -2150,11 +2135,10 @@
     Timestamp now = TimeUtil.nowTs();
     PatchSet.Id psId = c.currentPatchSetId();
 
-    PatchLineComment comment = newPublishedComment(
-        psId, "filename", uuid, null, 1, otherUser, null, now, messageForBase,
-        (short) 0, rev);
+    Comment comment = newComment(psId, "filename", uuid, null, 1, otherUser,
+        null, now, messageForBase, (short) 0, rev);
     update.setPatchSetId(psId);
-    update.putComment(comment);
+    update.putComment(Status.PUBLISHED, comment);
     update.commit();
 
     assertThat(newNotes(c).getComments()).containsExactlyEntriesIn(
@@ -2178,14 +2162,12 @@
     ChangeUpdate update = newUpdate(c, otherUser);
     update.setPatchSetId(ps2);
     Timestamp now = TimeUtil.nowTs();
-    PatchLineComment comment1 = newComment(ps1, filename,
-        uuid, range, range.getEndLine(), otherUser, null, now, "comment on ps1",
-        side, rev1, Status.DRAFT);
-    PatchLineComment comment2 = newComment(ps2, filename,
-        uuid, range, range.getEndLine(), otherUser, null, now, "comment on ps2",
-        side, rev2, Status.DRAFT);
-    update.putComment(comment1);
-    update.putComment(comment2);
+    Comment comment1 = newComment(ps1, filename, uuid, range,
+        range.getEndLine(), otherUser, null, now, "comment on ps1", side, rev1);
+    Comment comment2 = newComment(ps2, filename, uuid, range,
+        range.getEndLine(), otherUser, null, now, "comment on ps2", side, rev2);
+    update.putComment(Status.DRAFT, comment1);
+    update.putComment(Status.DRAFT, comment2);
     update.commit();
 
     ChangeNotes notes = newNotes(c);
@@ -2194,10 +2176,8 @@
 
     update = newUpdate(c, otherUser);
     update.setPatchSetId(ps2);
-    comment1.setStatus(Status.PUBLISHED);
-    comment2.setStatus(Status.PUBLISHED);
-    update.putComment(comment1);
-    update.putComment(comment2);
+    update.putComment(Status.PUBLISHED, comment1);
+    update.putComment(Status.PUBLISHED, comment2);
     update.commit();
 
     notes = newNotes(c);
@@ -2216,14 +2196,12 @@
     ChangeUpdate update = newUpdate(c, otherUser);
     update.setPatchSetId(ps1);
     Timestamp now = TimeUtil.nowTs();
-    PatchLineComment comment1 = newComment(ps1, "file1",
-        "uuid1", range, range.getEndLine(), otherUser, null, now, "comment1",
-        side, rev1.get(), Status.DRAFT);
-    PatchLineComment comment2 = newComment(ps1, "file2",
-        "uuid2", range, range.getEndLine(), otherUser, null, now, "comment2",
-        side, rev1.get(), Status.DRAFT);
-    update.putComment(comment1);
-    update.putComment(comment2);
+    Comment comment1 = newComment(ps1, "file1", "uuid1", range,
+        range.getEndLine(), otherUser, null, now, "comment1", side, rev1.get());
+    Comment comment2 = newComment(ps1, "file2", "uuid2", range,
+        range.getEndLine(), otherUser, null, now, "comment2", side, rev1.get());
+    update.putComment(Status.DRAFT, comment1);
+    update.putComment(Status.DRAFT, comment2);
     update.commit();
 
     ChangeNotes notes = newNotes(c);
@@ -2233,8 +2211,7 @@
 
     update = newUpdate(c, otherUser);
     update.setPatchSetId(ps1);
-    comment2.setStatus(Status.PUBLISHED);
-    update.putComment(comment2);
+    update.putComment(Status.PUBLISHED, comment2);
     update.commit();
 
     notes = newNotes(c);
@@ -2269,14 +2246,14 @@
 
     ChangeUpdate update = newUpdate(c, otherUser);
     Timestamp now = TimeUtil.nowTs();
-    PatchLineComment comment1 = newComment(ps1, "file1",
-        "uuid1", range, range.getEndLine(), otherUser, null, now, "comment on ps1",
-        side, rev1.get(), Status.DRAFT);
-    PatchLineComment comment2 = newComment(ps1, "file2",
-        "uuid2", range, range.getEndLine(), otherUser, null, now, "another comment",
-        side, rev1.get(), Status.DRAFT);
-    update.putComment(comment1);
-    update.putComment(comment2);
+    Comment comment1 =
+        newComment(ps1, "file1", "uuid1", range, range.getEndLine(), otherUser,
+            null, now, "comment on ps1", side, rev1.get());
+    Comment comment2 =
+        newComment(ps1, "file2", "uuid2", range, range.getEndLine(), otherUser,
+            null, now, "another comment", side, rev1.get());
+    update.putComment(Status.DRAFT, comment1);
+    update.putComment(Status.DRAFT, comment2);
     update.commit();
 
     String refName = refsDraftComments(c.getId(), otherUserId);
@@ -2284,8 +2261,7 @@
 
     update = newUpdate(c, otherUser);
     update.setPatchSetId(ps1);
-    comment2.setStatus(Status.PUBLISHED);
-    update.putComment(comment2);
+    update.putComment(Status.PUBLISHED, comment2);
     update.commit();
     assertThat(exactRefAllUsers(refName)).isNotNull();
     assertThat(exactRefAllUsers(refName)).isNotEqualTo(oldDraftId);
@@ -2295,7 +2271,6 @@
     // non-atomically after adding the published comment succeeded.
     ChangeDraftUpdate draftUpdate =
         newUpdate(c, otherUser).createDraftUpdateIfNull();
-    comment2.setStatus(Status.DRAFT);
     draftUpdate.putComment(comment2);
     try (NoteDbUpdateManager manager =
         updateManagerFactory.create(c.getProject())) {
@@ -2308,8 +2283,6 @@
     assertThat(draftNotes.load().getComments().get(rev1))
         .containsExactly(comment1, comment2);
 
-    comment2.setStatus(Status.PUBLISHED); // Reset for later assertions.
-
     // Zombie comment is filtered out of drafts via ChangeNotes.
     ChangeNotes notes = newNotes(c);
     assertThat(notes.getDraftComments(otherUserId).get(rev1))
@@ -2319,8 +2292,7 @@
 
     update = newUpdate(c, otherUser);
     update.setPatchSetId(ps1);
-    comment1.setStatus(Status.PUBLISHED);
-    update.putComment(comment1);
+    update.putComment(Status.PUBLISHED, comment1);
     update.commit();
 
     // Updating an unrelated comment causes the zombie comment to get fixed up.
@@ -2334,18 +2306,16 @@
     String rev = "abcd1234abcd1234abcd1234abcd1234abcd1234";
 
     ChangeUpdate update1 = newUpdate(c, otherUser);
-    PatchLineComment comment1 = newComment(c.currentPatchSetId(), "filename",
+    Comment comment1 = newComment(c.currentPatchSetId(), "filename",
         "uuid1", range, range.getEndLine(), otherUser, null,
-        new Timestamp(update1.getWhen().getTime()), "comment 1", (short) 1, rev,
-        Status.PUBLISHED);
-    update1.putComment(comment1);
+        new Timestamp(update1.getWhen().getTime()), "comment 1", (short) 1, rev);
+    update1.putComment(Status.PUBLISHED, comment1);
 
     ChangeUpdate update2 = newUpdate(c, otherUser);
-    PatchLineComment comment2 = newComment(c.currentPatchSetId(), "filename",
+    Comment comment2 = newComment(c.currentPatchSetId(), "filename",
         "uuid2", range, range.getEndLine(), otherUser, null,
-        new Timestamp(update2.getWhen().getTime()), "comment 2", (short) 1, rev,
-        Status.PUBLISHED);
-    update2.putComment(comment2);
+        new Timestamp(update2.getWhen().getTime()), "comment 2", (short) 1, rev);
+    update2.putComment(Status.PUBLISHED, comment2);
 
     try (NoteDbUpdateManager manager = updateManagerFactory.create(project)) {
       manager.add(update1);
@@ -2354,10 +2324,10 @@
     }
 
     ChangeNotes notes = newNotes(c);
-    List<PatchLineComment> comments = notes.getComments().get(new RevId(rev));
+    List<Comment> comments = notes.getComments().get(new RevId(rev));
     assertThat(comments).hasSize(2);
-    assertThat(comments.get(0).getMessage()).isEqualTo("comment 1");
-    assertThat(comments.get(1).getMessage()).isEqualTo("comment 2");
+    assertThat(comments.get(0).message).isEqualTo("comment 1");
+    assertThat(comments.get(1).message).isEqualTo("comment 2");
   }
 
   private boolean testJson() {
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/NoteDbChecker.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/NoteDbChecker.java
index 6dba19a..c5f4301 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/testutil/NoteDbChecker.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/NoteDbChecker.java
@@ -24,7 +24,7 @@
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.reviewdb.server.ReviewDbUtil;
-import com.google.gerrit.server.PatchLineCommentsUtil;
+import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.notedb.ChangeBundle;
 import com.google.gerrit.server.notedb.ChangeBundleReader;
@@ -57,7 +57,7 @@
   private final ChangeBundleReader bundleReader;
   private final ChangeNotes.Factory notesFactory;
   private final ChangeRebuilder changeRebuilder;
-  private final PatchLineCommentsUtil plcUtil;
+  private final CommentsUtil commentsUtil;
 
   @Inject
   NoteDbChecker(Provider<ReviewDb> dbProvider,
@@ -66,14 +66,14 @@
       ChangeBundleReader bundleReader,
       ChangeNotes.Factory notesFactory,
       ChangeRebuilder changeRebuilder,
-      PatchLineCommentsUtil plcUtil) {
+      CommentsUtil commentsUtil) {
     this.dbProvider = dbProvider;
     this.repoManager = repoManager;
     this.bundleReader = bundleReader;
     this.notesMigration = notesMigration;
     this.notesFactory = notesFactory;
     this.changeRebuilder = changeRebuilder;
-    this.plcUtil = plcUtil;
+    this.commentsUtil = commentsUtil;
   }
 
   public void rebuildAndCheckAllChanges() throws Exception {
@@ -157,7 +157,7 @@
         ChangeBundle actual;
         try {
           actual = ChangeBundle.fromNotes(
-              plcUtil, notesFactory.create(db, c.getProject(), c.getId()));
+              commentsUtil, notesFactory.create(db, c.getProject(), c.getId()));
         } catch (Throwable t) {
           String msg = "Error converting change: " + c;
           msgs.add(msg);